From: Amaury Denoyelle Date: Fri, 10 Apr 2026 07:40:10 +0000 (+0200) Subject: MINOR: xprt_qstrm/mux-quic: handle extra QMux frames after params X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fb3b2687472a27c4b06dba3bf5533cfacf8c1df3;p=thirdparty%2Fhaproxy.git MINOR: xprt_qstrm/mux-quic: handle extra QMux frames after params 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. --- diff --git a/include/haproxy/xprt_qstrm.h b/include/haproxy/xprt_qstrm.h index 182a72c3e..536ba1abd 100644 --- a/include/haproxy/xprt_qstrm.h +++ b/include/haproxy/xprt_qstrm.h @@ -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 */ diff --git a/src/mux_quic.c b/src/mux_quic.c index 234dfe1c0..15302f5fa 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -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; diff --git a/src/xprt_qstrm.c b/src/xprt_qstrm.c index 7b44cd26c..a51222a70 100644 --- a/src/xprt_qstrm.c +++ b/src/xprt_qstrm.c @@ -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;