mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-01-31 18:41:39 +00:00
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:
parent
927b88ba00
commit
50290fbb42
@ -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
|
||||
|
241
src/http_fetch.c
241
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 @@ 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 },
|
||||
|
172
src/sample.c
172
src/sample.c
@ -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 },
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user