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.
This commit is contained in:
Christopher Faulet 2020-05-05 10:47:36 +02:00
parent cb436f0c29
commit 3970819a55
4 changed files with 429 additions and 16 deletions

View File

@ -4565,9 +4565,10 @@ http-check expect [min-recv <int>] [comment <msg>]
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 @@ http-check expect [min-recv <int>] [comment <msg>]
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 @@ http-check expect [min-recv <int>] [comment <msg>]
# 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

View File

@ -248,17 +248,34 @@ enum tcpcheck_expect_type {
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 tcpcheck_expect {
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);

View File

@ -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

View File

@ -606,6 +606,9 @@ static void chk_report_conn_err(struct check *check, int errno_bck, int expired)
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 @@ static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool)
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 @@ static void tcpcheck_expect_onerror_message(struct buffer *msg, struct check *ch
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 @@ static enum tcpcheck_eval_ret tcpcheck_eval_expect_http(struct check *check, str
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 @@ static enum tcpcheck_eval_ret tcpcheck_eval_expect_http(struct check *check, str
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 @@ static enum tcpcheck_eval_ret tcpcheck_eval_expect_http(struct check *check, str
goto error;
out:
free_trash_chunk(nbuf);
free_trash_chunk(vbuf);
free_trash_chunk(msg);
return ret;
@ -3969,15 +4095,16 @@ static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, str
{
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 @@ static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, str
}
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 @@ static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, str
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 @@ static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, str
}
goto error;
}
next:
cur_arg++;
}
@ -4244,7 +4481,7 @@ static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, str
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;
@ -4329,6 +4566,63 @@ static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, str
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 ! */
break;