diff --git a/Makefile b/Makefile index 508bd75d7..da50e2625 100644 --- a/Makefile +++ b/Makefile @@ -1003,7 +1003,7 @@ OBJS += src/mux_h2.o src/mux_h1.o src/mux_fcgi.o src/log.o \ src/http_acl.o src/dict.o src/dgram.o src/pipe.o \ src/hpack-huff.o src/hpack-enc.o src/ebtree.o src/hash.o \ src/httpclient_cli.o src/version.o src/ncbmbuf.o src/ech.o \ - src/cfgparse-peers.o + src/cfgparse-peers.o src/haterm.o ifneq ($(TRACE),) OBJS += src/calltrace.o diff --git a/doc/configuration.txt b/doc/configuration.txt index 3f351c8f0..b91c886c9 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -9307,6 +9307,9 @@ mode { tcp|http|log|spop } processing and switching will be possible. This is the mode which brings HAProxy most of its value. + haterm The frontend will work in haterm HTTP benchmark mode. This is + not supported by backends. See doc/haterm.txt for details. + log When used in a backend section, it will turn the backend into a log backend. Such backend can be used as a log destination for any "log" directive by using the "backend@" syntax. Log diff --git a/include/haproxy/hstream-t.h b/include/haproxy/hstream-t.h new file mode 100644 index 000000000..e9ef52a20 --- /dev/null +++ b/include/haproxy/hstream-t.h @@ -0,0 +1,36 @@ +#ifndef _HAPROXY_HSTREAM_T_H +#define _HAPROXY_HSTREAM_T_H + +#include +#include +#include + +/* hastream stream */ +struct hstream { + enum obj_type obj_type; + struct session *sess; + + struct stconn *sc; + struct task *task; + + struct buffer req; + struct buffer res; + unsigned long long to_write; /* #of response data bytes to write after headers */ + struct buffer_wait buf_wait; /* Wait list for buffer allocation */ + + int flags; + + int ka; /* .0: keep-alive .1: forced .2: http/1.1, .3: was_reused */ + int req_cache; + unsigned long long req_size; /* values passed in the URI to override the server's */ + unsigned long long req_body; /* remaining body to be consumed from the request */ + int req_code; + int res_wait; /* time to wait before replying in ms */ + int res_time; + int req_chunked; + int req_random; + int req_after_res; /* Drain the request body after having sent the response */ + enum http_meth_t req_meth; +}; + +#endif /* _HAPROXY_HSTREAM_T_H */ diff --git a/include/haproxy/hstream.h b/include/haproxy/hstream.h new file mode 100644 index 000000000..893adff60 --- /dev/null +++ b/include/haproxy/hstream.h @@ -0,0 +1,12 @@ +#ifndef _HAPROXY_HSTREAM_H +#define _HAPROXY_HSTREAM_H + +#include +#include + +struct task *sc_hstream_io_cb(struct task *t, void *ctx, unsigned int state); +int hstream_wake(struct stconn *sc); +void hstream_shutdown(struct stconn *sc); +void *hstream_new(struct session *sess, struct stconn *sc, struct buffer *input); + +#endif /* _HAPROXY_HSTREAM_H */ diff --git a/include/haproxy/obj_type-t.h b/include/haproxy/obj_type-t.h index da2efbf86..fd232b347 100644 --- a/include/haproxy/obj_type-t.h +++ b/include/haproxy/obj_type-t.h @@ -46,6 +46,7 @@ enum obj_type { #ifdef USE_QUIC OBJ_TYPE_DGRAM, /* object is a struct quic_dgram */ #endif + OBJ_TYPE_HATERM, /* object is a struct hstream */ OBJ_TYPE_ENTRIES /* last one : number of entries */ } __attribute__((packed)) ; diff --git a/include/haproxy/obj_type.h b/include/haproxy/obj_type.h index 233f9d7ad..bd850b969 100644 --- a/include/haproxy/obj_type.h +++ b/include/haproxy/obj_type.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -189,6 +190,19 @@ static inline struct check *objt_check(enum obj_type *t) return __objt_check(t); } +static inline struct hstream *__objt_hstream(enum obj_type *t) +{ + return container_of(t, struct hstream, obj_type); +} + +static inline struct hstream *objt_hstream(enum obj_type *t) +{ + if (!t || *t != OBJ_TYPE_HATERM) + return NULL; + + return __objt_hstream(t); +} + #ifdef USE_QUIC static inline struct quic_dgram *__objt_dgram(enum obj_type *t) { diff --git a/include/haproxy/stconn.h b/include/haproxy/stconn.h index 3a2c5238c..5a906e623 100644 --- a/include/haproxy/stconn.h +++ b/include/haproxy/stconn.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -45,10 +46,12 @@ void se_shutdown(struct sedesc *sedesc, enum se_shut_mode mode); struct stconn *sc_new_from_endp(struct sedesc *sedesc, struct session *sess, struct buffer *input); struct stconn *sc_new_from_strm(struct stream *strm, unsigned int flags); struct stconn *sc_new_from_check(struct check *check, unsigned int flags); +struct stconn *sc_new_from_haterm(struct sedesc *sd, struct session *sess, struct buffer *input); void sc_free(struct stconn *sc); int sc_attach_mux(struct stconn *sc, void *target, void *ctx); int sc_attach_strm(struct stconn *sc, struct stream *strm); +int sc_attach_hstream(struct stconn *sc, struct hstream *hs); void sc_destroy(struct stconn *sc); int sc_reset_endp(struct stconn *sc); @@ -331,6 +334,21 @@ static inline struct check *sc_check(const struct stconn *sc) return NULL; } +/* Returns the haterm stream from a sc if the application is a + * haterm stream. Otherwise NULL is returned. __sc_hstream() returns the haterm + * stream without any control while sc_hstream() check the application type. + */ +static inline struct hstream *__sc_hstream(const struct stconn *sc) +{ + return __objt_hstream(sc->app); +} +static inline struct hstream *sc_hstream(const struct stconn *sc) +{ + if (obj_type(sc->app) == OBJ_TYPE_HATERM) + return __objt_hstream(sc->app); + return NULL; +} + /* Returns the name of the application layer's name for the stconn, * or "NONE" when none is attached. */ diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c index 9a680ef09..3bce9dd0b 100644 --- a/src/cfgparse-listen.c +++ b/src/cfgparse-listen.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -645,9 +646,22 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) mode = str_to_proxy_mode(args[1]); if (!mode) { - ha_alert("parsing [%s:%d] : unknown proxy mode '%s'.\n", file, linenum, args[1]); - err_code |= ERR_ALERT | ERR_FATAL; - goto out; + if (strcmp(args[1], "haterm") == 0) { + if (!(curproxy->cap & PR_CAP_FE)) { + ha_alert("parsing [%s:%d] : mode haterm is only applicable" + " on proxies with frontend capability.\n", file, linenum); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + mode = PR_MODE_HTTP; + curproxy->stream_new_from_sc = hstream_new; + } + else { + ha_alert("parsing [%s:%d] : unknown proxy mode '%s'.\n", file, linenum, args[1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } } else if ((mode == PR_MODE_SYSLOG || mode == PR_MODE_SPOP) && !(curproxy->cap & PR_CAP_BE)) { diff --git a/src/haterm.c b/src/haterm.c new file mode 100644 index 000000000..672a69912 --- /dev/null +++ b/src/haterm.c @@ -0,0 +1,1028 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_TYPED_POOL(pool_head_hstream, "hstream", struct hstream); + +/* haterm stream state flags */ +#define HS_ST_IN_ALLOC 0x0001 +#define HS_ST_OUT_ALLOC 0x0002 +#define HS_ST_CONN_ERROR 0x0004 +#define HS_ST_HTTP_GOT_HDRS 0x0008 +#define HS_ST_HTTP_HELP 0x0010 +#define HS_ST_HTTP_EXPECT 0x0020 +#define HS_ST_HTTP_RESP_SL_SENT 0x0040 + +const char *HTTP_HELP = + "HAProxy's dummy HTTP server for benchmarks - version " HAPROXY_VERSION ".\n" + "All integer argument values are in the form [digits]*[kmgr] (r=random(0..1)).\n" + "The following arguments are supported to override the default objects :\n" + " - /?s= return bytes.\n" + " E.g. /?s=20k\n" + " - /?r= present as the HTTP return code.\n" + " E.g. /?r=404\n" + " - /?c= set the return as not cacheable if <1.\n" + " E.g. /?c=0\n" + " - /?A= drain the request body after sending the response.\n" + " E.g. /?A=1\n" + " - /?C= force the response to use close if >0.\n" + " E.g. /?C=1\n" + " - /?K= force the response to use keep-alive if >0.\n" + " E.g. /?K=1\n" + " - /?t=