diff --git a/doc/configuration.txt b/doc/configuration.txt index 69dfbe7bb..85d94d985 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 6bd89bc22..b89d0181b 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 },