MINOR: mux-quic: emit FLOW_CONTROL_ERROR

Send a CONNECTION_CLOSE if the peer emits more data than authorized by
our flow-control. This is implemented for both stream and connection
level.

Fields have been added in qcc/qcs structures to differentiate received
offsets for limit enforcing with consumed offsets for sending of
MAX_DATA/MAX_STREAM_DATA frames.
diff --git a/include/haproxy/mux_quic-t.h b/include/haproxy/mux_quic-t.h
index b2d0aad..903e326 100644
--- a/include/haproxy/mux_quic-t.h
+++ b/include/haproxy/mux_quic-t.h
@@ -60,7 +60,8 @@
 
 		uint64_t md; /* current max-data allowed for the peer */
 		uint64_t md_init; /* initial max-data */
-		uint64_t sent_offsets; /* sum of all offsets received */
+		uint64_t offsets_recv; /* sum of offsets received */
+		uint64_t offsets_consume; /* sum of offsets consumed */
 	} lfctl;
 
 	/* flow-control fields set by the peer which we must respect. */
@@ -108,6 +109,7 @@
 
 	struct {
 		uint64_t offset; /* absolute current base offset of ncbuf */
+		uint64_t offset_max; /* maximum absolute offset received */
 		struct ncbuf ncbuf; /* receive buffer - can handle out-of-order offset frames */
 		struct buffer app_buf; /* receive buffer used by conn_stream layer */
 		uint64_t msd; /* current max-stream-data limit to enforce */
diff --git a/src/mux_quic.c b/src/mux_quic.c
index 0e3e2c2..8485f81 100644
--- a/src/mux_quic.c
+++ b/src/mux_quic.c
@@ -165,7 +165,7 @@
 
 	qcs->rx.ncbuf = NCBUF_NULL;
 	qcs->rx.app_buf = BUF_NULL;
-	qcs->rx.offset = 0;
+	qcs->rx.offset = qcs->rx.offset_max = 0;
 
 	/* TODO use uni limit for unidirectional streams */
 	qcs->rx.msd = quic_stream_is_local(qcc, id) ? qcc->lfctl.msd_bidi_l :
@@ -333,12 +333,12 @@
 		tasklet_wakeup(qcc->wait_event.tasklet);
 	}
 
-	qcc->lfctl.sent_offsets += bytes;
-	if (qcc->lfctl.md - qcc->lfctl.sent_offsets < qcc->lfctl.md_init / 2) {
+	qcc->lfctl.offsets_consume += bytes;
+	if (qcc->lfctl.md - qcc->lfctl.offsets_consume < qcc->lfctl.md_init / 2) {
 		frm = pool_zalloc(pool_head_quic_frame);
 		BUG_ON(!frm); /* TODO handle this properly */
 
-		qcc->lfctl.md = qcc->lfctl.sent_offsets + qcc->lfctl.md_init;
+		qcc->lfctl.md = qcc->lfctl.offsets_consume + qcc->lfctl.md_init;
 
 		LIST_INIT(&frm->reflist);
 		frm->type = QUIC_FT_MAX_DATA;
@@ -486,8 +486,25 @@
 	 * Else send FINAL_SIZE_ERROR.
 	 */
 
-	/* TODO initial max-stream-data overflow. Implement FLOW_CONTROL_ERROR emission. */
-	BUG_ON(offset + len > qcs->rx.msd);
+	if (offset + len > qcs->rx.offset_max) {
+		uint64_t diff = offset + len - qcs->rx.offset_max;
+		qcs->rx.offset_max = offset + len;
+		qcc->lfctl.offsets_recv += diff;
+
+		if (offset + len > qcs->rx.msd ||
+		    qcc->lfctl.offsets_recv > qcc->lfctl.md) {
+			/* RFC 9000 4.1. Data Flow Control
+			 *
+			 * A receiver MUST close the connection with an error
+			 * of type FLOW_CONTROL_ERROR if the sender violates
+			 * the advertised connection or stream data limits
+			 */
+			TRACE_DEVEL("leaving on flow control error", QMUX_EV_QCC_RECV|QMUX_EV_QCS_RECV,
+				    qcc->conn, qcs);
+			qcc_emit_cc(qcc, QC_ERR_FLOW_CONTROL_ERROR);
+			return 1;
+		}
+	}
 
 	if (!qc_get_ncbuf(qcs, &qcs->rx.ncbuf) || ncb_is_null(&qcs->rx.ncbuf)) {
 		/* TODO should mark qcs as full */
@@ -1283,7 +1300,7 @@
 	qcc->lfctl.cl_bidi_r = 0;
 
 	qcc->lfctl.md = qcc->lfctl.md_init = lparams->initial_max_data;
-	qcc->lfctl.sent_offsets = 0;
+	qcc->lfctl.offsets_recv = qcc->lfctl.offsets_consume = 0;
 
 	rparams = &conn->handle.qc->tx.params;
 	qcc->rfctl.md = rparams->initial_max_data;