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;