]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: ssl: allow h3/QMux negotiation without explicit proto
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 19 May 2026 15:54:05 +0000 (17:54 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 19 May 2026 16:40:50 +0000 (18:40 +0200)
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.

src/mux_quic.c
src/ssl_sock.c
src/xprt_qmux.c

index 94f7b5c5b9293cc73a79bcdef0a31c36783505e3..f9766509a4ccb73c0a926a52030cb26054ce935f 100644 (file)
@@ -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);
index d9065d82e4052e86f0dd09dd1aa1275978294734..38f8ab505081c6ba2333c1e152da75bafbdd958e 100644 (file)
@@ -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) {
index 937c977a34b4d58ac5f4490331c74f2aac507ace..88f4b0120faa15439990c4f40cbd112892a0ad9c 100644 (file)
@@ -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;