MINOR: sample: Replace "req.ungrpc" smp fetch by a "ungrpc" converter.
This patch simply extracts the code of smp_fetch_req_ungrpc() for "req.ungrpc"
from http_fetch.c to move it to sample.c with very few modifications.
Furthermore smp_fetch_body_buf() used to fetch the body contents is no more needed.
Update the documentation for gRPC.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 75383f8..3ce29ca 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -13792,17 +13792,11 @@
contain characters 'a-z', 'A-Z', '0-9', '.' and '_'.
svarint
- Converts a binary input sample of a protocol buffers signed "varints" ("sint32"
+ Converts a binary input sample of a protocol buffers signed "varint" ("sint32"
and "sint64") to an integer.
- More information may be found here about the protocol buffers message types:
+ More information may be found here about the protocol buffers message field types:
https://developers.google.com/protocol-buffers/docs/encoding
-varint
- Converts a binary input sample of a protocol buffers "varints", excepted
- the signed ones "sint32" and "sint64", to an integer.
- More information may be found here about the protocol buffers message types:
- https://developers.google.com/protocol-buffers/docs/encoding
-
table_bytes_in_rate(<table>)
Uses the string representation of the input sample to perform a look up in
the specified table. If the key is not found in the table, integer value zero
@@ -13973,6 +13967,39 @@
Takes an url-encoded string provided as input and returns the decoded
version as output. The input and the output are of type string.
+ungrpc(<field_number>) : binary
+ This extracts the protocol buffers message field in raw mode of an input binary
+ sample with <field_number> as field number (dotted notation).
+
+ Example:
+ // with such a protocol buffer .proto file content adapted from
+ // https://github.com/grpc/grpc/blob/master/examples/protos/route_guide.proto
+
+ message Point {
+ int32 latitude = 1;
+ int32 longitude = 2;
+ }
+
+ message PPoint {
+ Point point = 59;
+ }
+
+ message Rectangle {
+ // One corner of the rectangle.
+ PPoint lo = 48;
+ // The other corner of the rectangle.
+ PPoint hi = 49;
+ }
+
+ let's say a body request is made of a "Rectangle" object value (two PPoint
+ protocol buffers messages), the four protocol buffers fields could be
+ extracted with these "ungrpc" directives:
+
+ req.body,ungrpc(48.59.1) # "latitude" of "lo" first PPoint
+ req.body,ungrpc(48.59.2) # "longitude" of "lo" first PPoint
+ req.body,ungrpc(49.59.1) # "latidude" of "hi" second PPoint
+ req.body,ungrpc(49.59.2) # "longitude" of "hi" second PPoint
+
unset-var(<var name>)
Unsets a variable if the input content is defined. The name of the variable
starts with an indication about its scope. The scopes allowed are:
@@ -13999,6 +14026,12 @@
# e.g. 20140710162350 127.0.0.1:57325
log-format %[date,utime(%Y%m%d%H%M%S)]\ %ci:%cp
+varint
+ Converts a binary input sample of a protocol buffers "varint", excepted
+ the signed ones "sint32" and "sint64", to an integer.
+ More information may be found here about the protocol buffers message field types:
+ https://developers.google.com/protocol-buffers/docs/encoding
+
word(<index>,<delimiters>[,<count>])
Extracts the nth word counting from the beginning (positive index) or from
the end (negative index) considering given delimiters from an input string.
@@ -15976,38 +16009,6 @@
the first one. Negative values indicate positions relative to the last one,
with -1 being the last one. A typical use is with the X-Forwarded-For header.
-req.ungrpc(<field_number>) : binary
- This extracts the protocol buffers message in raw mode of a gRPC request body
- with <field_number> as terminal field number (dotted notation).
-
- Example:
- // with such a protocol buffer .proto file content adapted from
- // https://github.com/grpc/grpc/blob/master/examples/protos/route_guide.proto
-
- message Point {
- int32 latitude = 1;
- int32 longitude = 2;
- }
-
- message PPoint {
- Point point = 59;
- }
-
- message Rectangle {
- // One corner of the rectangle.
- PPoint lo = 48;
- // The other corner of the rectangle.
- PPoint hi = 49;
- }
-
- Let's say a body requests is made of a "Rectangle" object value (two PPoint
- protocol buffers messages), the four protocol buffers messages could be fetched
- with this "req.ungrpc" sample fetch directives:
-
- req.ungrpc(48.59.1) # "latitude" of "lo" first PPoint
- req.ungrpc(48.59.2) # "longitude" of "lo" first PPoint
- req.ungrpc(49.59.1) # "latidude" of "hi" second PPoint
- req.ungrpc(49.59.2) # "longitude" of "hi" second PPoint
http_auth(<userlist>) : boolean
diff --git a/src/http_fetch.c b/src/http_fetch.c
index 8f88646..51f2ef1 100644
--- a/src/http_fetch.c
+++ b/src/http_fetch.c
@@ -39,7 +39,6 @@
#include <proto/log.h>
#include <proto/obj_type.h>
#include <proto/proto_http.h>
-#include <proto/protocol_buffers.h>
#include <proto/sample.h>
#include <proto/stream.h>
@@ -1517,245 +1516,6 @@
return ret;
}
-static inline struct buffer *
-smp_fetch_body_buf(const struct arg *args, struct sample *smp)
-{
- struct buffer *buf;
-
- if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
- /* HTX version */
- struct htx *htx = smp_prefetch_htx(smp, args);
- int32_t pos;
-
- if (!htx)
- return NULL;
-
- buf = get_trash_chunk();
- for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
- struct htx_blk *blk = htx_get_blk(htx, pos);
- enum htx_blk_type type = htx_get_blk_type(blk);
-
- if (type == HTX_BLK_EOM || type == HTX_BLK_EOD)
- break;
- if (type == HTX_BLK_DATA) {
- if (!htx_data_to_h1(htx_get_blk_value(htx, blk), buf, 0))
- return NULL;
- }
- }
- }
- else {
- /* LEGACY version */
- struct http_msg *msg;
- unsigned long len;
- unsigned long block1;
- char *body;
-
- if (smp_prefetch_http(smp->px, smp->strm, smp->opt, args, smp, 1) <= 0)
- return NULL;
-
- if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
- msg = &smp->strm->txn->req;
- else
- msg = &smp->strm->txn->rsp;
-
- len = http_body_bytes(msg);
- body = c_ptr(msg->chn, -http_data_rewind(msg));
-
- block1 = len;
- if (block1 > b_wrap(&msg->chn->buf) - body)
- block1 = b_wrap(&msg->chn->buf) - body;
-
- buf = get_trash_chunk();
- if (block1 == len) {
- /* buffer is not wrapped (or empty) */
- memcpy(buf->area, body, len);
- }
- else {
- /* buffer is wrapped, we need to defragment it */
- memcpy(buf->area, body, block1);
- memcpy(buf->area + block1, b_orig(&msg->chn->buf),
- len - block1);
- }
- buf->data = len;
- }
-
- return buf;
-}
-
-#define GRPC_MSG_COMPRESS_FLAG_SZ 1 /* 1 byte */
-#define GRPC_MSG_LENGTH_SZ 4 /* 4 bytes */
-#define GRPC_MSG_HEADER_SZ (GRPC_MSG_COMPRESS_FLAG_SZ + GRPC_MSG_LENGTH_SZ)
-
-/*
- * Fetch a gRPC field value. Takes a mandatory argument: the field identifier
- * (dotted notation) internally represented as an array of unsigned integers
- * and its size.
- * Return 1 if the field was found, 0 if not.
- */
-static int smp_fetch_req_ungrpc(const struct arg *args, struct sample *smp, const char *kw, void *private)
-{
- struct buffer *body;
- unsigned char *pos;
- size_t grpc_left;
- unsigned int *fid;
- size_t fid_sz;
-
- if (!smp->strm)
- return 0;
-
- fid = args[0].data.fid.ids;
- fid_sz = args[0].data.fid.sz;
-
- body = smp_fetch_body_buf(args, smp);
- if (!body)
- return 0;
-
- pos = (unsigned char *)body->area;
- /* Remaining bytes in the body to be parsed. */
- grpc_left = body->data;
-
- while (grpc_left > GRPC_MSG_COMPRESS_FLAG_SZ + GRPC_MSG_LENGTH_SZ) {
- int next_field, found;
- size_t grpc_msg_len, left;
- unsigned int wire_type, field_number;
- uint64_t key, elen;
-
- grpc_msg_len = left = ntohl(*(uint32_t *)(pos + GRPC_MSG_COMPRESS_FLAG_SZ));
-
- pos += GRPC_MSG_HEADER_SZ;
- grpc_left -= GRPC_MSG_HEADER_SZ;
-
- if (grpc_left < left)
- return 0;
-
- found = 1;
- /* Length of the length-delimited messages if any. */
- elen = 0;
-
- /* Message decoding: there may be serveral key+value protobuf pairs by
- * gRPC message.
- */
- next_field = 0;
- while (next_field < fid_sz) {
- uint64_t sleft;
-
- if ((ssize_t)left <= 0)
- return 0;
-
- /* Remaining bytes saving. */
- sleft = left;
-
- /* Key decoding */
- if (!protobuf_decode_varint(&key, &pos, &left))
- return 0;
-
- wire_type = key & 0x7;
- field_number = key >> 3;
- found = field_number == fid[next_field];
-
- if (found && field_number != fid[next_field])
- found = 0;
-
- switch (wire_type) {
- case PBUF_TYPE_VARINT:
- {
- if (!found) {
- protobuf_skip_varint(&pos, &left);
- } else if (next_field == fid_sz - 1) {
- int varint_len;
- unsigned char *spos = pos;
-
- varint_len = protobuf_varint_getlen(&pos, &left);
- if (varint_len == -1)
- return 0;
-
- smp->data.type = SMP_T_BIN;
- smp->data.u.str.area = (char *)spos;
- smp->data.u.str.data = varint_len;
- smp->flags = SMP_F_VOL_TEST;
- return 1;
- }
- break;
- }
-
- case PBUF_TYPE_64BIT:
- {
- if (!found) {
- pos += sizeof(uint64_t);
- left -= sizeof(uint64_t);
- } else if (next_field == fid_sz - 1) {
- smp->data.type = SMP_T_BIN;
- smp->data.u.str.area = (char *)pos;
- smp->data.u.str.data = sizeof(uint64_t);
- smp->flags = SMP_F_VOL_TEST;
- return 1;
- }
- break;
- }
-
- case PBUF_TYPE_LENGTH_DELIMITED:
- {
- /* Decode the length of this length-delimited field. */
- if (!protobuf_decode_varint(&elen, &pos, &left))
- return 0;
-
- if (elen > left)
- return 0;
-
- /* The size of the current field is computed from here do skip
- * the bytes to encode the previous lenght.*
- */
- sleft = left;
- if (!found) {
- /* Skip the current length-delimited field. */
- pos += elen;
- left -= elen;
- break;
- } else if (next_field == fid_sz - 1) {
- smp->data.type = SMP_T_BIN;
- smp->data.u.str.area = (char *)pos;
- smp->data.u.str.data = elen;
- smp->flags = SMP_F_VOL_TEST;
- return 1;
- }
- break;
- }
-
- case PBUF_TYPE_32BIT:
- {
- if (!found) {
- pos += sizeof(uint32_t);
- left -= sizeof(uint32_t);
- } else if (next_field == fid_sz - 1) {
- smp->data.type = SMP_T_BIN;
- smp->data.u.str.area = (char *)pos;
- smp->data.u.str.data = sizeof(uint32_t);
- smp->flags = SMP_F_VOL_TEST;
- return 1;
- }
- break;
- }
-
- default:
- return 0;
- }
-
- if ((ssize_t)(elen) > 0)
- elen -= sleft - left;
-
- if (found) {
- next_field++;
- }
- else if ((ssize_t)elen <= 0) {
- next_field = 0;
- }
- }
- grpc_left -= grpc_msg_len;
- }
-
- return 0;
-}
-
/* Fetch an HTTP header's IP value. takes a mandatory argument of type string
* and an optional one of type int to designate a specific occurrence.
* It returns an IPv4 or IPv6 address.
@@ -3122,7 +2882,6 @@
{ "req.hdr_ip", smp_fetch_hdr_ip, ARG2(0,STR,SINT), val_hdr, SMP_T_IPV4, SMP_USE_HRQHV },
{ "req.hdr_names", smp_fetch_hdr_names, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
{ "req.hdr_val", smp_fetch_hdr_val, ARG2(0,STR,SINT), val_hdr, SMP_T_SINT, SMP_USE_HRQHV },
- { "req.ungrpc", smp_fetch_req_ungrpc, ARG1(1, PBUF_FNUM), NULL, SMP_T_BIN, SMP_USE_HRQHV },
/* explicit req.{cook,hdr} are used to force the fetch direction to be response-only */
{ "res.cook", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRSHV },
diff --git a/src/sample.c b/src/sample.c
index 1f323bd..9c20469 100644
--- a/src/sample.c
+++ b/src/sample.c
@@ -2776,6 +2776,175 @@
return 1;
}
+#define GRPC_MSG_COMPRESS_FLAG_SZ 1 /* 1 byte */
+#define GRPC_MSG_LENGTH_SZ 4 /* 4 bytes */
+#define GRPC_MSG_HEADER_SZ (GRPC_MSG_COMPRESS_FLAG_SZ + GRPC_MSG_LENGTH_SZ)
+
+/*
+ * Extract the field value of an input binary sample. Takes a mandatory argument:
+ * the protocol buffers field identifier (dotted notation) internally represented
+ * as an array of unsigned integers and its size.
+ * Return 1 if the field was found, 0 if not.
+ */
+static int sample_conv_ungrpc(const struct arg *arg_p, struct sample *smp, void *private)
+{
+ unsigned char *pos;
+ size_t grpc_left;
+ unsigned int *fid;
+ size_t fid_sz;
+
+ if (!smp->strm)
+ return 0;
+
+ fid = arg_p[0].data.fid.ids;
+ fid_sz = arg_p[0].data.fid.sz;
+
+ pos = (unsigned char *)smp->data.u.str.area;
+ /* Remaining bytes in the body to be parsed. */
+ grpc_left = smp->data.u.str.data;
+
+ while (grpc_left > GRPC_MSG_COMPRESS_FLAG_SZ + GRPC_MSG_LENGTH_SZ) {
+ int next_field, found;
+ size_t grpc_msg_len, left;
+ unsigned int wire_type, field_number;
+ uint64_t key, elen;
+
+ grpc_msg_len = left = ntohl(*(uint32_t *)(pos + GRPC_MSG_COMPRESS_FLAG_SZ));
+
+ pos += GRPC_MSG_HEADER_SZ;
+ grpc_left -= GRPC_MSG_HEADER_SZ;
+
+ if (grpc_left < left)
+ return 0;
+
+ found = 1;
+ /* Length of the length-delimited messages if any. */
+ elen = 0;
+
+ /* Message decoding: there may be serveral key+value protobuf pairs by
+ * gRPC message.
+ */
+ next_field = 0;
+ while (next_field < fid_sz) {
+ uint64_t sleft;
+
+ if ((ssize_t)left <= 0)
+ return 0;
+
+ /* Remaining bytes saving. */
+ sleft = left;
+
+ /* Key decoding */
+ if (!protobuf_decode_varint(&key, &pos, &left))
+ return 0;
+
+ wire_type = key & 0x7;
+ field_number = key >> 3;
+ found = field_number == fid[next_field];
+
+ if (found && field_number != fid[next_field])
+ found = 0;
+
+ switch (wire_type) {
+ case PBUF_TYPE_VARINT:
+ {
+ if (!found) {
+ protobuf_skip_varint(&pos, &left);
+ } else if (next_field == fid_sz - 1) {
+ int varint_len;
+ unsigned char *spos = pos;
+
+ varint_len = protobuf_varint_getlen(&pos, &left);
+ if (varint_len == -1)
+ return 0;
+
+ smp->data.type = SMP_T_BIN;
+ smp->data.u.str.area = (char *)spos;
+ smp->data.u.str.data = varint_len;
+ smp->flags = SMP_F_VOL_TEST;
+ return 1;
+ }
+ break;
+ }
+
+ case PBUF_TYPE_64BIT:
+ {
+ if (!found) {
+ pos += sizeof(uint64_t);
+ left -= sizeof(uint64_t);
+ } else if (next_field == fid_sz - 1) {
+ smp->data.type = SMP_T_BIN;
+ smp->data.u.str.area = (char *)pos;
+ smp->data.u.str.data = sizeof(uint64_t);
+ smp->flags = SMP_F_VOL_TEST;
+ return 1;
+ }
+ break;
+ }
+
+ case PBUF_TYPE_LENGTH_DELIMITED:
+ {
+ /* Decode the length of this length-delimited field. */
+ if (!protobuf_decode_varint(&elen, &pos, &left))
+ return 0;
+
+ if (elen > left)
+ return 0;
+
+ /* The size of the current field is computed from here do skip
+ * the bytes to encode the previous lenght.*
+ */
+ sleft = left;
+ if (!found) {
+ /* Skip the current length-delimited field. */
+ pos += elen;
+ left -= elen;
+ break;
+ } else if (next_field == fid_sz - 1) {
+ smp->data.type = SMP_T_BIN;
+ smp->data.u.str.area = (char *)pos;
+ smp->data.u.str.data = elen;
+ smp->flags = SMP_F_VOL_TEST;
+ return 1;
+ }
+ break;
+ }
+
+ case PBUF_TYPE_32BIT:
+ {
+ if (!found) {
+ pos += sizeof(uint32_t);
+ left -= sizeof(uint32_t);
+ } else if (next_field == fid_sz - 1) {
+ smp->data.type = SMP_T_BIN;
+ smp->data.u.str.area = (char *)pos;
+ smp->data.u.str.data = sizeof(uint32_t);
+ smp->flags = SMP_F_VOL_TEST;
+ return 1;
+ }
+ break;
+ }
+
+ default:
+ return 0;
+ }
+
+ if ((ssize_t)(elen) > 0)
+ elen -= sleft - left;
+
+ if (found) {
+ next_field++;
+ }
+ else if ((ssize_t)elen <= 0) {
+ next_field = 0;
+ }
+ }
+ grpc_left -= grpc_msg_len;
+ }
+
+ return 0;
+}
+
/* This function checks the "strcmp" converter's arguments and extracts the
* variable name and its scope.
*/
@@ -3161,6 +3330,9 @@
{ "sha1", sample_conv_sha1, 0, NULL, SMP_T_BIN, SMP_T_BIN },
{ "concat", sample_conv_concat, ARG3(1,STR,STR,STR), smp_check_concat, SMP_T_STR, SMP_T_STR },
{ "strcmp", sample_conv_strcmp, ARG1(1,STR), smp_check_strcmp, SMP_T_STR, SMP_T_SINT },
+
+ /* gRPC converters. */
+ { "ungrpc", sample_conv_ungrpc, ARG1(1,PBUF_FNUM), NULL, SMP_T_BIN, SMP_T_BIN },
{ "varint", sample_conv_varint, 0, NULL, SMP_T_BIN, SMP_T_SINT },
{ "svarint", sample_conv_svarint, 0, NULL, SMP_T_BIN, SMP_T_SINT },