From: Christopher Faulet Date: Thu, 30 Apr 2020 09:30:00 +0000 (+0200) Subject: MINOR: checks: Add support of HTTP response sample fetches X-Git-Tag: v2.2-dev7~17 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=16032ab44a7179d3a2783a83d9992755d21a9873;p=thirdparty%2Fhaproxy.git MINOR: checks: Add support of HTTP response sample fetches HTPP sample fetches acting on the response can now be called from any sample expression or log-format string in a tcp-check based ruleset. To avoid any ambiguities, all these sample fetches are in the check scope, for instance check.hdr() or check.cook(). --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 4a1c150d5b..87cc69163c 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -17546,6 +17546,124 @@ check.payload_lv(,[,]) : binary (check input buffer is filled on tcp-check expect rules and reset on tcp-check send rules). +check.body : binary + Returns the available body of the HTTP response in the context of a + http-check health check as a block of data. + +check.body_param([) : string + Assumes the body of the HTTP response in the context of a http-check health + check is url-encoded. This extracts the first occurrence of the parameter + in the body, which ends before '&'. The parameter name is + case-sensitive. If no name is given, any parameter will match, and the first + one will be returned. The result is a string corresponding to the value of + the parameter as presented in the request body (no URL decoding is + performed). + +check.body_len : integer + Returns the length in bytes of the available body of the HTTP response in the + context of a http-check health check. It may be lower than the advertised + length if the body is larger than the buffer. + +check.body_size : integer + Returns the advertised length of the HTTP response's body in bytes in the + context of a http-check health check. It will represent the advertised + Content-Length header, or the size of the available body in case of chunked + encoding. + +check.cook([]) : string + Extracts the last occurrence of the cookie name on a "Set-Cookie" + header line from the HTTP response in the context of a http-check health + check, and returns its value as string. If no name is specified, the first + cookie value is returned. + +check.cook_cnt([]) : integer + Returns an integer value representing the number of occurrences of the cookie + in the HTTP response in the context of a http-check health check, or + all cookies if is not specified. + +check.cook_val([]) : integer + Extracts the last occurrence of the cookie name on a "Set-Cookie" + header line from the HTTP response in the context of a http-check health + check, and converts its value to an integer which is returned. If no name is + specified, the first cookie value is returned. + +check.fhdr([,]) : string + Extracts the last occurrence of header in an HTTP response in the + context of a http-check health check. Optionally, a specific occurrence might + be specified as a position number. Positive values indicate a position from + the first occurrence, with 1 being the first one. Negative values indicate + positions relative to the last one, with -1 being the last one. It differs + from check.hdr() in that any commas present in the value are returned and are + not used as delimiters. + +check.fhdr_cnt([]) : integer + Returns an integer value representing the number of occurrences of response + header field name , or the total number of header fields if is + not specified, in the context of a http-check health check. Contrary to its + check.hdr_cnt() cousin, this function returns the number of full line headers + and does not stop on commas. + +check.hdr([[,]]) : string + + Extracts the last occurrence of header in an HTTP response in the + context of a http-check health check. Optionally, a specific occurrence might + be specified as a position number. Positive values indicate a position from + the first occurrence, with 1 being 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 once converted to IP, associated with an + IP stick-table. The function considers any comma as a delimiter for distinct + values. If full-line headers are desired instead, use check.fhdr(). Please + carefully check RFC7231 to know how certain headers are supposed to be + parsed. Also, some of them are case insensitive (e.g. Connection). + +check.hdr_cnt([]) : integer + Returns an integer value representing the number of occurrences of response + header field name , or the total number of header field values if + is not specified, in the context of a http-check health check. It is + important to remember that one header line may count as several headers if it + has several values. The function considers any comma as a delimiter for + distinct values. If full-line headers are desired instead, check.fhdr_cnt() + should be used instead. See "check.hdr" for more information on header + matching. + +check.hdr_ip([[,]]) : ip + Extracts the last occurrence of header in an HTTP response in the + context of a http-check health check, converts it to an IPv4 or IPv6 address + and returns this address. If is omitted, every value of every header + is checked. Optionally, a specific occurrence might be specified as a + position number. Positive values indicate a position from the first + occurrence, with 1 being 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 and X-Client-IP headers. + +check.hdr_val([[,]]) : integer + Extracts the last occurrence of header in an HTTP response in the + context of a http-check health check, and converts it to an integer value. If + is omitted, every value of every header is checked. Optionally, a + specific occurrence might be specified as a position number. Positive values + indicate a position from the first occurrence, with 1 being 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. + +check.hdrs : string + Returns the headers in the HTTP response in the context of a http-check + health check as string including the last empty line separating headers from + the response body. The last empty line can be used to detect a truncated + header block. + +check.hdrs_bin : binary + Returns the headers in the HTTP response in the context of a http-check + health check in preparsed binary form. + +check.status : integer + Returns an integer containing the HTTP status code of the HTTP response in + the context of a http-check health check, for example, 302. + +check.ver : string (deprecated) + Returns the version string from the HTTP response in the context of a + http-check health check, for example "1.1". + + 7.3.8. Fetching samples for developers --------------------------------------- diff --git a/src/http_fetch.c b/src/http_fetch.c index bf1d3e9c3a..f6a948dca6 100644 --- a/src/http_fetch.c +++ b/src/http_fetch.c @@ -386,7 +386,8 @@ static int smp_fetch_rqver(const struct arg *args, struct sample *smp, const cha static int smp_fetch_stver(const struct arg *args, struct sample *smp, const char *kw, void *private) { struct channel *chn = SMP_RES_CHN(smp); - struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1); + struct check *check = ((kw[0] == 'c' && smp->sess) ? objt_check(smp->sess->origin) : NULL); + struct htx *htx = smp_prefetch_htx(smp, chn, check, 1); struct htx_sl *sl; char *ptr; int len; @@ -414,7 +415,8 @@ static int smp_fetch_stver(const struct arg *args, struct sample *smp, const cha static int smp_fetch_stcode(const struct arg *args, struct sample *smp, const char *kw, void *private) { struct channel *chn = SMP_RES_CHN(smp); - struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1); + struct check *check = ((kw[0] == 'c' && smp->sess) ? objt_check(smp->sess->origin) : NULL); + struct htx *htx = smp_prefetch_htx(smp, chn, check, 1); struct htx_sl *sl; char *ptr; int len; @@ -460,7 +462,8 @@ static int smp_fetch_uniqueid(const struct arg *args, struct sample *smp, const static int smp_fetch_hdrs(const struct arg *args, struct sample *smp, const char *kw, void *private) { struct channel *chn = SMP_REQ_CHN(smp); - struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1); + struct check *check = ((kw[0] == 'c' && smp->sess) ? objt_check(smp->sess->origin) : NULL); + struct htx *htx = smp_prefetch_htx(smp, chn, check, 1); struct buffer *temp; int32_t pos; @@ -505,7 +508,8 @@ static int smp_fetch_hdrs(const struct arg *args, struct sample *smp, const char static int smp_fetch_hdrs_bin(const struct arg *args, struct sample *smp, const char *kw, void *private) { struct channel *chn = SMP_REQ_CHN(smp); - struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1); + struct check *check = ((kw[0] == 'c' && smp->sess) ? objt_check(smp->sess->origin) : NULL); + struct htx *htx = smp_prefetch_htx(smp, chn, check, 1); struct buffer *temp; char *p, *end; int32_t pos; @@ -572,7 +576,8 @@ static int smp_fetch_hdrs_bin(const struct arg *args, struct sample *smp, const static int smp_fetch_body(const struct arg *args, struct sample *smp, const char *kw, void *private) { struct channel *chn = SMP_REQ_CHN(smp); - struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1); + struct check *check = ((kw[0] == 'c' && smp->sess) ? objt_check(smp->sess->origin) : NULL); + struct htx *htx = smp_prefetch_htx(smp, chn, check, 1); struct buffer *temp; int32_t pos; @@ -605,7 +610,8 @@ static int smp_fetch_body(const struct arg *args, struct sample *smp, const char static int smp_fetch_body_len(const struct arg *args, struct sample *smp, const char *kw, void *private) { struct channel *chn = SMP_REQ_CHN(smp); - struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1); + struct check *check = ((kw[0] == 'c' && smp->sess) ? objt_check(smp->sess->origin) : NULL); + struct htx *htx = smp_prefetch_htx(smp, chn, check, 1); int32_t pos; unsigned long long len = 0; @@ -636,7 +642,8 @@ static int smp_fetch_body_len(const struct arg *args, struct sample *smp, const static int smp_fetch_body_size(const struct arg *args, struct sample *smp, const char *kw, void *private) { struct channel *chn = SMP_REQ_CHN(smp); - struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1); + struct check *check = ((kw[0] == 'c' && smp->sess) ? objt_check(smp->sess->origin) : NULL); + struct htx *htx = smp_prefetch_htx(smp, chn, check, 1); int32_t pos; unsigned long long len = 0; @@ -732,7 +739,8 @@ static int smp_fetch_fhdr(const struct arg *args, struct sample *smp, const char { /* possible keywords: req.fhdr, res.fhdr */ struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp)); - struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1); + struct check *check = ((kw[0] == 'c' && smp->sess) ? objt_check(smp->sess->origin) : NULL); + struct htx *htx = smp_prefetch_htx(smp, chn, check, 1); struct http_hdr_ctx *ctx = smp->ctx.a[0]; struct ist name; int occ = 0; @@ -785,7 +793,8 @@ static int smp_fetch_fhdr_cnt(const struct arg *args, struct sample *smp, const { /* possible keywords: req.fhdr_cnt, res.fhdr_cnt */ struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp)); - struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1); + struct check *check = ((kw[0] == 'c' && smp->sess) ? objt_check(smp->sess->origin) : NULL); + struct htx *htx = smp_prefetch_htx(smp, chn, check, 1); struct http_hdr_ctx ctx; struct ist name; int cnt; @@ -815,7 +824,8 @@ static int smp_fetch_hdr_names(const struct arg *args, struct sample *smp, const { /* possible keywords: req.hdr_names, res.hdr_names */ struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp)); - struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1); + struct check *check = ((kw[0] == 'c' && smp->sess) ? objt_check(smp->sess->origin) : NULL); + struct htx *htx = smp_prefetch_htx(smp, chn, check, 1); struct buffer *temp; char del = ','; @@ -860,7 +870,8 @@ static int smp_fetch_hdr(const struct arg *args, struct sample *smp, const char { /* possible keywords: req.hdr / hdr, res.hdr / shdr */ struct channel *chn = ((kw[0] == 'h' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp)); - struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1); + struct check *check = ((kw[0] == 'c' && smp->sess) ? objt_check(smp->sess->origin) : NULL); + struct htx *htx = smp_prefetch_htx(smp, chn, check, 1); struct http_hdr_ctx *ctx = smp->ctx.a[0]; struct ist name; int occ = 0; @@ -923,7 +934,8 @@ static int smp_fetch_hdr_cnt(const struct arg *args, struct sample *smp, const c { /* possible keywords: req.hdr_cnt / hdr_cnt, res.hdr_cnt / shdr_cnt */ struct channel *chn = ((kw[0] == 'h' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp)); - struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1); + struct check *check = ((kw[0] == 'c' && smp->sess) ? objt_check(smp->sess->origin) : NULL); + struct htx *htx = smp_prefetch_htx(smp, chn, check, 1); struct http_hdr_ctx ctx; struct ist name; int cnt; @@ -1546,7 +1558,8 @@ static int smp_fetch_cookie(const struct arg *args, struct sample *smp, const ch { /* possible keywords: req.cookie / cookie / cook, res.cookie / scook / set-cookie */ struct channel *chn = ((kw[0] == 'c' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp)); - struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1); + struct check *check = ((kw[0] == 'c' && smp->sess) ? objt_check(smp->sess->origin) : NULL); + struct htx *htx = smp_prefetch_htx(smp, chn, check, 1); struct http_hdr_ctx *ctx = smp->ctx.a[2]; struct ist hdr; int occ = 0; @@ -1565,7 +1578,7 @@ static int smp_fetch_cookie(const struct arg *args, struct sample *smp, const ch if (!htx) return 0; - hdr = (!(chn->flags & CF_ISRESP) ? ist("Cookie") : ist("Set-Cookie")); + hdr = (!(check || (chn && chn->flags & CF_ISRESP)) ? ist("Cookie") : ist("Set-Cookie")); if (!occ && !(smp->opt & SMP_OPT_ITERATE)) /* no explicit occurrence and single fetch => last cookie by default */ @@ -1643,7 +1656,8 @@ static int smp_fetch_cookie_cnt(const struct arg *args, struct sample *smp, cons { /* possible keywords: req.cook_cnt / cook_cnt, res.cook_cnt / scook_cnt */ struct channel *chn = ((kw[0] == 'c' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp)); - struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1); + struct check *check = ((kw[0] == 'c' && smp->sess) ? objt_check(smp->sess->origin) : NULL); + struct htx *htx = smp_prefetch_htx(smp, chn, check, 1); struct http_hdr_ctx ctx; struct ist hdr; char *val_beg, *val_end; @@ -1655,7 +1669,7 @@ static int smp_fetch_cookie_cnt(const struct arg *args, struct sample *smp, cons if (!htx) return 0; - hdr = (!(chn->flags & CF_ISRESP) ? ist("Cookie") : ist("Set-Cookie")); + hdr = (!(check || (chn && chn->flags & CF_ISRESP)) ? ist("Cookie") : ist("Set-Cookie")); val_end = val_beg = NULL; ctx.blk = NULL; @@ -1822,6 +1836,7 @@ static int smp_fetch_url_param(const struct arg *args, struct sample *smp, const static int smp_fetch_body_param(const struct arg *args, struct sample *smp, const char *kw, void *private) { struct channel *chn = SMP_REQ_CHN(smp); + struct check *check = ((kw[0] == 'c' && smp->sess) ? objt_check(smp->sess->origin) : NULL); const char *name; int name_len; @@ -1836,7 +1851,7 @@ static int smp_fetch_body_param(const struct arg *args, struct sample *smp, cons } if (!smp->ctx.a[0]) { // first call, find the query string - struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1); + struct htx *htx = smp_prefetch_htx(smp, chn, check, 1); struct buffer *temp; int32_t pos; @@ -2114,6 +2129,26 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, { { "url_param", smp_fetch_url_param, ARG2(0,STR,STR), NULL, SMP_T_STR, SMP_USE_HRQHV }, { "urlp" , smp_fetch_url_param, ARG2(0,STR,STR), NULL, SMP_T_STR, SMP_USE_HRQHV }, { "urlp_val", smp_fetch_url_param_val, ARG2(0,STR,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV }, + + + { "check.ver", smp_fetch_stver, 0, NULL, SMP_T_STR, SMP_USE_INTRN }, + { "check.status", smp_fetch_stcode, 0, NULL, SMP_T_SINT, SMP_USE_INTRN }, + { "check.hdrs", smp_fetch_hdrs, 0, NULL, SMP_T_BIN, SMP_USE_INTRN }, + { "check.hdrs_bin", smp_fetch_hdrs_bin, 0, NULL, SMP_T_BIN, SMP_USE_INTRN }, + { "check.body", smp_fetch_body, 0, NULL, SMP_T_BIN, SMP_USE_INTRN }, + { "check.body_len", smp_fetch_body_len, 0, NULL, SMP_T_SINT, SMP_USE_INTRN }, + { "check.body_size", smp_fetch_body_size, 0, NULL, SMP_T_SINT, SMP_USE_INTRN }, + { "check.body_param", smp_fetch_body_param, ARG1(0,STR), NULL, SMP_T_BIN, SMP_USE_INTRN }, + { "check.fhdr", smp_fetch_fhdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_INTRN }, + { "check.fhdr_cnt", smp_fetch_fhdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_INTRN }, + { "check.hdr", smp_fetch_hdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_INTRN }, + { "check.hdr_cnt", smp_fetch_hdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_INTRN }, + { "check.hdr_ip", smp_fetch_hdr_ip, ARG2(0,STR,SINT), val_hdr, SMP_T_IPV4, SMP_USE_INTRN }, + { "check.hdr_names", smp_fetch_hdr_names, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_INTRN }, + { "check.hdr_val", smp_fetch_hdr_val, ARG2(0,STR,SINT), val_hdr, SMP_T_SINT, SMP_USE_INTRN }, + { "check.cook", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_INTRN }, + { "check.cook_cnt", smp_fetch_cookie_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_INTRN }, + { "check.cook_val", smp_fetch_cookie_val, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_INTRN }, { /* END */ }, }};