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 4a1c150..87cc691 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -17546,6 +17546,124 @@
(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([<name>) : 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
+ <name> 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 <name> 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([<name>]) : string
+ Extracts the last occurrence of the cookie name <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([<name>]) : integer
+ Returns an integer value representing the number of occurrences of the cookie
+ <name> in the HTTP response in the context of a http-check health check, or
+ all cookies if <name> is not specified.
+
+check.cook_val([<name>]) : integer
+ Extracts the last occurrence of the cookie name <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(<name>[,<occ>]) : string
+ Extracts the last occurrence of header <name> 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([<name>]) : integer
+ Returns an integer value representing the number of occurrences of response
+ header field name <name>, or the total number of header fields if <name> 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([<name>[,<occ>]]) : string
+
+ Extracts the last occurrence of header <name> 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([<name>]) : integer
+ Returns an integer value representing the number of occurrences of response
+ header field name <name>, or the total number of header field values if
+ <name> 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([<name>[,<occ>]]) : ip
+ Extracts the last occurrence of header <name> 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 <name> 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([<name>[,<occ>]]) : integer
+ Extracts the last occurrence of header <name> in an HTTP response in the
+ context of a http-check health check, and converts it to an integer value. If
+ <name> 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 bf1d3e9..f6a948d 100644
--- a/src/http_fetch.c
+++ b/src/http_fetch.c
@@ -386,7 +386,8 @@
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_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_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_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_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_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_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 @@
{
/* 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 @@
{
/* 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 @@
{
/* 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 @@
{
/* 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 @@
{
/* 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 @@
{
/* 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 @@
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 @@
{
/* 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 @@
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_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 @@
}
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 @@
{ "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 */ },
}};