MINOR: quic: Add draining connection state.

As soon as we receive a CONNECTION_CLOSE frame, we must stop sending packets.
We add QUIC_FL_CONN_DRAINING connection flag to do so.
diff --git a/include/haproxy/xprt_quic-t.h b/include/haproxy/xprt_quic-t.h
index e72ad6a..4812841 100644
--- a/include/haproxy/xprt_quic-t.h
+++ b/include/haproxy/xprt_quic-t.h
@@ -662,6 +662,7 @@
 #define QUIC_FL_CONN_LISTENER                    (1U << 3)
 #define QUIC_FL_CONN_ACCEPT_REGISTERED           (1U << 4)
 #define QUIC_FL_CONN_IDLE_TIMER_RESTARTED_AFTER_READ (1U << 6)
+#define QUIC_FL_CONN_DRAINING                    (1U << 30)
 #define QUIC_FL_CONN_IMMEDIATE_CLOSE             (1U << 31)
 struct quic_conn {
 	uint32_t version;
diff --git a/src/xprt_quic.c b/src/xprt_quic.c
index 10d3285..6fdcae6 100644
--- a/src/xprt_quic.c
+++ b/src/xprt_quic.c
@@ -177,6 +177,7 @@
                                            struct quic_conn *qc, size_t dglen, int pkt_type,
                                            int padding, int probe, int cc, int *err);
 static struct task *quic_conn_app_io_cb(struct task *t, void *context, unsigned int state);
+static void qc_idle_timer_do_rearm(struct quic_conn *qc);
 static void qc_idle_timer_rearm(struct quic_conn *qc, int read);
 
 /* Only for debug purpose */
@@ -2576,6 +2577,20 @@
 			break;
 		case QUIC_FT_CONNECTION_CLOSE:
 		case QUIC_FT_CONNECTION_CLOSE_APP:
+			if (!(qc->flags & QUIC_FL_CONN_DRAINING)) {
+				TRACE_PROTO("Entering draining state", QUIC_EV_CONN_PRSHPKT, qc);
+				/* RFC 9000 10.2. Immediate Close:
+				 * The closing and draining connection states exist to ensure
+				 * that connections close cleanly and that delayed or reordered
+				 * packets are properly discarded. These states SHOULD persist
+				 * for at least three times the current PTO interval...
+				 *
+				 * Rearm the idle timeout only one time when entering draining
+				 * state.
+				 */
+				qc_idle_timer_do_rearm(qc);
+				qc->flags |= QUIC_FL_CONN_DRAINING|QUIC_FL_CONN_IMMEDIATE_CLOSE;
+			}
 			/* warn the mux to close the connection */
 			if (qc->mux_state == QC_MUX_READY) {
 				qc->qcc->flags |= QC_CF_CC_RECV;
@@ -3555,9 +3570,14 @@
 	if (!qc_treat_rx_pkts(qel, NULL, ctx, 0))
 		goto err;
 
+	if ((qc->flags & QUIC_FL_CONN_DRAINING) &&
+	    !(qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE))
+		goto out;
+
 	if (!qc_send_app_pkts(qc, &qel->pktns->tx.frms))
 		goto err;
 
+out:
 	return t;
 
  err:
@@ -3619,6 +3639,10 @@
 	if (!qc_treat_rx_pkts(qel, next_qel, ctx, force_ack))
 		goto err;
 
+	if ((qc->flags & QUIC_FL_CONN_DRAINING) &&
+	    !(qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE))
+		goto out;
+
 	if (zero_rtt && next_qel && !MT_LIST_ISEMPTY(&next_qel->rx.pqpkts) &&
 	    (next_qel->tls_ctx.flags & QUIC_FL_TLS_SECRETS_SET)) {
 		qel = next_qel;
@@ -3676,7 +3700,9 @@
 		goto next_level;
 	}
 
-	MT_LIST_APPEND(qc->tx.qring_list, &qr->mt_list);
+ out:
+	if (qr)
+		MT_LIST_APPEND(qc->tx.qring_list, &qr->mt_list);
 	TRACE_LEAVE(QUIC_EV_CONN_IO_CB, qc, &st);
 	return t;
 
@@ -4031,21 +4057,27 @@
 	return 1;
 }
 
+/* Rearm the idle timer for <qc> QUIC connection. */
+static void qc_idle_timer_do_rearm(struct quic_conn *qc)
+{
+	unsigned int expire;
+
+	expire = QUIC_MAX(3 * quic_pto(qc), qc->max_idle_timeout);
+	qc->idle_timer_task->expire = tick_add(now_ms, MS_TO_TICKS(expire));
+}
+
 /* Rearm the idle timer for <qc> QUIC connection depending on <read> boolean
  * which is set to 1 when receiving a packet , and 0 when sending packet
  */
 static void qc_idle_timer_rearm(struct quic_conn *qc, int read)
 {
-	unsigned int expire;
-
-	expire = QUIC_MAX(3 * quic_pto(qc), qc->max_idle_timeout);
 	if (read) {
 		qc->flags |= QUIC_FL_CONN_IDLE_TIMER_RESTARTED_AFTER_READ;
 	}
 	else {
 		qc->flags &= ~QUIC_FL_CONN_IDLE_TIMER_RESTARTED_AFTER_READ;
 	}
-	qc->idle_timer_task->expire = tick_add(now_ms, MS_TO_TICKS(expire));
+	qc_idle_timer_do_rearm(qc);
 }
 
 /* The task handling the idle timeout */
@@ -5551,6 +5583,8 @@
 		pkt->in_flight_len = pkt->len;
 		qc->path->prep_in_flight += pkt->len;
 	}
+	/* Always reset this flags */
+	qc->flags &= ~QUIC_FL_CONN_IMMEDIATE_CLOSE;
 	if (pkt->flags & QUIC_FL_TX_PACKET_ACK) {
 		qel->pktns->flags &= ~QUIC_FL_PKTNS_ACK_REQUIRED;
 		qel->pktns->rx.nb_aepkts_since_last_ack = 0;