From a5910cc6ef96c39310f84063766953c914a2f58f Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sat, 2 May 2015 00:46:08 +0200 Subject: [PATCH] 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". --- doc/configuration.txt | 19 ++++++++ src/proto_http.c | 104 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/doc/configuration.txt b/doc/configuration.txt index 69dfbe7bbc..85d94d9859 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -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([]) : string cook([]) : string (deprecated) This extracts the last occurrence of the cookie name on a "Cookie" diff --git a/src/proto_http.c b/src/proto_http.c index 6bd89bc22c..b89d0181bd 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -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 },