From: Amaury Denoyelle Date: Tue, 19 May 2026 15:54:05 +0000 (+0200) Subject: MEDIUM: ssl: allow h3/QMux negotiation without explicit proto X-Git-Tag: v3.4-dev13~30 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f2b152c95e53f6047ce8277a1bef769b4f781540;p=thirdparty%2Fhaproxy.git MEDIUM: ssl: allow h3/QMux negotiation without explicit proto Implements automatic selection of QMux MUX if "h3" ALPN has been negotiated on top of TCP/SSL. The first part of this change is to define "alpn" member of mux_proto_list. This is necessary so that conn_get_best_mux_entry() can select it when "h3" has been chosen. As a side-effect, this also automatically sets a default ALPN to "h3" for bind lines with "proto qmux". The most important change is to adapt the SSL layer. On handshake completion, the eligible MUX is retrieved via conn_select_mux_fe/be() functions. If xprt_qmux is required by it, MUX init is delayed and QMux handshake is started first. This last change is necessary as connection flags CO_FL_QMUX_RECV/SEND are only set if "proto qmux" is explicitely set. In case xprt_qmux is activated via pure ALPN negotiation, these flags are also set on xprt_qmux_init(). This is mandatory to ensure emission/reception of QMux transport parameters will be performed as expected. --- diff --git a/src/mux_quic.c b/src/mux_quic.c index 94f7b5c5b..f9766509a 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -4814,6 +4814,6 @@ static const struct mux_ops qmux_ops = { static struct mux_proto_list mux_proto_qmux = { .mux_proto = IST("qmux"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &qmux_ops, - .init_xprt = XPRT_QMUX }; + .alpn = "\002h3", .init_xprt = XPRT_QMUX }; INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_qmux); diff --git a/src/ssl_sock.c b/src/ssl_sock.c index d9065d82e..38f8ab505 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -6962,10 +6962,15 @@ struct task *ssl_sock_io_cb(struct task *t, void *context, unsigned int state) * woke a tasklet already. */ if (ctx->conn->xprt_ctx == ctx) { + const struct mux_proto_list *mux; int closed_connection = 0; if (!ctx->conn->mux) { - if (ctx->conn->flags & (CO_FL_QMUX_RECV|CO_FL_QMUX_SEND)) { + mux = !conn_is_back(conn) ? + conn_select_mux_fe(conn) : conn_select_mux_be(conn); + + if (ctx->conn->flags & (CO_FL_QMUX_RECV|CO_FL_QMUX_SEND) || + mux->init_xprt == XPRT_QMUX) { const struct xprt_ops *ops = xprt_get(XPRT_QMUX); void *xprt_ctx_hs = NULL; @@ -6983,8 +6988,13 @@ struct task *ssl_sock_io_cb(struct task *t, void *context, unsigned int state) ret = conn->xprt->start(conn, xprt_ctx_hs); BUG_ON(ret); } - else + else { + /* TODO MUX selection already performs by conn_select_mux_fe/be(). + * Implement an alternative to conn_create_mux() to skip this + * part and directly init the connection and its MUX. + */ ret = conn_create_mux(ctx->conn, &closed_connection); + } } if (ret >= 0 && ctx->conn->mux && !woke && ctx->conn->mux && ctx->conn->mux->wake) { diff --git a/src/xprt_qmux.c b/src/xprt_qmux.c index 937c977a3..88f4b0120 100644 --- a/src/xprt_qmux.c +++ b/src/xprt_qmux.c @@ -300,6 +300,11 @@ static int xprt_qmux_init(struct connection *conn, void **xprt_ctx) ctx->lparams.initial_max_stream_data_bidi_remote = qcm_stream_rx_bufsz(); ctx->lparams.initial_max_stream_data_uni = qcm_stream_rx_bufsz(); + /* Ensure the connection flags are set. Necessary when current XPRT is + * activated without explicit "proto qmux" configuration. + */ + conn->flags |= (CO_FL_QMUX_RECV|CO_FL_QMUX_SEND); + *xprt_ctx = ctx; return 0;