From: Amaury Denoyelle Date: Wed, 17 Jun 2026 16:05:52 +0000 (+0200) Subject: BUG/MINOR: quic: ignore STREAM after MUX closure on BE side X-Git-Url: http://git.kaiwu.me/postgresql/log/contrib/virtual_hosts.html?a=commitdiff_plain;h=73a4f6bea70d4035dd2f5c4ec6d65f3b749a08b0;p=haproxy.git BUG/MINOR: quic: ignore STREAM after MUX closure on BE side For frontend connections, quic_conn layer is able to reject any new streams opened after MUX closure. This is necessary as the peer may not have been notified yet of the closure. This operation is unnecessary on backend side. This is due to the fact that only HTTP protocols are currently supported on top of QUIC, with requests initiated by the client. For requests started before the MUX closure, either they are already completed or closed early with a STOP_SENDING emitted during stream shut. Prior to this patch, spurrious RESET_STREAM could have been emitted on backend connections after MUX closure as quic_conn stream_max_bidi was not correctly set. Now reject is only performed for frontend connections so this should not occured anymore. This should be backported up to 3.3. --- diff --git a/src/mux_quic.c b/src/mux_quic.c index dcf21a726..a8a552cb6 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -3643,8 +3643,10 @@ static void qcc_release(struct qcc *qcc) } /* register streams IDs so that quic-conn layer can ignore already closed streams. */ - qc->rx.stream_max_uni = qcc->largest_uni_r; - qc->rx.stream_max_bidi = qcc->largest_bidi_r; + if (!conn_is_back(conn)) { + qc->rx.stream_max_uni = qcc->largest_uni_r; + qc->rx.stream_max_bidi = qcc->largest_bidi_r; + } } tasklet_free(qcc->wait_event.tasklet); @@ -3667,7 +3669,7 @@ static void qcc_release(struct qcc *qcc) if (qcc->app_ops) { if (qcc->app_ops->release) qcc->app_ops->release(qcc->ctx); - if (conn && conn_is_quic(conn) && conn->handle.qc) + if (conn && !conn_is_back(conn) && conn_is_quic(conn) && conn->handle.qc) conn->handle.qc->strm_reject = qcc->app_ops->strm_reject; } TRACE_PROTO("application layer released", QMUX_EV_QCC_END, conn); diff --git a/src/quic_rx.c b/src/quic_rx.c index 06b3f3e99..932058f0c 100644 --- a/src/quic_rx.c +++ b/src/quic_rx.c @@ -975,11 +975,21 @@ static int qc_parse_pkt_frms(struct quic_conn *qc, struct quic_rx_packet *pkt, { struct qf_stream *strm_frm = &frm->stream; const char fin = frm->type & QUIC_STREAM_FRAME_TYPE_FIN_BIT; - const uint64_t max = quic_stream_is_uni(strm_frm->id) ? - qc->rx.stream_max_uni : qc->rx.stream_max_bidi; + uint64_t max; /* The upper layer may not be allocated. */ if (!qc_is_conn_ready(qc)) { + if (qc_is_back(qc)) { + /* Drain STREAM frames on the backend side. MUX should + * have emitted STOP_SENDING on shut for incomplete streams, + * so the peer should soon stop emission. + */ + TRACE_DATA("Ignore stream on backend", QUIC_EV_CONN_PRSHPKT, qc); + break; + } + + max = quic_stream_is_uni(strm_frm->id) ? + qc->rx.stream_max_uni : qc->rx.stream_max_bidi; if (strm_frm->id < max) { TRACE_DATA("Already closed stream", QUIC_EV_CONN_PRSHPKT, qc); }