From 3970819a550a78eb8e88c47fdf0516f9777a91fd Mon Sep 17 00:00:00 2001 From: Christopher Faulet Date: Tue, 5 May 2020 10:47:36 +0200 Subject: [PATCH] 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 ] [log-format] \ [ value [ -m ] [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. 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 ( or ) 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. --- doc/configuration.txt | 29 ++- include/types/checks.h | 42 +++- reg-tests/checks/http-check-expect.vtc | 66 ++++++ src/checks.c | 308 ++++++++++++++++++++++++- 4 files changed, 429 insertions(+), 16 deletions(-) create mode 100644 reg-tests/checks/http-check-expect.vtc diff --git a/doc/configuration.txt b/doc/configuration.txt index 87cc69163..1092fd5e2 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -4565,9 +4565,10 @@ http-check expect [min-recv ] [comment ] between the exclamation mark and the keyword. See below for more details on the supported keywords. - 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 ('\'). + 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 ] [comment ] will be considered invalid if the status code matches. This is mostly used to check for multiple codes. + header name [ -m ] [log-format] + [ value [ -m ] [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. 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 ( or ) 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 : 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 ] [comment ] # 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 48092859c..927dada33 100644 --- a/include/types/checks.h +++ b/include/types/checks.h @@ -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); diff --git a/reg-tests/checks/http-check-expect.vtc b/reg-tests/checks/http-check-expect.vtc new file mode 100644 index 000000000..83e4330d1 --- /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 8d371e652..a5ddc13ba 100644 --- a/src/checks.c +++ b/src/checks.c @@ -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;