From: Christopher Faulet Date: Wed, 18 Feb 2026 08:26:02 +0000 (+0100) Subject: BUG/MEDIUM: mux-h2/quic: Stop sending via fast-forward if stream is closed X-Git-Tag: v3.4-dev5~61 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cda056b9f4c73be728e64baf402198f984318632;p=thirdparty%2Fhaproxy.git BUG/MEDIUM: mux-h2/quic: Stop sending via fast-forward if stream is closed If is illegal to send data if the stream is already closed. The case is properly handled when data are sent via snd_buf(), by draining the data. But it was still possible to process these data via nego_ff(). So, in this patch, both for the H2 and QUIC multiplexers, the fast-forward is disabled if the stream is closed and nothing is performed. Doing so, we will automatically fall back on the regular sending path and be able to drain data in snd_buf(). Thanks to Mike Walker for his investigation on the subject. This patch should be backported as far as 3.0. --- diff --git a/src/mux_h2.c b/src/mux_h2.c index 6ff52d0d8..a870426dc 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -8097,6 +8097,15 @@ static size_t h2_nego_ff(struct stconn *sc, struct buffer *input, size_t count, TRACE_ENTER(H2_EV_H2S_SEND|H2_EV_STRM_SEND, h2s->h2c->conn, h2s); + if (h2s->st >= H2_SS_HLOC) { + /* Cannot emit any new data if stream already closed. Data + * draining will be performed via snd_buf. + */ + TRACE_DEVEL("stream already closed, disable FF", H2_EV_H2S_SEND, h2s->h2c->conn, h2s); + h2s->sd->iobuf.flags |= IOBUF_FL_NO_FF; + goto end; + } + /* If we were not just woken because we wanted to send but couldn't, * and there's somebody else that is waiting to send, do nothing, * we will subscribe later and be put at the end of the list diff --git a/src/mux_quic.c b/src/mux_quic.c index b24f2086b..14afb4073 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -4167,6 +4167,15 @@ static size_t qmux_strm_nego_ff(struct stconn *sc, struct buffer *input, goto end; } + if (qcs_is_close_local(qcs) || (qcs->flags & QC_SF_TO_RESET)) { + /* Cannot emit any new data if stream already closed. Data + * draining will be performed via snd_buf. + */ + TRACE_DEVEL("stream already closed", QMUX_EV_STRM_SEND, qcs->qcc->conn, qcs); + qcs->sd->iobuf.flags |= IOBUF_FL_NO_FF; + goto end; + } + if (LIST_INLIST(&qcs->el_buf)) { TRACE_DEVEL("leaving on no buf avail", QMUX_EV_STRM_SEND, qcs->qcc->conn, qcs); qcs->sd->iobuf.flags |= IOBUF_FL_FF_BLOCKED;