]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MEDIUM: mux_quic: account discarded data in connection flow control
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 23 Sep 2026 13:34:28 +0000 (15:34 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 7 Oct 2026 08:21:48 +0000 (10:21 +0200)
Read data not yet consumed by the upper stream layer are discarded when
a RESET_STREAM is received. This patch implements their accounting in
the connection flow control.

This feature is mandatory as it could result in discrepancies between
each peers for the measured connection flow control. If this grows too
big, the sender may be indefinitely blocked, forcing the opening of a
new connection.

This patch introduces a new function qcc_account_conn_fctl(), which is
used to factorize the code already present during STREAM frames
handling. This is now also called for RESET_STREAM frames.

This must be backported up to 2.8, after a period of observation.

src/mux_quic.c

index 86585999c721bb74f68a58b634823d5edcf64cae..403acbfcd6ba9a9995c5ec9f75c7df6aa390eae9 100644 (file)
@@ -1351,6 +1351,36 @@ static ncb_sz_t qcs_rx_avail_data(struct qcs *qcs)
        return b ? ncb_data(&b->ncb, 0) : 0;
 }
 
+/* Account for <bytes> in <qcc> connection flow-control. If needed, a MAX_DATA
+ * frame is prepared for emission.
+ */
+static void qcc_account_for_conn_fctl(struct qcc *qcc, uint64_t bytes)
+{
+       struct quic_frame *frm;
+
+       TRACE_ENTER(QMUX_EV_QCC_RECV, qcc->conn);
+
+       qcc->lfctl.offsets_consume += bytes;
+       if (qcc->lfctl.md - qcc->lfctl.offsets_consume < qcc->lfctl.md_init / 2) {
+               TRACE_DATA("increase conn credit via MAX_DATA", QMUX_EV_QCS_RECV, qcc->conn);
+               frm = qc_frm_alloc(QUIC_FT_MAX_DATA);
+               if (!frm) {
+                       qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0,
+                                     muxc_tevt_type_internal_err);
+                       goto out;
+               }
+
+               qcc->lfctl.md = qcc->lfctl.offsets_consume + qcc->lfctl.md_init;
+               frm->max_data.max_data = qcc->lfctl.md;
+
+               LIST_APPEND(&qcc->lfctl.frms, &frm->list);
+               tasklet_wakeup(qcc->wait_event.tasklet);
+       }
+
+ out:
+       TRACE_LEAVE(QMUX_EV_QCC_RECV, qcc->conn);
+}
+
 /* Remove <bytes> from <buf> current Rx buffer of <qcs> stream. Flow-control
  * for received offsets may be allocated for the peer if needed.
  */
@@ -1422,23 +1452,7 @@ static void qcs_consume(struct qcs *qcs, uint64_t bytes, struct qc_stream_rxbuf
        }
 
  conn_fctl:
-       qcc->lfctl.offsets_consume += bytes;
-       if (qcc->lfctl.md - qcc->lfctl.offsets_consume < qcc->lfctl.md_init / 2) {
-               TRACE_DATA("increase conn credit via MAX_DATA", QMUX_EV_QCS_RECV, qcc->conn, qcs);
-               frm = qc_frm_alloc(QUIC_FT_MAX_DATA);
-               if (!frm) {
-                       qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0,
-                                     muxc_tevt_type_internal_err);
-                       return;
-               }
-
-               qcc->lfctl.md = qcc->lfctl.offsets_consume + qcc->lfctl.md_init;
-
-               frm->max_data.max_data = qcc->lfctl.md;
-
-               LIST_APPEND(&qcs->qcc->lfctl.frms, &frm->list);
-               tasklet_wakeup(qcc->wait_event.tasklet);
-       }
+       qcc_account_for_conn_fctl(qcc, bytes);
 
        TRACE_LEAVE(QMUX_EV_QCS_RECV, qcc->conn, qcs);
 }
@@ -2404,6 +2418,10 @@ int qcc_recv_reset_stream(struct qcc *qcc, uint64_t id, uint64_t err, uint64_t f
                qcs_free_rxbuf(qcs, b);
        }
 
+       qcc_account_for_conn_fctl(qcc, to_consume);
+       /* Ensure final size checks coherence for future STREAM frames. */
+       qcs->rx.offset_max = final_size;
+
        /* Remove stream from recv_list if present. */
        LIST_DEL_INIT(&qcs->el_recv);