From: Christopher Faulet Date: Fri, 24 Apr 2020 11:53:12 +0000 (+0200) Subject: MINOR: checks: Support list of status codes on http-check expect rules X-Git-Tag: v2.2-dev7~78 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8021a5f4a594497f0cbf6e232e24354271027aeb;p=thirdparty%2Fhaproxy.git MINOR: checks: Support list of status codes on http-check expect rules It is now possible to match on a comma-separated list of status codes or range of codes. In addtion, instead of a string comparison to match the response's status code, a integer comparison is performed. Here is an example: http-check expect status 200,201,300-310 --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 3e4be61cc3..6e39c89d9b 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -4473,10 +4473,10 @@ http-check connect [default] [port ] [addr ] [send-proxy] http-check connect http-check send GET / HTTP/1.1 hdr host haproxy.1wt.eu - http-check expect rstatus "^[23][0-9]{2}" + http-check expect status 200-399 http-check connect port 443 ssl sni haproxy.1wt.eu http-check send GET / HTTP/1.1 hdr host haproxy.1wt.eu - http-check expect rstatus "^[23][0-9]{2}" + http-check expect status 200-399 server www 10.0.0.1 check port 80 @@ -4575,11 +4575,13 @@ http-check expect [min-recv ] [comment ] statement is supported in a backend. If a server fails to respond or times out, the check obviously fails. The available matches are : - status : test the exact string match for the HTTP status code. - A health check response will be considered valid if the - response's status code is exactly this string. If the - "status" keyword is prefixed with "!", then the response - will be considered invalid if the status code matches. + status : test the status codes found parsing string. it + must be a comma-separated list of status codes or range + codes. A health check response will be considered as + valid if the response's status code matches any status + code or is inside any range of the list. If the "status" + keyword is prefixed with "!", then the response will be + considered invalid if the status code matches. rstatus : test a regular expression for the HTTP status code. A health check response will be considered valid if the @@ -4627,7 +4629,7 @@ http-check expect [min-recv ] [comment ] Examples : # only accept status 200 as valid - http-check expect status 200 + http-check expect status 200,201,300-310 # consider SQL errors as errors http-check expect ! string SQL\ Error diff --git a/include/types/checks.h b/include/types/checks.h index 8d17d5123e..867d2f7cef 100644 --- a/include/types/checks.h +++ b/include/types/checks.h @@ -200,6 +200,11 @@ struct tcpcheck_http_hdr { struct list list; /* header chained list */ }; +struct tcpcheck_codes { + unsigned int (*codes)[2]; /* an array of roange of codes: [0]=min [1]=max */ + size_t num; /* number of entry in the array */ +}; + #define TCPCHK_SND_HTTP_FL_URI_FMT 0x0001 /* Use a log-format string for the uri */ #define TCPCHK_SND_HTTP_FL_BODY_FMT 0x0002 /* Use a log-format string for the body */ #define TCPCHK_SND_HTTP_FROM_OPT 0x0004 /* Send rule coming from "option httpck" directive */ @@ -252,8 +257,9 @@ struct tcpcheck_expect { enum tcpcheck_expect_type type; /* Type of pattern used for matching. */ unsigned int flags; /* TCPCHK_EXPT_FL_* */ union { - struct ist data; /* Matching a literal string / binary anywhere in the response. */ - struct my_regex *regex; /* Matching a regex pattern. */ + 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 */ /* custom function to eval epxect rule */ enum tcpcheck_eval_ret (*custom)(struct check *, struct tcpcheck_rule *, int); diff --git a/src/checks.c b/src/checks.c index d2765fe2d0..47468af8fd 100644 --- a/src/checks.c +++ b/src/checks.c @@ -601,7 +601,7 @@ static void chk_report_conn_err(struct check *check, int errno_bck, int expired) chunk_appendf(chk, " (expect binary regex)"); break; case TCPCHK_EXPECT_HTTP_STATUS: - chunk_appendf(chk, " (expect HTTP status '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data)); + chunk_appendf(chk, " (expect HTTP status codes)"); break; case TCPCHK_EXPECT_HTTP_REGEX_STATUS: chunk_appendf(chk, " (expect HTTP status regex)"); @@ -781,9 +781,11 @@ static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool) free_tcpcheck_fmt(&rule->expect.onsuccess_fmt); release_sample_expr(rule->expect.status_expr); switch (rule->expect.type) { + case TCPCHK_EXPECT_HTTP_STATUS: + free(rule->expect.codes.codes); + break; case TCPCHK_EXPECT_STRING: case TCPCHK_EXPECT_BINARY: - case TCPCHK_EXPECT_HTTP_STATUS: case TCPCHK_EXPECT_HTTP_BODY: istfree(&rule->expect.data); break; @@ -1044,8 +1046,10 @@ static void tcpcheck_expect_onerror_message(struct buffer *msg, struct check *ch chunk_strcat(msg, (match ? "TCPCHK matched unwanted content" : "TCPCHK did not match content")); switch (rule->expect.type) { - case TCPCHK_EXPECT_STRING: case TCPCHK_EXPECT_HTTP_STATUS: + chunk_appendf(msg, "(status codes) at step %d", tcpcheck_get_step_id(check, rule)); + break; + case TCPCHK_EXPECT_STRING: case TCPCHK_EXPECT_HTTP_BODY: chunk_appendf(msg, " '%.*s' at step %d", (unsigned int)istlen(rule->expect.data), istptr(rule->expect.data), tcpcheck_get_step_id(check, rule)); @@ -2093,7 +2097,7 @@ static enum tcpcheck_eval_ret tcpcheck_eval_expect_http(struct check *check, str struct buffer *msg = NULL; enum healthcheck_status status; struct ist desc = IST_NULL; - int match, inverse; + int i, match, inverse; last_read |= (!htx_free_space(htx) || (htx_get_tail_type(htx) == HTX_BLK_EOM)); @@ -2127,7 +2131,14 @@ static enum tcpcheck_eval_ret tcpcheck_eval_expect_http(struct check *check, str switch (expect->type) { case TCPCHK_EXPECT_HTTP_STATUS: - match = isteq(htx_sl_res_code(sl), expect->data); + match = 0; + for (i = 0; i < expect->codes.num; i++) { + if (sl->info.res.status >= expect->codes.codes[i][0] && + sl->info.res.status <= expect->codes.codes[i][1]) { + match = 1; + break; + } + } /* Set status and description in case of error */ status = HCHK_STATUS_L7STS; @@ -3918,7 +3929,7 @@ 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 *str, *on_success_msg, *on_error_msg, *comment, *pattern; + char *on_success_msg, *on_error_msg, *comment, *pattern; enum tcpcheck_expect_type type = TCPCHK_EXPECT_UNDEF; enum healthcheck_status ok_st = HCHK_STATUS_L7OKD; enum healthcheck_status err_st = HCHK_STATUS_L7RSP; @@ -3926,7 +3937,7 @@ static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, str long min_recv = -1; int inverse = 0; - str = on_success_msg = on_error_msg = comment = pattern = NULL; + on_success_msg = on_error_msg = comment = pattern = NULL; if (!*(args[cur_arg+1])) { memprintf(errmsg, "expects at least a matching pattern as arguments"); goto error; @@ -4229,8 +4240,44 @@ static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, str } switch (chk->expect.type) { + case TCPCHK_EXPECT_HTTP_STATUS: { + const char *p = pattern; + unsigned int c1,c2; + + chk->expect.codes.codes = NULL; + chk->expect.codes.num = 0; + while (1) { + c1 = c2 = read_uint(&p, pattern + strlen(pattern)); + if (*p == '-') { + p++; + c2 = read_uint(&p, pattern + strlen(pattern)); + } + if (c1 > c2) { + memprintf(errmsg, "invalid range of status codes '%s'", pattern); + goto error; + } + + chk->expect.codes.num++; + chk->expect.codes.codes = my_realloc2(chk->expect.codes.codes, + chk->expect.codes.num * sizeof(*chk->expect.codes.codes)); + if (!chk->expect.codes.codes) { + memprintf(errmsg, "out of memory"); + goto error; + } + chk->expect.codes.codes[chk->expect.codes.num-1][0] = c1; + chk->expect.codes.codes[chk->expect.codes.num-1][1] = c2; + + if (*p == '\0') + break; + if (*p != ',') { + memprintf(errmsg, "invalid character '%c' in the list of status codes", *p); + goto error; + } + p++; + } + break; + } case TCPCHK_EXPECT_STRING: - case TCPCHK_EXPECT_HTTP_STATUS: case TCPCHK_EXPECT_HTTP_BODY: chk->expect.data = ist2(strdup(pattern), strlen(pattern)); if (!isttest(chk->expect.data)) { @@ -4277,7 +4324,6 @@ static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, str error: free_tcpcheck(chk, 0); - free(str); free(comment); free(on_success_msg); free(on_error_msg); @@ -4917,7 +4963,7 @@ static int check_proxy_tcpcheck(struct proxy *px) */ chk = get_last_tcpcheck_rule(&px->tcpcheck_rules); if (chk && chk->action == TCPCHK_ACT_SEND) { - next = parse_tcpcheck_expect((char *[]){"http-check", "expect", "rstatus", "^[23]", ""}, + next = parse_tcpcheck_expect((char *[]){"http-check", "expect", "status", "200-399", ""}, 1, px, px->tcpcheck_rules.list, TCPCHK_RULES_HTTP_CHK, px->conf.file, px->conf.line, &errmsg); if (!next) {