From 27ee2927313aee25f83cf4173ccf2c4e14d58738 Mon Sep 17 00:00:00 2001 From: Christopher Faulet Date: Tue, 1 Oct 2024 18:40:40 +0200 Subject: [PATCH] MINOR: tcpcheck: Add support for an option host header value for httpchk option Support for headers and body hidden in the version for the "option httpchk" directive was removed. However a Host header is mandatory for HTTP/1.1 requests and some servers may return an error if it is not set. For now, to add it, an "http-check send" rule must be added. But it is not really handy to use an extra config line for this purpose. So now, it is possible to set the host header value, a log-format string, as extra argument to "option httpchk" directive. It must be the fourth argument: option httpchk GET / HTTP/1.1 www.srv.com While this patch is not a bug fix, it is simple enough to be backported if necessary. On 2.9 and older, lf_init_expr() does not exist and LIST_INIT() must be used instead. --- doc/configuration.txt | 6 +++++- reg-tests/checks/http-check.vtc | 4 +++- src/tcpcheck.c | 24 ++++++++++++++++++++++-- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 9972ce107..0e2836ee0 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -9858,6 +9858,7 @@ option httpchk option httpchk option httpchk option httpchk +option httpchk Enables HTTP protocol to check on the servers health May be used in the following contexts: tcp, http @@ -9879,7 +9880,10 @@ option httpchk is the optional HTTP version string. It defaults to "HTTP/1.0" but some servers might behave incorrectly in HTTP 1.0, so turning it to HTTP/1.1 may sometimes help. Note that the Host field is - mandatory in HTTP/1.1, use "http-check send" directive to add it. + mandatory in HTTP/1.1. + + is the optional HTTP Host header value. It is not set by default. + It is a log-format string. By default, server health checks only consist in trying to establish a TCP connection. When "option httpchk" is specified, a complete HTTP request is diff --git a/reg-tests/checks/http-check.vtc b/reg-tests/checks/http-check.vtc index 0cb6a167c..31ef87abb 100644 --- a/reg-tests/checks/http-check.vtc +++ b/reg-tests/checks/http-check.vtc @@ -8,6 +8,7 @@ server s1 { expect req.method == OPTIONS expect req.url == / expect req.proto == HTTP/1.0 + expect req.http.host == txresp } -start @@ -16,6 +17,7 @@ server s2 { expect req.method == GET expect req.url == /status expect req.proto == HTTP/1.1 + expect req.http.host == "www.haproxy.org" txresp } -start @@ -102,7 +104,7 @@ haproxy h1 -conf { backend be2 log ${S1_addr}:${S1_port} len 2048 local0 - option httpchk GET /status HTTP/1.1 + option httpchk GET /status HTTP/1.1 www.haproxy.org server srv ${s2_addr}:${s2_port} check inter 100ms rise 1 fall 1 backend be3 diff --git a/src/tcpcheck.c b/src/tcpcheck.c index d5bd796f3..10aad1af8 100644 --- a/src/tcpcheck.c +++ b/src/tcpcheck.c @@ -5081,7 +5081,7 @@ static struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, s { struct tcpcheck_rule *chk = NULL; struct tcpcheck_http_hdr *hdr = NULL; - char *meth = NULL, *uri = NULL, *vsn = NULL; + char *meth = NULL, *uri = NULL, *vsn = NULL, *host = NULL; char *hdrs, *body; hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL); @@ -5114,6 +5114,8 @@ static struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, s uri = args[cur_arg+1]; if (*args[cur_arg+2]) vsn = args[cur_arg+2]; + if (*args[cur_arg+3]) + host = args[cur_arg+3]; if (meth) { chk->send.http.meth.meth = find_http_meth(meth, strlen(meth)); @@ -5138,6 +5140,24 @@ static struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, s goto error; } } + if (host) { + hdr = calloc(1, sizeof(*hdr)); + if (!hdr) { + memprintf(errmsg, "out of memory"); + goto error; + } + lf_expr_init(&hdr->value); + hdr->name = istdup(ist("host")); + if (!isttest(hdr->name)) { + memprintf(errmsg, "out of memory"); + goto error; + } + + if (!parse_logformat_string(host, px, &hdr->value, 0, SMP_VAL_BE_CHK_RUL, errmsg)) + goto error; + LIST_APPEND(&chk->send.http.hdrs, &hdr->list); + hdr = NULL; + } return chk; @@ -5160,7 +5180,7 @@ int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, const if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL)) err_code |= ERR_WARN; - if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code)) + if (alertif_too_many_args_idx(4, 1, file, line, args, &err_code)) goto out; chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);