Allow agent checks to obtain information in an HTTP header of the response to an http check.
Signed-off-by: Simon Horman <ho...@verge.net.au> --- v4 * Do not duplicate code that is in process_result() v2 - v3 * No change --- doc/configuration.txt | 24 +++++++++++++-- include/types/proxy.h | 2 ++ src/cfgparse.c | 20 +++++++++++- src/checks.c | 85 ++++++++++++++++++++++++++++++++++++--------------- 4 files changed, 104 insertions(+), 27 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 9c5f5ee..233be4e 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -1139,6 +1139,7 @@ force-persist - X X X fullconn X - X X grace X X X X hash-type X - X X +http-check agent-hdr X - X X http-check disable-on-404 X - X X http-check expect - - X X http-check send-state X - X X @@ -2527,6 +2528,24 @@ hash-type <method> See also : "balance", "server" +http-check agent-hdr + Use the value of the given http header as the result of an agent check + May be used in sections : defaults | frontend | listen | backend + yes | no | yes | yes + Arguments : + + <header> The header string to use to send the server name + + When this option is set an http check is enhanced to read the value + of the given header in the response to an http check and interpret + it as the result of an agent check. In this way an http and agent check + may be performed simultaneously. + + This option is overridden by the agent-port parameter. + + See also : "option lb-agent-chk", "agent-port" + + http-check disable-on-404 Enable a maintenance mode upon HTTP/404 response to health-checks May be used in sections : defaults | frontend | listen | backend @@ -7682,10 +7701,11 @@ agent-port <port> In this the agent check is run in conjunction with another check and as such the check backend should be set to some value other than - "lb-agent-check". An alternative scenario is to run only an agent check + "lb-agent-check". A second scenario is to run only an agent check in which case the check backend should be set to "lb-agent-check" and "agent-port" should not be set; in that scenario the port may be set - using the "port" parameter. + using the "port" parameter. A third scenario is to use "http-check + agent-hdr" to run an agent and http checks simultaneously over HTTP. See also the "agent-inter" parameter. diff --git a/include/types/proxy.h b/include/types/proxy.h index e6bc755..66e5db7 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -328,6 +328,7 @@ struct proxy { int check_len; /* Length of the HTTP or SSL3 request */ char *expect_str; /* http-check expected content : string or text version of the regex */ regex_t *expect_regex; /* http-check expected content */ + const char *agent_http_header; /* http-check header to use for agent checks */ struct chunk errmsg[HTTP_ERR_SIZE]; /* default or customized error messages for known errors */ int uuid; /* universally unique proxy ID, used for SNMP */ unsigned int backlog; /* force the frontend's listen backlog */ @@ -359,6 +360,7 @@ struct proxy { } conf; /* config information */ void *parent; /* parent of the proxy when applicable */ struct comp *comp; /* http compression */ + }; struct switching_rule { diff --git a/src/cfgparse.c b/src/cfgparse.c index 780275e..6329b59 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -3869,7 +3869,25 @@ stats_error_parsing: if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL)) err_code |= ERR_WARN; - if (strcmp(args[1], "disable-on-404") == 0) { + if (strcmp(args[1], "agent-hdr") == 0) { + int cur_arg; + + if (curproxy->agent_http_header) { + Alert("parsing [%s:%d] : '%s %s' already specified.\n", file, linenum, args[0], args[1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + cur_arg = 2; + if (!*(args[cur_arg ])) { + Alert("parsing [%s:%d] : '%s %s' expects <string> as an argument.\n", + file, linenum, args[0], args[1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + curproxy->agent_http_header = strdup(args[cur_arg]); + } + else if (strcmp(args[1], "disable-on-404") == 0) { /* enable a graceful server shutdown on an HTTP 404 response */ curproxy->options |= PR_O_DISABLE404; } diff --git a/src/checks.c b/src/checks.c index 249a6ee..5d893f8 100644 --- a/src/checks.c +++ b/src/checks.c @@ -1306,6 +1306,35 @@ static struct task *server_warmup(struct task *t) return t; } +static void process_result(struct check *check) +{ + struct server *s = check->server; + + if (check->result & SRV_CHK_FAILED) { /* a failure or timeout detected */ + if (check->health > s->rise) { + check->health--; /* still good */ + s->counters.failed_checks++; + } + else + set_server_down(check); + } + else { /* check was OK */ + /* we may have to add/remove this server from the LB group */ + if ((s->state & SRV_RUNNING) && (s->proxy->options & PR_O_DISABLE404)) { + if ((s->state & SRV_GOINGDOWN) && !(check->result & SRV_CHK_DISABLE)) + set_server_enabled(s); + else if (!(s->state & SRV_GOINGDOWN) && (check->result & SRV_CHK_DISABLE)) + set_server_disabled(check); + } + + if (check->health < s->rise + s->fall - 1) { + check->health++; /* was bad, stays for a while */ + set_server_up(check); + } + } + check->state &= ~CHK_RUNNING; +} + /* * manages a server health-check. Returns * the time the task accepts to wait, or TIME_ETERNITY for infinity. @@ -1502,29 +1531,17 @@ static struct task *process_chk(struct task *t) conn_full_close(conn); } - if (check->result & SRV_CHK_FAILED) { /* a failure or timeout detected */ - if (check->health > s->rise) { - check->health--; /* still good */ - s->counters.failed_checks++; - } - else - set_server_down(check); - } - else { /* check was OK */ - /* we may have to add/remove this server from the LB group */ - if ((s->state & SRV_RUNNING) && (s->proxy->options & PR_O_DISABLE404)) { - if ((s->state & SRV_GOINGDOWN) && !(check->result & SRV_CHK_DISABLE)) - set_server_enabled(s); - else if (!(s->state & SRV_GOINGDOWN) && (check->result & SRV_CHK_DISABLE)) - set_server_disabled(check); - } + process_result(check); - if (check->health < s->rise + s->fall - 1) { - check->health++; /* was bad, stays for a while */ - set_server_up(check); - } + /* The agent check may run without a task if + * it is performed as aprt of the primary health check. + * If so process its result, here, while processing + * the primary health check. + */ + if (!check->server->agent.task && + check->server->agent.state != HCHK_STATUS_INI) { + process_result(&check->server->agent); } - check->state &= ~CHK_RUNNING; rv = 0; if (global.spread_checks > 0) { @@ -1716,9 +1733,22 @@ static int httpchk_expect(struct server *s, int done) { static char status_msg[] = "HTTP status check returned code <000>"; char status_code[] = "000"; - char *contentptr; + char *contentptr = NULL, *agent_value = NULL; int ret; + if (s->proxy->agent_http_header) { + httpchk_parse_header(s, s->proxy->agent_http_header, &agent_value); + + if (!agent_value || !strchr(agent_value, '\n')) { + /* if we don't match, we may need to wait more */ + if (!done) + return 0; + set_server_check_status(&s->agent, HCHK_STATUS_L7RSP, + "HTTP check did not match agent-hdr"); + agent_value = NULL; + } + } + switch (s->proxy->options2 & PR_O2_EXP_TYPE) { case PR_O2_EXP_STS: case PR_O2_EXP_RSTS: @@ -1739,7 +1769,8 @@ static int httpchk_expect(struct server *s, int done) case PR_O2_EXP_STR: case PR_O2_EXP_RSTR: - contentptr = httpchk_parse_header(s, NULL, NULL); + if (!contentptr) + contentptr = httpchk_parse_header(s, s->proxy->agent_http_header, &agent_value); /* Check that response contains a body... */ if (*contentptr == '\0') { @@ -1747,7 +1778,7 @@ static int httpchk_expect(struct server *s, int done) return 0; set_server_check_status(&s->check, HCHK_STATUS_L7RSP, "HTTP content check could not find a response body or an empty response body was found"); - return 1; + goto agent; } /* Check the response content against the supplied string @@ -1780,6 +1811,12 @@ static int httpchk_expect(struct server *s, int done) } break; } + +agent: + if (s->proxy->agent_http_header && agent_value) { + agent_expect(&s->agent, agent_value); + } + return 1; } -- 1.8.3.2