]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: checks: Support list of status codes on http-check expect rules
authorChristopher Faulet <cfaulet@haproxy.com>
Fri, 24 Apr 2020 11:53:12 +0000 (13:53 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Mon, 27 Apr 2020 08:46:28 +0000 (10:46 +0200)
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

doc/configuration.txt
include/types/checks.h
src/checks.c

index 3e4be61cc35a429b6cd8a9ac62bb8e8b3fdd1ed2..6e39c89d9b920091efacba331a6aaab952369b5d 100644 (file)
@@ -4473,10 +4473,10 @@ http-check connect [default] [port <expr>] [addr <ip>] [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 <int>] [comment <msg>]
   statement is supported in a backend. If a server fails to respond or times
   out, the check obviously fails. The available matches are :
 
-    status <string> : 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 <codes> :  test the status codes found parsing <codes> 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 <regex> : 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 <int>] [comment <msg>]
 
   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
index 8d17d5123e469fad57489e62364d0e4252155747..867d2f7cef9eb5a7d2e6d3a7bfee72e19fdf0fb4 100644 (file)
@@ -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);
index d2765fe2d03fa115cd4a214a5b9484d99ae80023..47468af8fd961e941b9b7f5a21ea429c8b27c5e0 100644 (file)
@@ -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) {