-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 <size> use POST method, sending <size> bytes of body (*)
--global <str> add a string to global section
--server <opts> set server <opt> options as defined for "server"
haproxy keyword
- --traces enable the traces for all the HTTP protocols
+ --fcgi-app <str> 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 <ciphers> for TLS1.2 and below (*)
--tls-ciphersuites <ciphers> for TLS1.3 and above (*)
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
--------------
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-<param>:<val>"
+instead to override an FCGI parameter.
#include <haproxy/chunk.h>
#include <haproxy/dynbuf.h>
#include <haproxy/errors.h>
+#include <haproxy/fcgi-app.h>
+#include <haproxy/fcgi-app-t.h>
+#include <haproxy/filters.h>
#include <haproxy/http.h>
#include <haproxy/http_htx.h>
#include <haproxy/htx.h>
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;
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)
{
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]);
}
}
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) {
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) {
/* 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;
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)
{
" 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 <size> use POST method, sending <size> bytes of body (*)\n"
" --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"
- " --traces enable the traces for all the HTTP protocols\n"
+ " --fcgi-app <str> 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 <ciphers> for TLS1.2 and below (*)\n"
" --tls-ciphersuites <ciphers> for TLS1.3 and above (*)\n"
#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 <hdr_str> header with "<name>:<value>" form and
* returs it.
*/
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;
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))) {
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;
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)
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 == '-')
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 ||
#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");
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;
/* "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);
err = 0;
leave:
free_hbuf(&dbuf);
+ free_hbuf(&fbuf);
free_hbuf(&gbuf);
free_hbuf(&buf);
if (err)