]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: xprt_qstrm/mux-quic: handle extra QMux frames after params
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 10 Apr 2026 07:40:10 +0000 (09:40 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 10 Apr 2026 08:20:52 +0000 (10:20 +0200)
Layer xprt_qstrm is responsible to read the initial QMux transport
parameters frame. However, it could receive more data if some other
frames follow it. This extra content can only be handled by the MUX
layer once initialized.

Theorically, it could have been implemented via MSG_PEEK. However, this
flag is currently ignored by SSL layer. Besides, it is tedious to
implement safely. A new approach has been prefered where the MUX layer
is responsible to retrieve remaining data via xprt_qstrm_rxbuf()
accessor function during its initialization.

Thus, qmux_init() now may retrieve the buffer from xprt_qstrm layer.
This is performed via b_xfer() which will result in a zero copy
transfer. If this happens, tasklet is immediately scheduled to start
demuxing.

include/haproxy/xprt_qstrm.h
src/mux_quic.c
src/xprt_qstrm.c

index 182a72c3e43ca8c408b2c02604fcc449575e531d..536ba1abd0a6397513975287c533f70242338e48 100644 (file)
@@ -4,4 +4,6 @@
 const struct quic_transport_params *xprt_qstrm_lparams(const void *context);
 const struct quic_transport_params *xprt_qstrm_rparams(const void *context);
 
+struct buffer *xprt_qstrm_rxbuf(const void *context);
+
 #endif /* _HAPROXY_XPRT_QSTRM_H */
index 234dfe1c00bf69b02af0e6779beb5d84df57dca2..15302f5fa35154ad908785ffec6f3bc9d955dbbd 100644 (file)
@@ -3784,6 +3784,8 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
        }
 
        if (!conn_is_quic(conn)) {
+               struct buffer *xprt_buf;
+
                qcc->tx.qstrm_buf = BUF_NULL;
                b_alloc(&qcc->tx.qstrm_buf, DB_MUX_TX);
                if (!b_size(&qcc->tx.qstrm_buf)) {
@@ -3797,6 +3799,11 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
                        TRACE_ERROR("rx qstrm buf alloc failure", QMUX_EV_QCC_NEW);
                        goto err;
                }
+
+               /* Retrieve data if xprt read too much */
+               xprt_buf = xprt_qstrm_rxbuf(conn->xprt_ctx);
+               if (unlikely(b_data(xprt_buf)))
+                       b_xfer(&qcc->rx.qstrm_buf, xprt_buf, b_data(xprt_buf));
        }
 
        if (conn_is_back(conn)) {
@@ -3857,8 +3864,14 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
        qcc_reset_idle_start(qcc);
        LIST_INIT(&qcc->opening_list);
 
-       if (conn_is_quic(conn))
+       if (conn_is_quic(conn)) {
                HA_ATOMIC_STORE(&conn->handle.qc->qcc, qcc);
+       }
+       else {
+               /* Wakeup MUX immediately if data copied from XPRT layer. */
+               if (unlikely(b_data(&qcc->rx.qstrm_buf)))
+                       tasklet_wakeup(qcc->wait_event.tasklet);
+       }
 
        /* Register conn as app_ops may use it. */
        qcc->conn = conn;
index 7b44cd26cdec5506af4773850c0ea2e0d7499ecc..a51222a70e0c42527670b7187dc163274e241179 100644 (file)
@@ -37,6 +37,13 @@ const struct quic_transport_params *xprt_qstrm_rparams(const void *context)
        return &ctx->rparams;
 }
 
+/* Returns RX buffer as mutable to allow zero-copy by the caller. */
+struct buffer *xprt_qstrm_rxbuf(void *context)
+{
+       struct xprt_qstrm_ctx *ctx = context;
+       return &ctx->rxbuf;
+}
+
 int conn_recv_qstrm(struct connection *conn, struct xprt_qstrm_ctx *ctx, int flag)
 {
        struct quic_frame frm;