MINOR: haterm: add haterm HTTP server

Contrary to haproxy, httpterm does not support all the HTTP protocols.
Furthermore, it has become easier to handle inbound/outbound
connections / streams since the rework done at conn_stream level.

This patch implements httpterm HTTP server services into haproxy. To do
so, it proceeds the same way as for the TCP checks which use only one
stream connector, but on frontend side.

The makefile is modified to handle haterm.c in additions to all the C
files for haproxy to build new haterm program into haproxy, the haterm
server also instantiates a haterm stream (hstream struct) attached to a
stream connector for each incoming connection without backend stream
connector. This is the role of sc_new_from_endp() called by the muxes to
instantiate streams/hstreams.

As for stream_new(), hstream_new() instantiates a task named
process_hstream() (see haterm.c) which has the same role as
process_stream() but for haterm streams.

haterm into haproxy takes advantage of the HTTP muxes and HTX API to
support all the HTTP protocols supported by haproxy.
This commit is contained in:
Frederic Lecaille 2026-02-11 15:05:42 +01:00 committed by Willy Tarreau
parent 2bf091e9da
commit c9d47804d1
10 changed files with 1164 additions and 4 deletions

View File

@ -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

View File

@ -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@<name>" syntax. Log

View File

@ -0,0 +1,36 @@
#ifndef _HAPROXY_HSTREAM_T_H
#define _HAPROXY_HSTREAM_T_H
#include <haproxy/dynbuf-t.h>
#include <haproxy/http-t.h>
#include <haproxy/obj_type-t.h>
/* 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 */

12
include/haproxy/hstream.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef _HAPROXY_HSTREAM_H
#define _HAPROXY_HSTREAM_H
#include <haproxy/cfgparse.h>
#include <haproxy/hstream-t.h>
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 */

View File

@ -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)) ;

View File

@ -26,6 +26,7 @@
#include <haproxy/applet-t.h>
#include <haproxy/check-t.h>
#include <haproxy/connection-t.h>
#include <haproxy/hstream-t.h>
#include <haproxy/listener-t.h>
#include <haproxy/obj_type-t.h>
#include <haproxy/pool.h>
@ -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)
{

View File

@ -24,6 +24,7 @@
#include <haproxy/api.h>
#include <haproxy/connection.h>
#include <haproxy/hstream-t.h>
#include <haproxy/htx-t.h>
#include <haproxy/obj_type.h>
#include <haproxy/stconn-t.h>
@ -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.
*/

View File

@ -18,6 +18,7 @@
#include <haproxy/compression-t.h>
#include <haproxy/connection.h>
#include <haproxy/extcheck.h>
#include <haproxy/hstream.h>
#include <haproxy/http_ana.h>
#include <haproxy/http_htx.h>
#include <haproxy/http_ext.h>
@ -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)) {

1028
src/haterm.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,7 @@
#include <haproxy/connection.h>
#include <haproxy/check.h>
#include <haproxy/filters.h>
#include <haproxy/hstream.h>
#include <haproxy/http_ana.h>
#include <haproxy/pipe.h>
#include <haproxy/pool.h>
@ -91,6 +92,15 @@ struct sc_app_ops sc_app_check_ops = {
.name = "CHCK",
};
struct sc_app_ops sc_app_hstream_ops = {
.chk_rcv = NULL,
.chk_snd = NULL,
.abort = NULL,
.shutdown= NULL,
.wake = hstream_wake,
.name = "HTERM",
};
/* Initializes an endpoint */
void sedesc_init(struct sedesc *sedesc)
{
@ -415,6 +425,30 @@ int sc_attach_strm(struct stconn *sc, struct stream *strm)
return 0;
}
/* Attach a stconn to a haterm layer and sets the relevant
* callbacks. Returns -1 on error and 0 on success. SE_FL_ORPHAN flag is
* removed. This function is called by a haterm stream when it is created
* to attach it on the stream connector on the client side.
*/
int sc_attach_hstream(struct stconn *sc, struct hstream *hs)
{
BUG_ON(!sc_ep_test(sc, SE_FL_T_MUX));
sc->app = &hs->obj_type;
sc_ep_clr(sc, SE_FL_ORPHAN);
sc_ep_report_read_activity(sc);
sc->wait_event.tasklet = tasklet_new();
if (!sc->wait_event.tasklet)
return -1;
sc->wait_event.tasklet->process = sc_hstream_io_cb;
sc->wait_event.tasklet->context = sc;
sc->wait_event.events = 0;
sc->app_ops = &sc_app_hstream_ops;
return 0;
}
/* Detaches the stconn from the endpoint, if any. For a connecrion, if a
* mux owns the connection ->detach() callback is called. Otherwise, it means
* the stream connector owns the connection. In this case the connection is closed