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.
This commit is contained in:
Frdric Lcaille 2019-02-27 14:34:51 +01:00 committed by Willy Tarreau
parent 927b88ba00
commit 50290fbb42
3 changed files with 213 additions and 281 deletions

View File

@ -13792,15 +13792,9 @@ sub(<value>)
contain characters 'a-z', 'A-Z', '0-9', '.' and '_'. contain characters 'a-z', 'A-Z', '0-9', '.' and '_'.
svarint 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. 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 https://developers.google.com/protocol-buffers/docs/encoding
table_bytes_in_rate(<table>) table_bytes_in_rate(<table>)
@ -13973,6 +13967,39 @@ url_dec
Takes an url-encoded string provided as input and returns the decoded Takes an url-encoded string provided as input and returns the decoded
version as output. The input and the output are of type string. 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>) unset-var(<var name>)
Unsets a variable if the input content is defined. The name of the variable 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: starts with an indication about its scope. The scopes allowed are:
@ -13999,6 +14026,12 @@ utime(<format>[,<offset>])
# e.g. 20140710162350 127.0.0.1:57325 # e.g. 20140710162350 127.0.0.1:57325
log-format %[date,utime(%Y%m%d%H%M%S)]\ %ci:%cp 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>]) word(<index>,<delimiters>[,<count>])
Extracts the nth word counting from the beginning (positive index) or from Extracts the nth word counting from the beginning (positive index) or from
the end (negative index) considering given delimiters from an input string. the end (negative index) considering given delimiters from an input string.
@ -15976,38 +16009,6 @@ hdr_val([<name>[,<occ>]]) : integer (deprecated)
the first one. Negative values indicate positions relative to the last one, 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. 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 http_auth(<userlist>) : boolean

View File

@ -39,7 +39,6 @@
#include <proto/log.h> #include <proto/log.h>
#include <proto/obj_type.h> #include <proto/obj_type.h>
#include <proto/proto_http.h> #include <proto/proto_http.h>
#include <proto/protocol_buffers.h>
#include <proto/sample.h> #include <proto/sample.h>
#include <proto/stream.h> #include <proto/stream.h>
@ -1517,245 +1516,6 @@ static int smp_fetch_hdr_val(const struct arg *args, struct sample *smp, const c
return ret; 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 /* 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. * and an optional one of type int to designate a specific occurrence.
* It returns an IPv4 or IPv6 address. * It returns an IPv4 or IPv6 address.
@ -3122,7 +2882,6 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
{ "req.hdr_ip", smp_fetch_hdr_ip, ARG2(0,STR,SINT), val_hdr, SMP_T_IPV4, SMP_USE_HRQHV }, { "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_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.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 */ /* 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 }, { "res.cook", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRSHV },

View File

@ -2776,6 +2776,175 @@ static int sample_conv_strcmp(const struct arg *arg_p, struct sample *smp, void
return 1; 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 /* This function checks the "strcmp" converter's arguments and extracts the
* variable name and its scope. * variable name and its scope.
*/ */
@ -3161,6 +3330,9 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH, {
{ "sha1", sample_conv_sha1, 0, NULL, SMP_T_BIN, SMP_T_BIN }, { "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 }, { "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 }, { "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 }, { "varint", sample_conv_varint, 0, NULL, SMP_T_BIN, SMP_T_SINT },
{ "svarint", sample_conv_svarint, 0, NULL, SMP_T_BIN, SMP_T_SINT }, { "svarint", sample_conv_svarint, 0, NULL, SMP_T_BIN, SMP_T_SINT },