From: Frederic Lecaille Date: Fri, 31 Jul 2026 09:58:58 +0000 (+0200) Subject: MINOR: haload: support HTTP status code by version X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=646153dbcd1417eb8c52909c5b4376563cca767a;p=thirdparty%2Fhaproxy.git MINOR: haload: support HTTP status code by version Add support for tracking and displaying HTTP status code distribution broken down by HTTP version (h0, h1, h2, h3) via the new -hsv option. Building upon -hs, -hsv retains the global status code distribution and appends additional columns detailed by HTTP version. Each URL configuration (struct hld_url_cfg) stores its targeted HTTP version in ->http_ver. Thread statistics track status code counts per HTTP version in thrs_info.vtot_sc[][], which are then aggregated and displayed alongside global status codes in hld_summary(). Also remove deprecated --show-status-codes, which is a too long long option, in favor of -hs / -hsv. --- diff --git a/doc/haload.txt b/doc/haload.txt index de3bfc882..66dafb7c3 100644 --- a/doc/haload.txt +++ b/doc/haload.txt @@ -61,6 +61,8 @@ HALoad displays its usage when run without argument or wrong arguments: -h(0|1|2|2c|3) use h0 (hq-interop for QUIC), h1, h2, h2c or h3 (QUIC/TCP) protocols (*) -(0|1|2|2c|3) same as above (*) + -hs show HTTP status codes distribution + -hsv show HTTP status codes distribution ((global + per HTTP version) -l enable long output format; double for raw values -m maximum concurrent streams (1) -n maximum total requests (-1) @@ -78,7 +80,6 @@ HALoad displays its usage when run without argument or wrong arguments: --global add a string to global section --server set server options as defined for "server" haproxy keyword - --show-status-codes show HTTP status codes distribution --traces enable the traces for all the HTTP protocols SSL options: --tls-ciphers for TLS1.2 and below (*) diff --git a/include/haproxy/haload.h b/include/haproxy/haload.h index 028823715..69d7f1d35 100644 --- a/include/haproxy/haload.h +++ b/include/haproxy/haload.h @@ -7,6 +7,21 @@ #include #include +#define HLD_HAS_HTTP_VER_0 (1U << HLD_HTTP_VER_0) +#define HLD_HAS_HTTP_VER_1 (1U << HLD_HTTP_VER_1) +#define HLD_HAS_HTTP_VER_2 (1U << HLD_HTTP_VER_2) +#define HLD_HAS_HTTP_VER_3 (1U << HLD_HTTP_VER_3) + +enum hld_http_ver { + HLD_HTTP_VER_0, + HLD_HTTP_VER_1, + HLD_HTTP_VER_2, + HLD_HTTP_VER_3, + /* Do not add more enum below */ + HLD_HTTP_VER_MAX, +}; + + struct hld_path { char *path; struct hld_path *next; @@ -16,6 +31,7 @@ struct hld_url_cfg { int ssl; int is_quic; int h2c; + enum hld_http_ver http_ver; char *addr; char *raw_addr; // used only to set the host header value char *srv_opts; @@ -67,4 +83,5 @@ extern int arg_usr; extern int arg_thrd; extern int arg_wait; +extern unsigned int hld_ver_flags; #endif /* _HAPROXY_HALOAD_H */ diff --git a/src/haload.c b/src/haload.c index fee354507..c68eb12b6 100644 --- a/src/haload.c +++ b/src/haload.c @@ -61,6 +61,8 @@ struct hld_thr_info { uint64_t *ttfb_pct; // counts per ttfb value for percentile uint64_t *ttlb_pct; // counts per ttlb value for percentile uint64_t tot_sc[5]; // total status codes on this thread: 1xx,2xx,3xx,4xx,5xx + uint64_t vtot_sc[HLD_HTTP_VER_MAX][5]; // by version total status codes + // on this thread: 1xx,2xx,3xx,4xx,5xx struct task *rate_task; // task used when is set __attribute__((aligned(64))) union { } __pad; }; @@ -108,6 +110,7 @@ int all_usr_stop_asap; // all users must stop as soon as possible int usr_tid; int usr_cnt; // user counter incremented by main task int running_tasks; // tasks counter for the users and for the conn rate +unsigned int hld_ver_flags; // flags to identify the HTTP versions used char *hld_args[MAX_LINE_ARGS + 1]; @@ -570,15 +573,23 @@ void hld_summary(void) { int th; uint64_t cur_conn, tot_conn, tot_req, tot_err, tot_rcvd, bytes; - uint64_t tot_ttfb, tot_ttlb, tot_fbs, tot_lbs, tot_sc[5]; + uint64_t tot_ttfb, tot_ttlb, tot_fbs, tot_lbs, tot_sc[5], vtot_sc[HLD_HTTP_VER_MAX][5]; static uint64_t prev_totc, prev_totr, prev_totb; - static uint64_t prev_ttfb, prev_ttlb, prev_fbs, prev_lbs, prev_sc[5]; + static uint64_t prev_ttfb, prev_ttlb, prev_fbs, prev_lbs, prev_sc[5], + prev_vsc[HLD_HTTP_VER_MAX][5]; static struct timeval prev_date = TV_UNSET; double interval; cur_conn = tot_conn = tot_req = tot_err = tot_rcvd = 0; tot_ttfb = tot_ttlb = tot_fbs = tot_lbs = 0; - tot_sc[0] = tot_sc[1] = tot_sc[2] = tot_sc[3] = tot_sc[4] = 0; + if (arg_hscd) + tot_sc[0] = tot_sc[1] = tot_sc[2] = tot_sc[3] = tot_sc[4] = 0; + if (arg_hscd == 2) { + int v; + + for (v = HLD_HTTP_VER_0; v < HLD_HTTP_VER_MAX; v++) + vtot_sc[v][0] = vtot_sc[v][1] = vtot_sc[v][2] = vtot_sc[v][3] = vtot_sc[v][4] = 0; + } for (th = 0; th < arg_thrd; th++) { cur_conn += HA_ATOMIC_LOAD(&thrs_info[th].curconn); @@ -590,11 +601,29 @@ void hld_summary(void) tot_ttlb += HA_ATOMIC_LOAD(&thrs_info[th].tot_ttlb); tot_fbs += HA_ATOMIC_LOAD(&thrs_info[th].tot_fbs); tot_lbs += HA_ATOMIC_LOAD(&thrs_info[th].tot_lbs); - tot_sc[0]+= HA_ATOMIC_LOAD(&thrs_info[th].tot_sc[0]); - tot_sc[1]+= HA_ATOMIC_LOAD(&thrs_info[th].tot_sc[1]); - tot_sc[2]+= HA_ATOMIC_LOAD(&thrs_info[th].tot_sc[2]); - tot_sc[3]+= HA_ATOMIC_LOAD(&thrs_info[th].tot_sc[3]); - tot_sc[4]+= HA_ATOMIC_LOAD(&thrs_info[th].tot_sc[4]); + if (arg_hscd) { + tot_sc[0]+= HA_ATOMIC_LOAD(&thrs_info[th].tot_sc[0]); + tot_sc[1]+= HA_ATOMIC_LOAD(&thrs_info[th].tot_sc[1]); + tot_sc[2]+= HA_ATOMIC_LOAD(&thrs_info[th].tot_sc[2]); + tot_sc[3]+= HA_ATOMIC_LOAD(&thrs_info[th].tot_sc[3]); + tot_sc[4]+= HA_ATOMIC_LOAD(&thrs_info[th].tot_sc[4]); + } + if (arg_hscd == 2) { + int v; + + for (v = HLD_HTTP_VER_0; v < HLD_HTTP_VER_MAX; v++) { + if (!(hld_ver_flags & (1U << v))) + continue; + + vtot_sc[v][0]+= HA_ATOMIC_LOAD(&thrs_info[th].vtot_sc[v][0]); + vtot_sc[v][1]+= HA_ATOMIC_LOAD(&thrs_info[th].vtot_sc[v][1]); + vtot_sc[v][2]+= HA_ATOMIC_LOAD(&thrs_info[th].vtot_sc[v][2]); + vtot_sc[v][3]+= HA_ATOMIC_LOAD(&thrs_info[th].vtot_sc[v][3]); + vtot_sc[v][4]+= HA_ATOMIC_LOAD(&thrs_info[th].vtot_sc[v][4]); + } + } + + } if (tv_isset(&prev_date)) @@ -658,12 +687,28 @@ void hld_summary(void) /* status codes distribution */ if (arg_hscd) - printf("%3llu %3llu %3llu %3llu %3llu ", + printf("%3llu %3llu %3llu %3llu %3llu", (unsigned long long)(tot_sc[0] - prev_sc[0]), (unsigned long long)(tot_sc[1] - prev_sc[1]), (unsigned long long)(tot_sc[2] - prev_sc[2]), (unsigned long long)(tot_sc[3] - prev_sc[3]), (unsigned long long)(tot_sc[4] - prev_sc[4])); + if (arg_hscd == 2) { + int v; + + for (v = HLD_HTTP_VER_0; v < HLD_HTTP_VER_MAX; v++) { + if (!(hld_ver_flags & (1U << v))) + continue; + + printf(" %3llu %3llu %3llu %3llu %3llu", + (unsigned long long)(vtot_sc[v][0] - prev_vsc[v][0]), + (unsigned long long)(vtot_sc[v][1] - prev_vsc[v][1]), + (unsigned long long)(vtot_sc[v][2] - prev_vsc[v][2]), + (unsigned long long)(vtot_sc[v][3] - prev_vsc[v][3]), + (unsigned long long)(vtot_sc[v][4] - prev_vsc[v][4])); + } + + } putchar('\n'); fflush(stdout); @@ -675,11 +720,24 @@ void hld_summary(void) prev_lbs = tot_lbs; prev_ttfb = tot_ttfb; prev_ttlb = tot_ttlb; - prev_sc[0]= tot_sc[0]; - prev_sc[1]= tot_sc[1]; - prev_sc[2]= tot_sc[2]; - prev_sc[3]= tot_sc[3]; - prev_sc[4]= tot_sc[4]; + if (arg_hscd) { + prev_sc[0]= tot_sc[0]; + prev_sc[1]= tot_sc[1]; + prev_sc[2]= tot_sc[2]; + prev_sc[3]= tot_sc[3]; + prev_sc[4]= tot_sc[4]; + } + if (arg_hscd == 2) { + int v; + + for (v = HLD_HTTP_VER_0; v < HLD_HTTP_VER_MAX; v++) { + prev_vsc[v][0] = vtot_sc[v][0]; + prev_vsc[v][1] = vtot_sc[v][1]; + prev_vsc[v][2] = vtot_sc[v][2]; + prev_vsc[v][3] = vtot_sc[v][3]; + prev_vsc[v][4] = vtot_sc[v][4]; + } + } prev_date = hld_now; } @@ -768,6 +826,17 @@ static struct task *mtask_cb(struct task *t, void *context, unsigned int state) " err cps rps bps ttfb"); if (arg_hscd) printf(" 1xx 2xx 3xx 4xx 5xx"); + if (arg_hscd == 2) { + int i; + + for (i = 0 ; i < 4; i++) { + if (!(hld_ver_flags & (1 << i))) + continue; + printf(" (h%d)1xx 2xx 3xx 4xx 5xx", i); + } + } + + putchar('\n'); } @@ -1011,7 +1080,11 @@ static void hldstream_htx_buf_rcv(struct connection *conn, TRACE_PRINTF(TRACE_LEVEL_PROTO, HLD_STRM_EV_RX, hs, 0, 0, 0, "HTTP status: %d cur_read=%d", status, (int)cur_read); - thrs_info[tid].tot_sc[status * 41 / 4096 - 1]++; + if (arg_hscd) + thrs_info[tid].tot_sc[status * 41 / 4096 - 1]++; + if (arg_hscd == 2) { + thrs_info[tid].vtot_sc[hs->url->cfg->http_ver][status * 41 / 4096 - 1]++; + } if (hs->url->tot_req > 1 || !arg_accu) { ttfb = tv_us(tv_diff(&hs->req_date, &date)); thrs_info[tid].tot_fbs++; @@ -1451,6 +1524,8 @@ static inline void hld_usr_release(struct hld_usr **usr) task_destroy((*usr)->task); session_free((*usr)->sess); ha_free(usr); + + TRACE_LEAVE(HLD_EV_USR_TASK); } static struct task *hld_usr_task(struct task *t, void *context, unsigned int state) diff --git a/src/haload_init.c b/src/haload_init.c index 172d2c033..94db6f4af 100644 --- a/src/haload_init.c +++ b/src/haload_init.c @@ -23,6 +23,8 @@ static void hld_usage(char *name, int argc) " -e stop upon first connection error\n" " -h(0|1|2|2c|3) use h0 (hq-interop for QUIC), h1, h2, h2c or h3 (QUIC/TCP) protocols (*)\n" " -(0|1|2|2c|3) same as above (*)\n" + " -hs show HTTP status codes distribution\n" + " -hsv show HTTP status codes distribution ((global + per HTTP version)\n" " -l enable long output format; double for raw values\n" " -m maximum concurrent streams (1)\n" " -n maximum total requests (-1)\n" @@ -41,7 +43,6 @@ static void hld_usage(char *name, int argc) " --defaults add a string to default section\n" " --global add a string to global section\n" " --server set server options as defined for \"server\" haproxy keyword\n" - " --show-status-codes show HTTP status codes distribution\n" " --traces enable the traces for all the HTTP protocols\n" "SSL options:\n" " --tls-ciphers for TLS1.2 and below (*)\n" @@ -182,7 +183,8 @@ int hld_url_cfg_path_exist(struct hld_url_cfg *u, const char *path) * Return the URL if succeeded, NULL if not. */ static struct hld_url_cfg *hld_alloc_url(char *url, int is_quic, - char *alpn_param, int h2c_param) + char *alpn_param, int h2c_param, + enum hld_http_ver http_ver) { int ssl = 0; char *addr = NULL, *raw_addr = NULL, *path = NULL; @@ -288,6 +290,7 @@ static struct hld_url_cfg *hld_alloc_url(char *url, int is_quic, hld_url_cfg->ssl = ssl; hld_url_cfg->is_quic = is_quic; hld_url_cfg->h2c = h2c_param; + hld_url_cfg->http_ver = http_ver; hld_url_cfg->addr = addr; hld_url_cfg->raw_addr = raw_addr; if (alpn_param) { @@ -395,6 +398,7 @@ void haproxy_init_args(int argc, char **argv) struct hbuf gbuf = HBUF_NULL; // "global" section struct hbuf tbuf = HBUF_NULL; // "traces" section struct hbuf dbuf = HBUF_NULL; // "default" section + int http_ver = HLD_HTTP_VER_1; if (argc <= 1) hld_usage(progname, argc); @@ -422,6 +426,7 @@ void haproxy_init_args(int argc, char **argv) argc--; argv++; while (argc > 0) { + if (**argv == '-') { char *opt = *argv + 1; @@ -460,9 +465,6 @@ void haproxy_init_args(int argc, char **argv) free(srv_opts); srv_opts = strdup(opt); } - else if (strcmp(opt, "show-status-codes") == 0) { - arg_hscd = 1; - } else if (strcmp(opt, "tls-ciphers") == 0) { argv++, argc--; if ((argc <= 0 || **argv == '-')) @@ -497,27 +499,32 @@ void haproxy_init_args(int argc, char **argv) strcmp(opt, "h0") == 0) { alpn = "hq-interop"; h2c = 0; + http_ver = HLD_HTTP_VER_0; } else if (strcmp(opt, "1") == 0 || strcmp(opt, "h1") == 0) { alpn = "http/1.1"; h2c = 0; + http_ver = HLD_HTTP_VER_1; } else if (strcmp(opt, "2") == 0 || strcmp(opt, "h2") == 0) { alpn = "h2"; h2c = 0; + http_ver = HLD_HTTP_VER_2; } else if (strcmp(opt, "2c") == 0 || strcmp(opt, "h2c") == 0) { alpn = NULL; h2c = 1; + http_ver = HLD_HTTP_VER_2; } else if (strcmp(opt, "3") == 0 || strcmp(opt, "h3") == 0) { #if defined(USE_QUIC) alpn = "h3"; h2c = 0; + http_ver = HLD_HTTP_VER_3; #else ha_warning("QUIC support not compiled in. Rebuild with USE_QUIC=1.\n"); goto leave; @@ -534,6 +541,12 @@ void haproxy_init_args(int argc, char **argv) arg_serr = 1; } + else if (strcmp(opt, "hs") == 0) { + arg_hscd = 1; + } + else if (strcmp(opt, "hsv") == 0) { + arg_hscd = 2; + } else if (*opt == 'l') { arg_long++; while (*++opt && *opt == 'l') @@ -638,15 +651,19 @@ void haproxy_init_args(int argc, char **argv) struct hld_url_cfg *url; is_quic = strncmp(*argv, "quic://", 7) == 0; + if (is_quic) + http_ver = HLD_HTTP_VER_3; + hld_ver_flags |= (1 << http_ver); url = hld_alloc_url(*argv, is_quic, is_quic ? "h3" : alpn, - is_quic ? 0 : h2c); + is_quic ? 0 : h2c, http_ver); if (!url) { ha_alert("could not parse a new URL\n"); goto leave; } - + /* default version flag value */ + http_ver = HLD_HTTP_VER_3; } argv++; argc--;