]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: haload: support HTTP status code by version master
authorFrederic Lecaille <flecaille@haproxy.com>
Fri, 31 Jul 2026 09:58:58 +0000 (11:58 +0200)
committerFrederic Lecaille <flecaille@haproxy.com>
Fri, 31 Jul 2026 10:06:12 +0000 (12:06 +0200)
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.

doc/haload.txt
include/haproxy/haload.h
src/haload.c
src/haload_init.c

index de3bfc88285ea39224433e4f7a4a4d37d5ccd4af..66dafb7c3598887b38d31566923421574516bfbe 100644 (file)
@@ -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 <streams>     maximum concurrent streams (1)
             -n <reqs>        maximum total requests (-1)
@@ -78,7 +80,6 @@ HALoad displays its usage when run without argument or wrong arguments:
             --global <str>   add a string to global section
             --server <opts>  set server <opt> 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 <ciphers>      for TLS1.2 and below (*)
index 028823715cee14c811e269b180123f7044f19845..69d7f1d3581040830ec8acdd1ea28eea19f80cad 100644 (file)
@@ -7,6 +7,21 @@
 #include <haproxy/server-t.h>
 #include <haproxy/task-t.h>
 
+#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 */
index fee35450765592c921bd7e2e8cfed67e8bf2aac5..c68eb12b6b3e12f14893cce17a7519b315900ff6 100644 (file)
@@ -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 <arg_rate> 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 <mtask> 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)
index 172d2c03349e8993743387740174d904024eeb62..94db6f4af200557f56add893f3b1af0eb828bc38 100644 (file)
@@ -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 <streams>     maximum concurrent streams (1)\n"
                "        -n <reqs>        maximum total requests (-1)\n"
@@ -41,7 +43,6 @@ static void  hld_usage(char *name, int argc)
                "        --defaults <str> add a string to default section\n"
                "        --global <str>   add a string to global section\n"
                "        --server <opts>  set server <opt> 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 <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--;