MEDIUM: http: add the ability to redefine http-err-codes and http-fail-codes

The new global keywords "http-err-codes" and "http-fail-codes" allow to
redefine which HTTP status codes indicate a client-induced error or a
server error, as tracked by stick-table counters. This is only done
globally, though everything was done so that it could easily be extended
to a per-proxy mechanism if there was a real need for this (but it would
eat quite more RAM then).

A simple reg-test was added (http-err-fail.vtc).
This commit is contained in:
Willy Tarreau 2024-01-11 12:06:49 +01:00
parent 9d827e1049
commit 4cc25f26f9
3 changed files with 216 additions and 0 deletions

View File

@ -1919,6 +1919,40 @@ hard-stop-after <time>
See also: grace
http-err-codes [+-]<range>[,...] [...]
Replace, reduce or extend the list of status codes that define an error as
considered by the termination codes and the "http_err_cnt" counter in stick
tables. The default range for errors is 400 to 499, but in certain contexts
some users prefer to exclude specific codes, especially when tracking client
errors (e.g. 404 on systems with dynamically generated contents). See also
"http-fail-codes" and "http_err_cnt".
A range specified without '+' nor '-' redefines the existing range to the new
one. A range starting with '+' extends the existing range to also include the
specified one, which may or may not overlap with the existing one. A range
starting with '-' removes the specified range from the existing one. A range
consists in a number from 100 to 599, optionally followed by "-" followed by
another number greater than or equal to the first one to indicate the high
boundary of the range. Multiple ranges may be delimited by commas for a same
add/del/ replace operation.
Example:
http-err-codes 400,402-444,446-480,490 # sets exactly these codes
http-err-codes 400-499 -450 +500 # sets 400 to 500 except 450
http-err-codes -450-459 # removes 450 to 459 from range
http-err-codes +501,505 # adds 501 and 505 to range
http-fail-codes [+-]<range>[,...] [...]
Replace, reduce or extend the list of status codes that define a failure as
considered by the termination codes and the "http_fail_cnt" counter in stick
tables. The default range for failures is 500 to 599 except 501 and 505 which
can be triggered by clients, and normally indicate a failure from the server
to process the request. Some users prefer to exclude certain codes in certain
contexts where it is known they're not relevant, such as 500 in certain SOAP
environments as it doesn't translate a server fault there. The syntax is
exactly the same as for http-err-codes above. See also "http-err-codes" and
"http_fail_cnt".
httpclient.resolvers.disabled <on|off>
Disable the DNS resolution of the httpclient. Prevent the creation of the
"default" resolvers section.

View File

@ -0,0 +1,84 @@
varnishtest "test for http-err-codes/http-fail-codes redefinitions"
feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(3.0-dev1)'"
feature ignore_unknown_macro
server s2 {
rxreq
txresp -status 220
} -start
server s3 {
rxreq
txresp -status 300
} -start
server s4 {
rxreq
txresp -status 400
} -start
server s5 {
rxreq
txresp -status 555
} -start
haproxy h1 -conf {
global
http-err-codes 220 +300-499 -300-399 # only 220, 400-499 remain
http-fail-codes -550-580 +555,599,556-566
defaults
mode http
timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
option socket-stats
frontend fe
bind "fd@${fe}"
http-request track-sc0 path
http-after-response add-header x-table err=%[sc0_http_err_cnt],fail=%[sc0_http_fail_cnt]
stick-table type string size 100 store http_err_cnt,http_fail_cnt
default_backend be
backend be
use-server s2 if { path -m beg /2 }
use-server s3 if { path -m beg /3 }
use-server s4 if { path -m beg /4 }
use-server s5 if { path -m beg /5 }
server s2 ${s2_addr}:${s2_port}
server s3 ${s3_addr}:${s3_port}
server s4 ${s4_addr}:${s4_port}
server s5 ${s5_addr}:${s5_port}
} -start
client c2 -connect ${h1_fe_sock} {
txreq -url "/2"
rxresp
expect resp.status == 220
expect resp.http.x-table ~ "err=1,fail=0"
} -run
client c3 -connect ${h1_fe_sock} {
txreq -url "/3"
rxresp
expect resp.status == 300
expect resp.http.x-table ~ "err=0,fail=0"
} -run
client c4 -connect ${h1_fe_sock} {
txreq -url "/4"
rxresp
expect resp.status == 400
expect resp.http.x-table ~ "err=1,fail=0"
} -run
client c5 -connect ${h1_fe_sock} {
txreq -url "/5"
rxresp
expect resp.status == 555
expect resp.http.x-table ~ "err=0,fail=1"
} -run

View File

@ -12,6 +12,7 @@
#include <ctype.h>
#include <haproxy/api.h>
#include <haproxy/cfgparse.h>
#include <haproxy/http.h>
#include <haproxy/tools.h>
@ -1483,3 +1484,100 @@ static void _http_init()
http_status_del_range(http_fail_status_codes, 505, 505);
}
INITCALL0(STG_INIT, _http_init);
/*
* registered keywords below
*/
/* parses a global "http-err-codes" and "http-fail-codes" directive. */
static int http_parse_http_err_fail_codes(char **args, int section_type, struct proxy *curpx,
const struct proxy *defpx, const char *file, int line,
char **err)
{
const char *cmd = args[0];
const char *p, *b, *e;
int op, low, high;
long *bitfield;
int ret = -1;
if (strcmp(cmd, "http-err-codes") == 0)
bitfield = http_err_status_codes;
else if (strcmp(cmd, "http-fail-codes") == 0)
bitfield = http_fail_status_codes;
else
ABORT_NOW();
if (!*args[1]) {
memprintf(err, "Missing status codes range for '%s'.", cmd);
goto end;
}
/* operation: <0 = remove, 0 = replace, >0 = add. The operation is only
* reset for each new arg so that we can do +200,300,400 without
* changing the operation.
*/
for (; *(p = *(++args)); ) {
switch (*p) {
case '+': op = 1; p++; break;
case '-': op = -1; p++; break;
default: op = 0; break;
}
if (!*p)
goto inval;
while (1) {
b = p;
e = p + strlen(p);
low = read_uint(&p, e);
if (b == e || p == b)
goto inval;
high = low;
if (*p == '-') {
p++;
b = p;
high = read_uint(&p, e);
if (b == e || p == b || (*p && *p != ','))
goto inval;
}
else if (*p && *p != ',')
goto inval;
if (high < low || low < 100 || high > 599) {
memprintf(err, "Invalid status codes range '%s' in '%s'.\n"
" Codes must be between 100 and 599 and ranges in ascending order.",
*args, cmd);
goto end;
}
if (!op)
memset(bitfield, 0, sizeof(http_err_status_codes));
if (op >= 0)
http_status_add_range(bitfield, low, high);
if (op < 0)
http_status_del_range(bitfield, low, high);
if (!*p)
break;
/* skip ',' */
p++;
}
}
ret = 0;
end:
return ret;
inval:
memprintf(err, "Invalid status codes range '%s' in '%s' at position %lu. Ranges must be in the form [+-]{low[-{high}]}[,...].",
*args, cmd, (ulong)(p - *args));
goto end;
}
static struct cfg_kw_list cfg_kws = {{ },{
{ CFG_GLOBAL, "http-err-codes", http_parse_http_err_fail_codes },
{ CFG_GLOBAL, "http-fail-codes", http_parse_http_err_fail_codes },
{ /* END */ }
}};
INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);