]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: mux-h2/quic: Stop sending via fast-forward if stream is closed
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 18 Feb 2026 08:26:02 +0000 (09:26 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Wed, 18 Feb 2026 08:44:09 +0000 (09:44 +0100)
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.

src/mux_h2.c
src/mux_quic.c

index 6ff52d0d83c72aeb468b49d956c4cbe61cdf2cb8..a870426dc3b3b82e4e2f86d07177a1a7829ba3ca 100644 (file)
@@ -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
index b24f2086b6797669b81a5e13c36a2b95f2b1466a..14afb40737898b65b49b4eae43bf3a2ed0232812 100644 (file)
@@ -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;