MEDIUM: checks: Support matching on headers for http-check expect rules
It is now possible to add http-check expect rules matching HTTP header names and
values. Here is the format of these rules:
http-check expect header name [ -m <meth> ] <name> [log-format] \
[ value [ -m <meth> ] <value> [log-format] [full] ]
the name pattern (name ...) is mandatory but the value pattern (value ...) is
optionnal. If not specified, only the header presence is verified. <meth> is the
matching method, applied on the header name or the header value. Supported
matching methods are:
* "str" (exact match)
* "beg" (prefix match)
* "end" (suffix match)
* "sub" (substring match)
* "reg" (regex match)
If not specified, exact matching method is used. If the "log-format" option is
used, the pattern (<name> or <value>) is evaluated as a log-format string. This
option cannot be used with the regex matching method. Finally, by default, the
header value is considered as comma-separated list. Each part may be tested. The
"full" option may be used to test the full header line. Note that matchings are
case insensitive on the header names.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 87cc691..1092fd5 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -4565,9 +4565,10 @@
between the exclamation mark and the keyword. See below for more
details on the supported keywords.
- <pattern> is the pattern to look for. It may be a string or a regular
- expression. If the pattern contains spaces, they must be escaped
- with the usual backslash ('\').
+ <pattern> is the pattern to look for. It may be a string, a regular
+ expression or a more complex pattern with several arguments. If
+ the string pattern contains spaces, they must be escaped with the
+ usual backslash ('\').
By default, "option httpchk" considers that response statuses 2xx and 3xx
are valid, and that others are invalid. When "http-check expect" is used,
@@ -4590,6 +4591,25 @@
will be considered invalid if the status code matches.
This is mostly used to check for multiple codes.
+ header name [ -m <meth> ] <name> [log-format]
+ [ value [ -m <meth> ] <value> [log-format] [full] ] :
+ test the specified header pattern on the HTTP response
+ headers. The name pattern is mandatory but the value
+ pattern is optional. If not specified, only the header
+ presence is verified. <meth> is the matching method,
+ applied on the header name or the header value. Supported
+ matching methods are "str" (exact match), "beg" (prefix
+ match), "end" (suffix match), "sub" (substring match) or
+ "reg" (regex match). If not specified, exact matching
+ method is used. If the "log-format" option is used, the
+ pattern (<name> or <value>) is evaluated as a log-format
+ string. This option cannot be used with the regex
+ matching method. Finally, by default, the header value is
+ considered as comma-separated list. Each part may be
+ tested. The "full" option may be used to test the full
+ header line. Note that matchings are case insensitive on
+ the header names.
+
string <string> : test the exact string match in the HTTP response body.
A health check response will be considered valid if the
response's body contains this exact string. If the
@@ -4631,6 +4651,9 @@
# only accept status 200 as valid
http-check expect status 200,201,300-310
+ # be sure a sessid coookie is set
+ http-check expect header name "set-cookie" value -m beg "sessid="
+
# consider SQL errors as errors
http-check expect ! string SQL\ Error
diff --git a/include/types/checks.h b/include/types/checks.h
index 4809285..927dada 100644
--- a/include/types/checks.h
+++ b/include/types/checks.h
@@ -248,17 +248,34 @@
TCPCHK_EXPECT_STRING, /* Matches a string. */
TCPCHK_EXPECT_REGEX, /* Matches a regular pattern. */
TCPCHK_EXPECT_REGEX_BINARY, /* Matches a regular pattern on a hex-encoded text. */
- TCPCHK_EXPECT_BINARY, /* Matches a binary sequence. */
+ TCPCHK_EXPECT_BINARY, /* Matches a binary sequence on a hex-encoded text. */
TCPCHK_EXPECT_CUSTOM, /* Execute a custom function. */
- TCPCHK_EXPECT_HTTP_STATUS, /* Matches a string */
- TCPCHK_EXPECT_HTTP_REGEX_STATUS, /* Matches a regular pattern */
- TCPCHK_EXPECT_HTTP_BODY, /* Matches a string */
- TCPCHK_EXPECT_HTTP_REGEX_BODY, /* Matches a regular pattern */
+ TCPCHK_EXPECT_HTTP_STATUS, /* Matches a list of codes on the HTTP status */
+ TCPCHK_EXPECT_HTTP_REGEX_STATUS, /* Matches a regular pattern on the HTTP status */
+ TCPCHK_EXPECT_HTTP_HEADER, /* Matches on HTTP headers */
+ TCPCHK_EXPECT_HTTP_BODY, /* Matches a string oa the HTTP payload */
+ TCPCHK_EXPECT_HTTP_REGEX_BODY, /* Matches a regular pattern on a HTTP payload */
};
/* tcp-check expect flags */
-#define TCPCHK_EXPT_FL_INV 0x0001 /* Matching is inversed */
+#define TCPCHK_EXPT_FL_INV 0x0001 /* Matching is inversed */
+#define TCPCHK_EXPT_FL_HTTP_HNAME_STR 0x0002 /* Exact match on the HTTP header name */
+#define TCPCHK_EXPT_FL_HTTP_HNAME_BEG 0x0004 /* Prefix match on the HTTP header name */
+#define TCPCHK_EXPT_FL_HTTP_HNAME_END 0x0008 /* Suffix match on the HTTP header name */
+#define TCPCHK_EXPT_FL_HTTP_HNAME_SUB 0x0010 /* Substring match on the HTTP header name */
+#define TCPCHK_EXPT_FL_HTTP_HNAME_REG 0x0020 /* Regex match on the HTTP header name */
+#define TCPCHK_EXPT_FL_HTTP_HNAME_FMT 0x0040 /* The HTTP header name is a log-format string */
+#define TCPCHK_EXPT_FL_HTTP_HVAL_NONE 0x0080 /* No match on the HTTP header value */
+#define TCPCHK_EXPT_FL_HTTP_HVAL_STR 0x0100 /* Exact match on the HTTP header value */
+#define TCPCHK_EXPT_FL_HTTP_HVAL_BEG 0x0200 /* Prefix match on the HTTP header value */
+#define TCPCHK_EXPT_FL_HTTP_HVAL_END 0x0400 /* Suffix match on the HTTP header value */
+#define TCPCHK_EXPT_FL_HTTP_HVAL_SUB 0x0800 /* Substring match on the HTTP header value */
+#define TCPCHK_EXPT_FL_HTTP_HVAL_REG 0x1000 /* Regex match on the HTTP header value*/
+#define TCPCHK_EXPT_FL_HTTP_HVAL_FMT 0x2000 /* The HTTP header value is a log-format string */
+#define TCPCHK_EXPT_FL_HTTP_HVAL_FULL 0x4000 /* Match the full header value ( no stop on commas ) */
+#define TCPCHK_EXPT_FL_HTTP_HNAME_TYPE 0x003E /* Mask to get matching method on header name */
+#define TCPCHK_EXPT_FL_HTTP_HVAL_TYPE 0x1F00 /* Mask to get matching method on header value */
struct tcpcheck_expect {
enum tcpcheck_expect_type type; /* Type of pattern used for matching. */
unsigned int flags; /* TCPCHK_EXPT_FL_* */
@@ -266,6 +283,19 @@
struct ist data; /* Matching a literal string / binary anywhere in the response. */
struct my_regex *regex; /* Matching a regex pattern. */
struct tcpcheck_codes codes; /* Matching a list of codes */
+ struct {
+ union {
+ struct ist name;
+ struct list name_fmt;
+ struct my_regex *name_re;
+ };
+ union {
+ struct ist value;
+ struct list value_fmt;
+ struct my_regex *value_re;
+ };
+ } hdr; /* Matching a header pattern */
+
/* custom function to eval epxect rule */
enum tcpcheck_eval_ret (*custom)(struct check *, struct tcpcheck_rule *, int);
diff --git a/reg-tests/checks/http-check-expect.vtc b/reg-tests/checks/http-check-expect.vtc
new file mode 100644
index 0000000..83e4330
--- /dev/null
+++ b/reg-tests/checks/http-check-expect.vtc
@@ -0,0 +1,66 @@
+varnishtest "Health-checks: some http-check expect tests"
+feature ignore_unknown_macro
+#REQUIRE_VERSION=2.2
+#REGTEST_TYPE=slow
+# This script tests http-check expect rules.
+
+server s1 {
+ rxreq
+ expect req.method == OPTIONS
+ expect req.url == /
+ expect req.proto == HTTP/1.0
+ txresp -status 202 \
+ -hdr "x-test1: true, next value" \
+ -hdr "x-test2: true, begin-value, value-end, value-sub-string, value-reg-123ABC" \
+ -hdr "x-begin-test: 1" \
+ -hdr "x-test-end: 1" \
+ -hdr "x-sub-test: 1" \
+ -hdr "x-reg-test1: 1" \
+ -hdr "x-hdr-name: x-test1"
+} -start
+
+syslog S1 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Proxy be1 started."
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv succeeded.*code: 202"
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ timeout client 1s
+ timeout server 1s
+ timeout connect 100ms
+ option log-health-checks
+
+ backend be1
+ log ${S1_addr}:${S1_port} len 2048 local0
+ option httpchk
+ http-check expect status 200-399
+
+ http-check expect header name "x-test1"
+ http-check expect header name -m str "X-Test2"
+ http-check expect header name -m beg "X-Begin-"
+ http-check expect header name -m end "-End"
+ http-check expect header name -m sub "-Sub-"
+ http-check expect header name -m reg "^[a-z]+-Reg-[a-z]+[0-9]\$"
+ http-check set-var(check.hdr_name) check.fhdr(x-hdr-name)
+ http-check expect header name -m str "%[var(check.hdr_name)]" log-format
+ http-check expect header name -m str "%[check.fhdr(x-hdr-name)]" log-format
+
+ http-check expect header name "x-test1" value "true, next value" full
+ http-check expect header name "x-test2" value -m str "true"
+ http-check expect header name -m beg "x-test" value -m beg "begin-"
+ http-check expect header name -m beg "x-test" value -m end "-end"
+ http-check expect header name -m beg "x-test" value -m sub "-sub-"
+ http-check expect header name -m beg "x-test" value -m reg "^value-reg-[A-Z0-9]+\$"
+ http-check expect header name -m beg "x-test" value -m reg "value-reg-[A-Z0-9]+" full
+ http-check set-var(check.hdr_value) str(x-test1)
+ http-check expect header name -m beg "x-" value -m str "%[var(check.hdr_value)]" log-format
+ http-check expect header name -m beg "x-" value -m str "%[check.fhdr(x-hdr-name)]" log-format full
+
+ server srv ${s1_addr}:${s1_port} check inter 100ms rise 1 fall 1
+} -start
+
+syslog S1 -wait
diff --git a/src/checks.c b/src/checks.c
index 8d371e6..a5ddc13 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -606,6 +606,9 @@
case TCPCHK_EXPECT_HTTP_REGEX_STATUS:
chunk_appendf(chk, " (expect HTTP status regex)");
break;
+ case TCPCHK_EXPECT_HTTP_HEADER:
+ chunk_appendf(chk, " (expect HTTP header pattern)");
+ break;
case TCPCHK_EXPECT_HTTP_BODY:
chunk_appendf(chk, " (expect HTTP body content '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
break;
@@ -796,6 +799,21 @@
case TCPCHK_EXPECT_HTTP_REGEX_BODY:
regex_free(rule->expect.regex);
break;
+ case TCPCHK_EXPECT_HTTP_HEADER:
+ if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG)
+ regex_free(rule->expect.hdr.name_re);
+ else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT)
+ free_tcpcheck_fmt(&rule->expect.hdr.name_fmt);
+ else
+ istfree(&rule->expect.hdr.name);
+
+ if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG)
+ regex_free(rule->expect.hdr.value_re);
+ else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT)
+ free_tcpcheck_fmt(&rule->expect.hdr.value_fmt);
+ else if (!(rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE))
+ istfree(&rule->expect.hdr.value);
+ break;
case TCPCHK_EXPECT_CUSTOM:
case TCPCHK_EXPECT_UNDEF:
break;
@@ -1069,6 +1087,8 @@
case TCPCHK_EXPECT_CUSTOM:
chunk_appendf(msg, " (custom function) at step %d", tcpcheck_get_step_id(check, rule));
break;
+ case TCPCHK_EXPECT_HTTP_HEADER:
+ chunk_appendf(msg, " (header pattern) at step %d", tcpcheck_get_step_id(check, rule));
case TCPCHK_EXPECT_UNDEF:
/* Should never happen. */
return;
@@ -2116,8 +2136,8 @@
struct htx_blk *blk;
enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
struct tcpcheck_expect *expect = &rule->expect;
- struct buffer *msg = NULL;
- enum healthcheck_status status;
+ struct buffer *msg = NULL, *nbuf = NULL, *vbuf = NULL;
+ enum healthcheck_status status = HCHK_STATUS_L7RSP;
struct ist desc = IST_NULL;
int i, match, inverse;
@@ -2176,6 +2196,110 @@
desc = htx_sl_res_reason(sl);
break;
+ case TCPCHK_EXPECT_HTTP_HEADER: {
+ struct http_hdr_ctx ctx;
+ struct ist npat, vpat, value;
+ int full = (expect->flags & (TCPCHK_EXPT_FL_HTTP_HVAL_NONE|TCPCHK_EXPT_FL_HTTP_HVAL_FULL));
+
+ if (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
+ nbuf = alloc_trash_chunk();
+ if (!nbuf)
+ goto error;
+ nbuf->data = sess_build_logline(check->sess, NULL, b_orig(nbuf), b_size(nbuf), &expect->hdr.name_fmt);
+ npat = ist2(b_orig(nbuf), b_data(nbuf));
+ }
+ else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG))
+ npat = expect->hdr.name;
+
+ if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
+ vbuf = alloc_trash_chunk();
+ if (!vbuf)
+ goto error;
+ vbuf->data = sess_build_logline(check->sess, NULL, b_orig(vbuf), b_size(vbuf), &expect->hdr.value_fmt);
+ vpat = ist2(b_orig(vbuf), b_data(vbuf));
+ }
+ else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG))
+ vpat = expect->hdr.value;
+
+ match = 0;
+ ctx.blk = NULL;
+ while (1) {
+ switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_TYPE) {
+ case TCPCHK_EXPT_FL_HTTP_HNAME_STR:
+ if (!http_find_str_header(htx, npat, &ctx, full))
+ goto end_of_match;
+ break;
+ case TCPCHK_EXPT_FL_HTTP_HNAME_BEG:
+ if (!http_find_pfx_header(htx, npat, &ctx, full))
+ goto end_of_match;
+ break;
+ case TCPCHK_EXPT_FL_HTTP_HNAME_END:
+ if (!http_find_sfx_header(htx, npat, &ctx, full))
+ goto end_of_match;
+ break;
+ case TCPCHK_EXPT_FL_HTTP_HNAME_SUB:
+ if (!http_find_sub_header(htx, npat, &ctx, full))
+ goto end_of_match;
+ break;
+ case TCPCHK_EXPT_FL_HTTP_HNAME_REG:
+ if (!http_match_header(htx, expect->hdr.name_re, &ctx, full))
+ goto end_of_match;
+ break;
+ }
+
+ if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
+ match = 1;
+ goto end_of_match;
+ }
+
+ value = ctx.value;
+ switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_TYPE) {
+ case TCPCHK_EXPT_FL_HTTP_HVAL_STR:
+ if (isteq(value, vpat)) {
+ match = 1;
+ goto end_of_match;
+ }
+ break;
+ case TCPCHK_EXPT_FL_HTTP_HVAL_BEG:
+ if (istlen(value) < istlen(vpat))
+ break;
+ value = ist2(istptr(value), istlen(vpat));
+ if (isteq(value, vpat)) {
+ match = 1;
+ goto end_of_match;
+ }
+ break;
+ case TCPCHK_EXPT_FL_HTTP_HVAL_END:
+ if (istlen(value) < istlen(vpat))
+ break;
+ value = ist2(istptr(value) + istlen(value) - istlen(vpat), istlen(vpat));
+ if (isteq(value, vpat)) {
+ match = 1;
+ goto end_of_match;
+ }
+ break;
+ case TCPCHK_EXPT_FL_HTTP_HVAL_SUB:
+ if (isttest(istist(value, vpat))) {
+ match = 1;
+ goto end_of_match;
+ }
+ break;
+ case TCPCHK_EXPT_FL_HTTP_HVAL_REG:
+ if (regex_exec2(expect->hdr.value_re, istptr(value), istlen(value))) {
+ match = 1;
+ goto end_of_match;
+ }
+ break;
+ }
+ }
+
+ end_of_match:
+ status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7STS);
+ if (LIST_ISEMPTY(&expect->onerror_fmt))
+ desc = htx_sl_res_reason(sl);
+ break;
+ }
+
case TCPCHK_EXPECT_HTTP_BODY:
case TCPCHK_EXPECT_HTTP_REGEX_BODY:
chunk_reset(&trash);
@@ -2237,6 +2361,8 @@
goto error;
out:
+ free_trash_chunk(nbuf);
+ free_trash_chunk(vbuf);
free_trash_chunk(msg);
return ret;
@@ -3969,15 +4095,16 @@
{
struct tcpcheck_rule *prev_check, *chk = NULL;
struct sample_expr *status_expr = NULL;
- char *on_success_msg, *on_error_msg, *comment, *pattern;
+ char *on_success_msg, *on_error_msg, *comment, *pattern, *npat, *vpat;
enum tcpcheck_expect_type type = TCPCHK_EXPECT_UNDEF;
enum healthcheck_status ok_st = HCHK_STATUS_UNKNOWN;
enum healthcheck_status err_st = HCHK_STATUS_UNKNOWN;
enum healthcheck_status tout_st = HCHK_STATUS_UNKNOWN;
+ unsigned int flags = 0;
long min_recv = -1;
int inverse = 0;
- on_success_msg = on_error_msg = comment = pattern = NULL;
+ on_success_msg = on_error_msg = comment = pattern = npat = vpat = NULL;
if (!*(args[cur_arg+1])) {
memprintf(errmsg, "expects at least a matching pattern as arguments");
goto error;
@@ -4075,6 +4202,116 @@
}
type = TCPCHK_EXPECT_CUSTOM;
}
+ else if (strcmp(args[cur_arg], "header") == 0) {
+ int orig_arg = cur_arg;
+
+ if (proto != TCPCHK_RULES_HTTP_CHK)
+ goto bad_tcp_kw;
+ if (type != TCPCHK_EXPECT_UNDEF) {
+ memprintf(errmsg, "only on pattern expected");
+ goto error;
+ }
+ type = TCPCHK_EXPECT_HTTP_HEADER;
+
+ /* Parse the name pattern, mandatory */
+ if (!*(args[cur_arg+1]) || !*(args[cur_arg+2]) || strcmp(args[cur_arg+1], "name") != 0) {
+ memprintf(errmsg, "'%s' expects at the keyword name as first argument followed by a pattern",
+ args[orig_arg]);
+ goto error;
+ }
+ cur_arg += 2;
+ if (strcmp(args[cur_arg], "-m") == 0) {
+ if (!*(args[cur_arg+1])) {
+ memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
+ args[orig_arg], args[cur_arg]);
+ goto error;
+ }
+ if (strcmp(args[cur_arg+1], "str") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
+ else if (strcmp(args[cur_arg+1], "beg") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HNAME_BEG;
+ else if (strcmp(args[cur_arg+1], "end") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HNAME_END;
+ else if (strcmp(args[cur_arg+1], "sub") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HNAME_SUB;
+ else if (strcmp(args[cur_arg+1], "reg") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HNAME_REG;
+ else {
+ memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
+ args[orig_arg], args[cur_arg], args[cur_arg+1]);
+ goto error;
+ }
+ cur_arg += 2;
+ }
+ else
+ flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
+ npat = args[cur_arg];
+
+ if (!(*args[cur_arg+1])) {
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_NONE;
+ goto next;
+ }
+
+ if (strcmp(args[cur_arg+1], "log-format") == 0) {
+ if (flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG) {
+ memprintf(errmsg, "'%s': '%s' cannot be used with a regex matching pattern",
+ args[orig_arg], args[cur_arg+1]);
+ goto error;
+ }
+ flags |= TCPCHK_EXPT_FL_HTTP_HNAME_FMT;
+ cur_arg++;
+ }
+
+ if (!(*args[cur_arg+1]) || strcmp(args[cur_arg+1], "value") != 0) {
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_NONE;
+ goto next;
+ }
+
+ /* Parse the value pattern, optionnal */
+ cur_arg += 2;
+ if (strcmp(args[cur_arg], "-m") == 0) {
+ if (!*(args[cur_arg+1])) {
+ memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
+ args[orig_arg], args[cur_arg]);
+ goto error;
+ }
+ if (strcmp(args[cur_arg+1], "str") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
+ else if (strcmp(args[cur_arg+1], "beg") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_BEG;
+ else if (strcmp(args[cur_arg+1], "end") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_END;
+ else if (strcmp(args[cur_arg+1], "sub") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_SUB;
+ else if (strcmp(args[cur_arg+1], "reg") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_REG;
+ else {
+ memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
+ args[orig_arg], args[cur_arg], args[cur_arg+1]);
+ goto error;
+ }
+ cur_arg += 2;
+ }
+ else
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
+ vpat = args[cur_arg];
+
+ while (*args[cur_arg+1]) {
+ if (strcmp(args[cur_arg+1], "log-format") == 0) {
+ if (flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG) {
+ memprintf(errmsg, "'%s': '%s' cannot be used with a regex matching pattern",
+ args[orig_arg], args[cur_arg+1]);
+ goto error;
+ }
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FMT;
+ }
+ else if (strcmp(args[cur_arg+1], "full") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FULL;
+ else
+ break;
+ cur_arg++;
+ }
+ }
else if (strcmp(args[cur_arg], "comment") == 0) {
if (in_pattern) {
memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
@@ -4220,7 +4457,7 @@
if (proto == TCPCHK_RULES_HTTP_CHK) {
bad_http_kw:
memprintf(errmsg, "'only supports min-recv, [!]string', '[!]rstring', '[!]status', '[!]rstatus'"
- " or comment but got '%s' as argument.", args[cur_arg]);
+ "[!]header or comment but got '%s' as argument.", args[cur_arg]);
}
else {
bad_tcp_kw:
@@ -4229,7 +4466,7 @@
}
goto error;
}
-
+ next:
cur_arg++;
}
@@ -4244,7 +4481,7 @@
chk->comment = comment; comment = NULL;
chk->expect.type = type;
chk->expect.min_recv = min_recv;
- chk->expect.flags |= (inverse ? TCPCHK_EXPT_FL_INV : 0);
+ chk->expect.flags = flags | (inverse ? TCPCHK_EXPT_FL_INV : 0);
chk->expect.ok_status = ok_st;
chk->expect.err_status = err_st;
chk->expect.tout_status = tout_st;
@@ -4328,6 +4565,63 @@
chk->expect.regex = regex_comp(pattern, 1, 0, errmsg);
if (!chk->expect.regex)
goto error;
+ break;
+ case TCPCHK_EXPECT_HTTP_HEADER:
+ if (!npat) {
+ memprintf(errmsg, "unexpected error, undefined header name pattern");
+ goto error;
+ }
+ if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG) {
+ chk->expect.hdr.name_re = regex_comp(npat, 0, 0, errmsg);
+ if (!chk->expect.hdr.name_re)
+ goto error;
+ }
+ else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
+ px->conf.args.ctx = ARGC_SRV;
+ LIST_INIT(&chk->expect.hdr.name_fmt);
+ if (!parse_logformat_string(npat, px, &chk->expect.hdr.name_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
+ memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
+ goto error;
+ }
+ }
+ else {
+ chk->expect.hdr.name = ist2(strdup(npat), strlen(npat));
+ if (!isttest(chk->expect.hdr.name)) {
+ memprintf(errmsg, "out of memory");
+ goto error;
+ }
+ }
+
+ if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
+ chk->expect.hdr.value = IST_NULL;
+ break;
+ }
+
+ if (!vpat) {
+ memprintf(errmsg, "unexpected error, undefined header value pattern");
+ goto error;
+ }
+ else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG) {
+ chk->expect.hdr.value_re = regex_comp(vpat, 1, 0, errmsg);
+ if (!chk->expect.hdr.value_re)
+ goto error;
+ }
+ else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
+ px->conf.args.ctx = ARGC_SRV;
+ LIST_INIT(&chk->expect.hdr.value_fmt);
+ if (!parse_logformat_string(vpat, px, &chk->expect.hdr.value_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
+ memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
+ goto error;
+ }
+ }
+ else {
+ chk->expect.hdr.value = ist2(strdup(vpat), strlen(vpat));
+ if (!isttest(chk->expect.hdr.value)) {
+ memprintf(errmsg, "out of memory");
+ goto error;
+ }
+ }
+
break;
case TCPCHK_EXPECT_CUSTOM:
chk->expect.custom = NULL; /* Must be defined by the caller ! */