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 '_'.
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:
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:
More information may be found here about the protocol buffers message field types:
https://developers.google.com/protocol-buffers/docs/encoding
table_bytes_in_rate(<table>)
@ -13973,6 +13967,39 @@ url_dec
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 @@ utime(<format>[,<offset>])
# 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 @@ hdr_val([<name>[,<occ>]]) : integer (deprecated)
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

View File

@ -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 @@ static int smp_fetch_hdr_val(const struct arg *args, struct sample *smp, const c
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 @@ 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_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 },

View File

@ -2776,6 +2776,175 @@ static int sample_conv_strcmp(const struct arg *arg_p, struct sample *smp, void
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 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH, {
{ "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 },