From: Christopher Faulet Date: Tue, 5 May 2020 18:23:13 +0000 (+0200) Subject: MINOR: checks: Simplify matching on HTTP headers in HTTP expect rules X-Git-Tag: v2.2-dev8~118 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b5594265d2abb74676d1b0f8e5768d87d5d6b5ec;p=thirdparty%2Fhaproxy.git MINOR: checks: Simplify matching on HTTP headers in HTTP expect rules Extra parameters on http-check expect rules, for the header matching method, to use log-format string or to match full header line have been removed. There is now separate matching methods to match a full header line or to match each comma-separated values. "http-check expect fhdr" must be used in the first case, and "http-check expect hdr" in the second one. In addition, to match log-format header name or value, "-lf" suffix must be added to "name" or "value" keyword. For intance: http-check expect hdr name "set-cookie" value-lf -m beg "sessid=%[var(check.cookie)]" Thanks to this changes, each parameter may only be interpreted in one way. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index b183b98e41..80aa83fc7e 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -4560,8 +4560,8 @@ http-check expect [min-recv ] [comment ] log-format string. is a keyword indicating how to look for a specific pattern in the - response. The keyword may be one of "status", "rstatus", - "string", or "rstring". The keyword may be preceded by an + response. The keyword may be one of "status", "rstatus", "hdr", + "fhdr", "string", or "rstring". The keyword may be preceded by an exclamation mark ("!") to negate the match. Spaces are allowed between the exclamation mark and the keyword. See below for more details on the supported keywords. @@ -4592,8 +4592,8 @@ 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] ] : + hdr { name | name-lf } [ -m ] + [ { value | value-lf } [ -m ] : 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 @@ -4602,14 +4602,20 @@ http-check expect [min-recv ] [comment ] 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. + method is used. If the "name-lf" parameter is used, + is evaluated as a log-format string. If "value-lf" + parameter is used, is evaluated as a log-format + string. These parameters cannot be used with the regex + matching method. Finally, the header value is considered + as comma-separated list. Note that matchings are case + insensitive on the header names. + + fhdr { name | name-lf } [ -m ] + [ { value | value-lf } [ -m ] : + test the specified full header pattern on the HTTP + response headers. It does exactly the same than "hdr" + keyword, except the full header value is tested, commas + are not considered as delimiters. string : test the exact string match in the HTTP response body. A health check response will be considered valid if the diff --git a/reg-tests/checks/http-check-expect.vtc b/reg-tests/checks/http-check-expect.vtc index 9cbe2bd7be..ab629db8e5 100644 --- a/reg-tests/checks/http-check-expect.vtc +++ b/reg-tests/checks/http-check-expect.vtc @@ -39,26 +39,26 @@ haproxy h1 -conf { 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 expect hdr name "x-test1" + http-check expect hdr name -m str "X-Test2" + http-check expect hdr name -m beg "X-Begin-" + http-check expect hdr name -m end "-End" + http-check expect hdr name -m sub "-Sub-" + http-check expect hdr name -m reg "^[a-z]+-Reg-[a-z]+[0-9]\$" http-check set-var(check.hdr_name) res.fhdr(x-hdr-name) - http-check expect header name -m str "%[var(check.hdr_name)]" log-format - http-check expect header name -m str "%[res.fhdr(x-hdr-name)]" log-format + http-check expect hdr name-lf -m str "%[var(check.hdr_name)]" + http-check expect hdr name-lf -m str "%[res.fhdr(x-hdr-name)]" - 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 expect fhdr name "x-test1" value "true, next value" + http-check expect hdr name "x-test2" value -m str "true" + http-check expect hdr name -m beg "x-test" value -m beg "begin-" + http-check expect hdr name -m beg "x-test" value -m end "-end" + http-check expect hdr name -m beg "x-test" value -m sub "-sub-" + http-check expect hdr name -m beg "x-test" value -m reg "^value-reg-[A-Z0-9]+\$" + http-check expect fhdr name -m beg "x-test" value -m reg "value-reg-[A-Z0-9]+" 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 "%[res.fhdr(x-hdr-name)]" log-format full + http-check expect hdr name -m beg "x-" value-lf -m str "%[var(check.hdr_value)]" + http-check expect fhdr name -m beg "x-" value-lf -m str "%[res.fhdr(x-hdr-name)]" server srv ${s1_addr}:${s1_port} check inter 100ms rise 1 fall 1 } -start diff --git a/src/checks.c b/src/checks.c index 172cdd6082..d5306defe0 100644 --- a/src/checks.c +++ b/src/checks.c @@ -4319,7 +4319,7 @@ 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) { + else if (strcmp(args[cur_arg], "hdr") == 0 || strcmp(args[cur_arg], "fhdr") == 0) { int orig_arg = cur_arg; if (proto != TCPCHK_RULES_HTTP_CHK) @@ -4330,12 +4330,20 @@ static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, str } type = TCPCHK_EXPECT_HTTP_HEADER; + if (strcmp(args[cur_arg], "fhdr") == 0) + flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FULL; + /* 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", + if (!*(args[cur_arg+1]) || !*(args[cur_arg+2]) || + (strcmp(args[cur_arg+1], "name") != 0 && strcmp(args[cur_arg+1], "name-lf") != 0)) { + memprintf(errmsg, "'%s' expects at the name keyword as first argument followed by a pattern", args[orig_arg]); goto error; } + + if (strcmp(args[cur_arg+1], "name-lf") == 0) + flags |= TCPCHK_EXPT_FL_HTTP_HNAME_FMT; + cur_arg += 2; if (strcmp(args[cur_arg], "-m") == 0) { if (!*(args[cur_arg+1])) { @@ -4351,8 +4359,14 @@ static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, str 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) + else if (strcmp(args[cur_arg+1], "reg") == 0) { + if (flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) { + memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method", + args[orig_arg]); + goto error; + } 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]); @@ -4364,29 +4378,17 @@ static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, str 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) { + if (!*(args[cur_arg+1]) || + (strcmp(args[cur_arg+1], "value") != 0 && strcmp(args[cur_arg+1], "value-lf") != 0)) { flags |= TCPCHK_EXPT_FL_HTTP_HVAL_NONE; goto next; } + if (strcmp(args[cur_arg+1], "value-lf") == 0) + flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FMT; /* Parse the value pattern, optionnal */ - cur_arg += 2; - if (strcmp(args[cur_arg], "-m") == 0) { + if (strcmp(args[cur_arg+2], "-m") == 0) { + cur_arg += 2; 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]); @@ -4400,34 +4402,29 @@ static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, str 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) + else if (strcmp(args[cur_arg+1], "reg") == 0) { + if (flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) { + memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method", + args[orig_arg]); + goto error; + } 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++; + if (!*(args[cur_arg+2])) { + memprintf(errmsg, "'%s' expect a pattern with the value keyword", args[orig_arg]); + goto error; } + vpat = args[cur_arg+2]; + cur_arg += 2; } else if (strcmp(args[cur_arg], "comment") == 0) { if (in_pattern) { @@ -4574,7 +4571,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', '[!]string-lf', '[!]status', " - "'[!]rstatus', [!]header or comment but got '%s' as argument.", args[cur_arg]); + "'[!]rstatus', [!]hdr, [!]fhdr or comment but got '%s' as argument.", args[cur_arg]); } else { bad_tcp_kw: