]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MAJOR: mux-quic: activate QMux on the backend side
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 26 Mar 2026 09:27:28 +0000 (10:27 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 2 Apr 2026 12:02:05 +0000 (14:02 +0200)
During connect_server(), xprt_qstrm is selected to performed transport
parameters exchange prior to the mux layer initialization.

doc/configuration.txt
src/backend.c
src/mux_quic.c
src/server.c

index 874719d70b9517c281054ae697db6f941111a59b..cea9c8b93e6346181a61a9b88a267f0c273ba9a3 100644 (file)
@@ -17745,7 +17745,7 @@ proto <name>
   a bind line :
 
    quic : mode=HTTP  side=FE|BE  mux=QUIC  flags=HTX|NO_UPG|FRAMED
-    qmux : mode=HTTP  side=FE     mux=QMUX  flags=HTX|NO_UPG
+    qmux : mode=HTTP  side=FE|BE  mux=QMUX  flags=HTX|NO_UPG
     h2   : mode=HTTP  side=FE|BE  mux=H2    flags=HTX|HOL_RISK|NO_UPG
     h1   : mode=HTTP  side=FE|BE  mux=H1    flags=HTX|NO_UPG
     none : mode=TCP   side=FE|BE  mux=PASS  flags=NO_UPG
@@ -19323,6 +19323,7 @@ proto <name>
   a server line :
 
     quic : mode=HTTP  side=FE|BE  mux=QUIC  flags=HTX|NO_UPG|FRAMED
+    qmux : mode=HTTP  side=FE|BE  mux=QMUX  flags=HTX|NO_UPG
     h2   : mode=HTTP  side=FE|BE  mux=H2    flags=HTX|HOL_RISK|NO_UPG
     fcgi : mode=HTTP  side=BE     mux=FCGI  flags=HTX|HOL_RISK|NO_UPG
     h1   : mode=HTTP  side=FE|BE  mux=H1    flags=HTX|NO_UPG
@@ -19338,6 +19339,10 @@ proto <name>
 
   See also "ws" to use an alternative protocol for websocket streams.
 
+  QMux is a subset of QUIC which runs over TCP. It corresponds to the following
+  draft protocol https://www.ietf.org/archive/id/draft-opik-quic-qmux-01.html.
+  It is considered experimental in haproxy for now.
+
 quic-cc-algo { cubic | newreno | bbr | nocc }[(<args,...>)]
   This is a QUIC specific setting to select the congestion control algorithm
   for any connection targeting this server. They are similar to those used by
index 64781d5f320a79a95457003bb091a9e3dc622377..04992e048d4330c30f1e99128f37d7b8ca1ab489 100644 (file)
@@ -2124,6 +2124,11 @@ int connect_server(struct stream *s)
                        srv_conn->flags |= CO_FL_SOCKS4;
                }
 
+               if (srv && srv->mux_proto && isteq(srv->mux_proto->token, ist("qmux"))) {
+                       srv_conn->flags |= (CO_FL_QSTRM_RECV|CO_FL_QSTRM_SEND);
+                       may_start_mux_now = 0;
+               }
+
 #if defined(USE_OPENSSL) && defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
                /* if websocket stream, try to update connection ALPN. */
                if (unlikely(s->flags & SF_WEBSOCKET) &&
index 17cbea11deb70af53ec6a6408462d0d1773ab7f4..732a3367196111491ffc42b0c403cd0fe81f61d5 100644 (file)
@@ -4684,6 +4684,10 @@ static const struct mux_ops qstrm_ops = {
        .subscribe   = qmux_strm_subscribe,
        .unsubscribe = qmux_strm_unsubscribe,
        .wake        = qmux_wake,
+       .avail_streams = qmux_avail_streams,
+       .used_streams = qmux_used_streams,
+       .takeover    = NULL,  /* QUIC takeover support not implemented yet */
+       .attach      = qmux_strm_attach,
        .shut        = qmux_strm_shut,
        .ctl         = qmux_ctl,
        .sctl        = qmux_sctl,
@@ -4693,6 +4697,6 @@ static const struct mux_ops qstrm_ops = {
 };
 
 static struct mux_proto_list mux_proto_qstrm =
-  { .token = IST("qmux"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_FE, .mux = &qstrm_ops };
+  { .token = IST("qmux"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &qstrm_ops };
 
 INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_qstrm);
index d6a41471064f10e337cbf716f57418eaf79779bb..c14ac0724e6e05841f86092fd4e4d2796b71de77 100644 (file)
@@ -1453,6 +1453,15 @@ static int srv_parse_proto(char **args, int *cur_arg,
                memprintf(err, "'%s' :  unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
                return ERR_ALERT | ERR_FATAL;
        }
+
+       if (newsrv->mux_proto->mux->flags & MX_FL_EXPERIMENTAL) {
+               if (!experimental_directives_allowed) {
+                       memprintf(err, "'%s' : '%s' protocol is experimental, must be allowed via a global 'expose-experimental-directives'",
+                                 args[*cur_arg], args[*cur_arg + 1]);
+                       return ERR_ALERT | ERR_FATAL;
+               }
+               mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
+       }
        return 0;
 }