From: Frederic Lecaille Date: Mon, 31 Aug 2026 09:25:16 +0000 (+0200) Subject: MINOR: haload: add FastCGI support X-Git-Tag: v3.5-dev6~28 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=29c70e199f69122f4eba49ece50c06ccaac1b7b5;p=haproxy.git MINOR: haload: add FastCGI support Add -fcgi (protocol selector) and --fcgi-app (raw passthrough into a generated "fcgi-app haload" section, mainly for "docroot"). The FCGI filter is attached from hld_cfg_finalize() (post-check), not hld_pre_check(): servers don't exist yet at pre-check time, and HAProxy's own postparser then wrongly reports no FCGI server found. "set-param"/"pass-header" have no effect (no struct stream to run them); use -H ":fcgi-:" instead. --- diff --git a/doc/haload.txt b/doc/haload.txt index e875cf6a7..98b36e721 100644 --- a/doc/haload.txt +++ b/doc/haload.txt @@ -65,6 +65,7 @@ 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 (*) + -fcgi use FastCGI protocol (*) -get use GET method (default) (*) -head use HEAD method instead of GET (*) -post use POST method, sending bytes of body (*) @@ -88,7 +89,12 @@ 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 - --traces enable the traces for all the HTTP protocols + --fcgi-app add a string to the generated "fcgi-app haload" + section (docroot, index...). Note: "set-param" and + "pass-header" rules have no effect, since HALoad + does not use HAProxy streams to run them; use "-H" + to set FCGI params directly (e.g. -H ":fcgi-script_filename:...") + --traces enable the traces for all the protocols SSL options: --tls-ciphers for TLS1.2 and below (*) --tls-ciphersuites for TLS1.3 and above (*) @@ -145,6 +151,9 @@ Examples: defaults timeout connect 5s + # FastCGI target, with a docroot for the generated "fcgi-app" section + $ ./haload --fcgi-app "docroot /var/www" -fcgi -u 10 -d 30 http://127.0.0.1:9000/index.php + 4. Limitations -------------- @@ -158,3 +167,11 @@ For example, when targeting a single URL, specifying "-u 10 -m 5" might lead to opening more than 10 concurrent connections to the target server under heavy load, instead of strictly capping the total connections to the requested number of users. + +FastCGI (-fcgi) requires a "docroot" set via --fcgi-app, and only supports a +single "fcgi-app" configuration for the whole run: HAProxy allows only one +per backend, and HALoad uses a single internal backend for every URL. Also, +"set-param" and "pass-header" rules in that section have no effect, since +they are only evaluated through an HAProxy stream filter and HALoad drives +its connections without ever creating a stream; use "-H :fcgi-:" +instead to override an FCGI parameter. diff --git a/include/haproxy/haload.h b/include/haproxy/haload.h index 5983d31f7..825d64481 100644 --- a/include/haproxy/haload.h +++ b/include/haproxy/haload.h @@ -11,12 +11,14 @@ #define HLD_HAS_PROTO_H1 (1U << HLD_PROTO_H1) #define HLD_HAS_PROTO_H2 (1U << HLD_PROTO_H2) #define HLD_HAS_PROTO_H3 (1U << HLD_PROTO_H3) +#define HLD_HAS_PROTO_FCGI (1U << HLD_PROTO_FCGI) enum hld_proto { HLD_PROTO_H0, HLD_PROTO_H1, HLD_PROTO_H2, HLD_PROTO_H3, + HLD_PROTO_FCGI, /* Do not add more enum below */ HLD_PROTO_MAX, }; @@ -34,6 +36,7 @@ struct hld_url_cfg { int ssl; int is_quic; int h2c; + int fcgi; int thnk_time; enum hld_proto proto; char *addr; diff --git a/src/haload.c b/src/haload.c index 99194ab6f..8f90a5ff4 100644 --- a/src/haload.c +++ b/src/haload.c @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -33,6 +36,9 @@ static inline struct hld_usr *hld_new_usr(int nreqs, int tid); static void hld_report_percentiles(void); static void hld_dealloc_thrs_info(void); +/* not declared in any header */ +extern struct flt_ops fcgi_flt_ops; + struct hld_mtask { struct task *t; unsigned int show_time; @@ -1001,6 +1007,8 @@ static void update_err_ramp(void) HA_ATOMIC_STORE(&arg_rate, (uint64_t)err_rate_floor << step); } +static const char *hld_proto_names[HLD_PROTO_MAX] = { "h0", "h1", "h2", "h3", "fcgi" }; + /* main task */ static struct task *mtask_cb(struct task *t, void *context, unsigned int state) { @@ -1026,10 +1034,10 @@ static struct task *mtask_cb(struct task *t, void *context, unsigned int state) if (arg_hscd == 2) { int i; - for (i = 0 ; i < 4; i++) { + for (i = 0 ; i < HLD_PROTO_MAX; i++) { if (!(hld_proto_flags & (1 << i))) continue; - printf(" (h%d)1xx 2xx 3xx 4xx 5xx", i); + printf(" (%s)1xx 2xx 3xx 4xx 5xx", hld_proto_names[i]); } } @@ -2246,6 +2254,7 @@ static int hld_srv_parse_opts(char *opts, struct server *s) static int hld_cfg_finalize(void) { int ret = 0; + int fcgi_used = 0; struct hld_url_cfg *cfg; for (cfg = hld_url_cfgs; cfg; cfg = cfg->next) { @@ -2314,6 +2323,8 @@ static int hld_cfg_finalize(void) srv->mux_proto = get_mux_proto(ist("quic")); else if (cfg->h2c) srv->mux_proto = get_mux_proto(ist("h2")); + else if (cfg->fcgi) + srv->mux_proto = get_mux_proto(ist("fcgi")); } if (srv->mux_proto) { @@ -2383,6 +2394,42 @@ static int hld_cfg_finalize(void) /* Attach the server to the URL */ cfg->srv = srv; + if (cfg->fcgi) + fcgi_used = 1; + } + + if (fcgi_used) { + /* same as "use-fcgi-app haload" at config parsing time */ + struct fcgi_flt_conf *fcgi_conf; + struct flt_conf *fconf; + + fcgi_conf = calloc(1, sizeof(*fcgi_conf)); + fconf = calloc(1, sizeof(*fconf)); + if (!fcgi_conf || !fconf) { + ha_alert("could not allocate the FCGI filter config\n"); + goto leave; + } + + fcgi_conf->name = strdup("haload"); + if (!fcgi_conf->name) { + ha_alert("could not allocate the FCGI app name\n"); + goto leave; + } + + LIST_INIT(&fcgi_conf->param_rules); + LIST_INIT(&fcgi_conf->hdr_rules); + + /* same as fcgi_flt_check() does at post-parsing time */ + fcgi_conf->app = fcgi_app_find_by_name(fcgi_conf->name); + if (!fcgi_conf->app) { + ha_alert("fcgi-app 'haload' not found. Did you pass --fcgi-app with a \"docroot\"?\n"); + goto leave; + } + + fconf->id = fcgi_flt_id; + fconf->conf = fcgi_conf; + fconf->ops = &fcgi_flt_ops; + LIST_APPEND(&hld_proxy.filter_configs, &fconf->list); } ret = 1; diff --git a/src/haload_init.c b/src/haload_init.c index d0dc565e1..74dac49bb 100644 --- a/src/haload_init.c +++ b/src/haload_init.c @@ -16,6 +16,7 @@ char *srv_opts, *tls_ciphers, *tls_ciphersuites, *tls_curves, *alpn; enum http_meth_t http_meth = HTTP_METH_GET; struct ist meth_ist = IST("GET"); int h2c; +int fcgi; static void hld_usage(char *name, int argc) { @@ -30,6 +31,7 @@ static void hld_usage(char *name, int argc) " then resume the load asked for on the command line\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" + " -fcgi use FastCGI protocol (*)\n" " -get use GET method (default) (*)\n" " -head use HEAD method instead of GET (*)\n" " -post use POST method, sending bytes of body (*)\n" @@ -54,7 +56,8 @@ 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" - " --traces enable the traces for all the HTTP protocols\n" + " --fcgi-app add a string to the generated \"fcgi-app haload\" section\n" + " --traces enable the traces for all the protocols\n" "SSL options:\n" " --tls-ciphers for TLS1.2 and below (*)\n" " --tls-ciphersuites for TLS1.3 and above (*)\n" @@ -80,7 +83,8 @@ static const char *hld_cfg_traces_str = #endif "\ttrace haload sink stderr level developer start now verbosity clean\n" "\ttrace h1 sink stderr level developer start now verbosity minimal\n" - "\ttrace h2 sink stderr level developer start now verbosity minimal\n"; + "\ttrace h2 sink stderr level developer start now verbosity minimal\n" + "\ttrace fcgi sink stderr level developer start now verbosity minimal\n"; /* Allocate header with ":" form and * returs it. @@ -195,7 +199,7 @@ int hld_url_cfg_path_exist(struct hld_url_cfg *u, const char *path) */ static struct hld_url_cfg *hld_alloc_url(char *url, int is_quic, char *alpn_param, int h2c_param, - enum hld_proto proto) + int fcgi_param, enum hld_proto proto) { int ssl = 0; char *addr = NULL, *raw_addr = NULL, *path = NULL; @@ -242,7 +246,7 @@ static struct hld_url_cfg *hld_alloc_url(char *url, int is_quic, for (purl = hld_url_cfgs; purl; purl = purl->next) { if (purl->is_quic == is_quic && purl->ssl == ssl && - purl->h2c == h2c_param && + purl->h2c == h2c_param && purl->fcgi == fcgi_param && strcmp(purl->raw_addr, addr) == 0 && ((!purl->alpn && !alpn_param) || (purl->alpn && alpn_param && strcmp(purl->alpn, alpn_param) == 0))) { @@ -307,6 +311,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->fcgi = fcgi_param; hld_url_cfg->thnk_time = arg_thnk; hld_url_cfg->proto = proto; hld_url_cfg->addr = addr; @@ -416,6 +421,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 + struct hbuf fbuf = HBUF_NULL; // "fcgi-app" section int proto = HLD_PROTO_H1; if (argc <= 1) @@ -472,6 +478,22 @@ void haproxy_init_args(int argc, char **argv) hbuf_str_append(&dbuf, *argv); } + else if (strcmp(opt, "fcgi-app") == 0) { + argv++; argc--; + if (argc <= 0 || **argv == '-') + hld_usage(progname, argc); + + if (hbuf_is_null(&fbuf)) { + if (hbuf_alloc(&fbuf) == NULL) { + ha_alert("failed to allocate a buffer.\n"); + goto leave; + } + + hbuf_appendf(&fbuf, "fcgi-app haload\n"); + } + + hbuf_str_append(&fbuf, *argv); + } else if (strcmp(opt, "global") == 0) { argv++; argc--; if (argc <= 0 || **argv == '-') @@ -522,24 +544,28 @@ void haproxy_init_args(int argc, char **argv) strcmp(opt, "h0") == 0) { alpn = "hq-interop"; h2c = 0; + fcgi = 0; proto = HLD_PROTO_H0; } else if (strcmp(opt, "1") == 0 || strcmp(opt, "h1") == 0) { alpn = "http/1.1"; h2c = 0; + fcgi = 0; proto = HLD_PROTO_H1; } else if (strcmp(opt, "2") == 0 || strcmp(opt, "h2") == 0) { alpn = "h2"; h2c = 0; + fcgi = 0; proto = HLD_PROTO_H2; } else if (strcmp(opt, "2c") == 0 || strcmp(opt, "h2c") == 0) { alpn = NULL; h2c = 1; + fcgi = 0; proto = HLD_PROTO_H2; } else if (strcmp(opt, "3") == 0 || @@ -547,12 +573,19 @@ void haproxy_init_args(int argc, char **argv) #if defined(USE_QUIC) alpn = "h3"; h2c = 0; + fcgi = 0; proto = HLD_PROTO_H3; #else ha_warning("QUIC support not compiled in. Rebuild with USE_QUIC=1.\n"); goto leave; #endif } + else if (strcmp(opt, "fcgi") == 0) { + alpn = NULL; + h2c = 0; + fcgi = 1; + proto = HLD_PROTO_FCGI; + } else if (strcmp(opt, "get") == 0) { http_meth = HTTP_METH_GET; meth_ist = ist("GET"); @@ -711,7 +744,8 @@ void haproxy_init_args(int argc, char **argv) hld_proto_flags |= (1 << proto); url = hld_alloc_url(*argv, is_quic, is_quic ? "h3" : alpn, - is_quic ? 0 : h2c, proto); + is_quic ? 0 : h2c, + is_quic ? 0 : fcgi, proto); if (!url) { ha_alert("could not parse a new URL\n"); goto leave; @@ -756,6 +790,9 @@ void haproxy_init_args(int argc, char **argv) /* "default section */ if (!hbuf_is_null(&dbuf)) hbuf_appendf(&buf, "%.*s\n", (int)dbuf.data, dbuf.area); + /* "fcgi-app" section */ + if (!hbuf_is_null(&fbuf)) + hbuf_appendf(&buf, "%.*s\n", (int)fbuf.data, fbuf.area); fileless_cfg.filename = strdup("haterm cfgfile"); fileless_cfg.content = strdup(buf.area); @@ -777,6 +814,7 @@ void haproxy_init_args(int argc, char **argv) err = 0; leave: free_hbuf(&dbuf); + free_hbuf(&fbuf); free_hbuf(&gbuf); free_hbuf(&buf); if (err)