MEDIUM: http: provide 3 fetches for the body

Body processing is still fairly limited, but this is a start. It becomes
possible to apply regex to find contents in order to decide where to route
a request for example. Only the first chunk is parsed for now, and the
response is not yet available (the parsing function must be duplicated for
this).

req.body : binary
  This returns the HTTP request's available body as a block of data. It
  requires that the request body has been buffered made available using
  "option http-buffer-request". In case of chunked-encoded body, currently only
  the first chunk is analyzed.

req.body_len : integer
  This returns the length of the HTTP request's available body in bytes. It may
  be lower than the advertised length if the body is larger than the buffer. It
  requires that the request body has been buffered made available using
  "option http-buffer-request".

req.body_size : integer
  This returns the advertised length of the HTTP request's body in bytes. It
  will represent the advertised Content-Length header, or the size of the first
  chunk in case of chunked encoding. In order to parse the chunks, it requires
  that the request body has been buffered made available using
  "option http-buffer-request".
This commit is contained in:
Willy Tarreau 2015-05-02 00:46:08 +02:00
parent 9fbe18e174
commit a5910cc6ef
2 changed files with 123 additions and 0 deletions

View File

@ -12223,6 +12223,25 @@ capture.res.ver : string
"HTTP/1.1". Unlike "res.ver", it can be used in logs because it relies on a
persistent flag.
req.body : binary
This returns the HTTP request's available body as a block of data. It
requires that the request body has been buffered made available using
"option http-buffer-request". In case of chunked-encoded body, currently only
the first chunk is analyzed.
req.body_len : integer
This returns the length of the HTTP request's available body in bytes. It may
be lower than the advertised length if the body is larger than the buffer. It
requires that the request body has been buffered made available using
"option http-buffer-request".
req.body_size : integer
This returns the advertised length of the HTTP request's body in bytes. It
will represent the advertised Content-Length header, or the size of the first
chunk in case of chunked encoding. In order to parse the chunks, it requires
that the request body has been buffered made available using
"option http-buffer-request".
req.cook([<name>]) : string
cook([<name>]) : string (deprecated)
This extracts the last occurrence of the cookie name <name> on a "Cookie"

View File

@ -10339,6 +10339,106 @@ smp_fetch_stcode(struct proxy *px, struct session *sess, struct stream *strm, un
return 1;
}
/* returns the longest available part of the body. This requires that the body
* has been waited for using http-buffer-request.
*/
static int
smp_fetch_body(struct proxy *px, struct session *sess, struct stream *strm, unsigned int opt,
const struct arg *args, struct sample *smp, const char *kw, void *private)
{
struct http_txn *txn = strm->txn;
struct http_msg *msg;
unsigned long len;
unsigned long block1;
char *body;
struct chunk *temp;
CHECK_HTTP_MESSAGE_FIRST();
if ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
msg = &txn->req;
else
msg = &txn->rsp;
len = http_body_bytes(msg);
body = b_ptr(msg->chn->buf, -http_data_rewind(msg));
block1 = len;
if (block1 > msg->chn->buf->data + msg->chn->buf->size - body)
block1 = msg->chn->buf->data + msg->chn->buf->size - body;
if (block1 == len) {
/* buffer is not wrapped (or empty) */
smp->type = SMP_T_BIN;
smp->data.str.str = body;
smp->data.str.len = len;
smp->flags = SMP_F_VOL_TEST | SMP_F_CONST;
}
else {
/* buffer is wrapped, we need to defragment it */
temp = get_trash_chunk();
memcpy(temp->str, body, block1);
memcpy(temp->str + block1, msg->chn->buf->data, len - block1);
smp->type = SMP_T_BIN;
smp->data.str.str = temp->str;
smp->data.str.len = len;
smp->flags = SMP_F_VOL_TEST;
}
return 1;
}
/* returns the available length of the body. This requires that the body
* has been waited for using http-buffer-request.
*/
static int
smp_fetch_body_len(struct proxy *px, struct session *sess, struct stream *strm, unsigned int opt,
const struct arg *args, struct sample *smp, const char *kw, void *private)
{
struct http_txn *txn = strm->txn;
struct http_msg *msg;
CHECK_HTTP_MESSAGE_FIRST();
if ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
msg = &txn->req;
else
msg = &txn->rsp;
smp->type = SMP_T_UINT;
smp->data.uint = http_body_bytes(msg);
smp->flags = SMP_F_VOL_TEST;
return 1;
}
/* returns the advertised length of the body, or the advertised size of the
* chunks available in the buffer. This requires that the body has been waited
* for using http-buffer-request.
*/
static int
smp_fetch_body_size(struct proxy *px, struct session *sess, struct stream *strm, unsigned int opt,
const struct arg *args, struct sample *smp, const char *kw, void *private)
{
struct http_txn *txn = strm->txn;
struct http_msg *msg;
CHECK_HTTP_MESSAGE_FIRST();
if ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
msg = &txn->req;
else
msg = &txn->rsp;
smp->type = SMP_T_UINT;
smp->data.uint = msg->body_len;
smp->flags = SMP_F_VOL_TEST;
return 1;
}
/* 4. Check on URL/URI. A pointer to the URI is stored. */
static int
smp_fetch_url(struct proxy *px, struct session *sess, struct stream *strm, unsigned int opt,
@ -12206,6 +12306,10 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
{ "req.ver", smp_fetch_rqver, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
{ "req_ver", smp_fetch_rqver, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
{ "req.body", smp_fetch_body, 0, NULL, SMP_T_BIN, SMP_USE_HRQHV },
{ "req.body_len", smp_fetch_body_len, 0, NULL, SMP_T_UINT, SMP_USE_HRQHV },
{ "req.body_size", smp_fetch_body_size, 0, NULL, SMP_T_UINT, SMP_USE_HRQHV },
/* HTTP version on the response path */
{ "res.ver", smp_fetch_stver, 0, NULL, SMP_T_STR, SMP_USE_HRSHV },
{ "resp_ver", smp_fetch_stver, 0, NULL, SMP_T_STR, SMP_USE_HRSHV },