MINOR: quic: delay post handshake frames after accept

When QUIC handshake is completed on our side, some frames are prepared
to be sent :
* HANDSHAKE_DONE
* several NEW_CONNECTION_ID with CIDs allocated

This step was previously executed in quic_conn_io_cb() directly after
CRYPTO frames parsing. This patch delays it to be completed after
accept. Special care have been taken to ensure it is still functional
with 0-RTT activated.

For the moment, this patch should have no impact. However, when
quic_conn thread migration on accept will be implemented, it will be
easier to remap only one CID to the new thread. New CIDs will be
allocated after migration on the new thread.

This should be backported up to 2.7 after a period of observation.
diff --git a/include/haproxy/quic_conn-t.h b/include/haproxy/quic_conn-t.h
index c8c6f48..51337b1 100644
--- a/include/haproxy/quic_conn-t.h
+++ b/include/haproxy/quic_conn-t.h
@@ -616,7 +616,7 @@
 /* Flags at connection level */
 #define QUIC_FL_CONN_ANTI_AMPLIFICATION_REACHED  (1U << 0)
 #define QUIC_FL_CONN_SPIN_BIT                    (1U << 1) /* Spin bit set by remote peer */
-#define QUIC_FL_CONN_POST_HANDSHAKE_FRAMES_BUILT (1U << 2)
+#define QUIC_FL_CONN_NEED_POST_HANDSHAKE_FRMS    (1U << 2) /* HANDSHAKE_DONE must be sent */
 #define QUIC_FL_CONN_LISTENER                    (1U << 3)
 #define QUIC_FL_CONN_ACCEPT_REGISTERED           (1U << 4)
 #define QUIC_FL_CONN_TX_MUX_CONTEXT              (1U << 5) /* sending in progress from the MUX layer */
diff --git a/src/quic_conn.c b/src/quic_conn.c
index 2d75e99..74a5f32 100644
--- a/src/quic_conn.c
+++ b/src/quic_conn.c
@@ -2459,6 +2459,7 @@
 		/* I/O callback switch */
 		qc->wait_event.tasklet->process = quic_conn_app_io_cb;
 		if (qc_is_listener(ctx->qc)) {
+			qc->flags |= QUIC_FL_CONN_NEED_POST_HANDSHAKE_FRMS;
 			qc->state = QUIC_HS_ST_CONFIRMED;
 			/* The connection is ready to be accepted. */
 			quic_accept_push_qc(qc);
@@ -4141,7 +4142,7 @@
 	}
 
 	LIST_SPLICE(&qel->pktns->tx.frms, &frm_list);
-	qc->flags |= QUIC_FL_CONN_POST_HANDSHAKE_FRAMES_BUILT;
+	qc->flags &= ~QUIC_FL_CONN_NEED_POST_HANDSHAKE_FRMS;
 
 	ret = 1;
  leave:
@@ -4780,6 +4781,14 @@
 	TRACE_ENTER(QUIC_EV_CONN_TXPKT, qc);
 	BUG_ON(qc->mux_state != QC_MUX_READY); /* Only MUX can uses this function so it must be ready. */
 
+	/* Try to send post handshake frames first unless on 0-RTT. */
+	if ((qc->flags & QUIC_FL_CONN_NEED_POST_HANDSHAKE_FRMS) &&
+	    qc->state >= QUIC_HS_ST_COMPLETE) {
+		struct quic_enc_level *qel = &qc->els[QUIC_TLS_ENC_LEVEL_APP];
+		quic_build_post_handshake_frames(qc);
+		qc_send_app_pkts(qc, &qel->pktns->tx.frms);
+	}
+
 	TRACE_STATE("preparing data (from MUX)", QUIC_EV_CONN_TXPKT, qc);
 	qc->flags |= QUIC_FL_CONN_TX_MUX_CONTEXT;
 	ret = qc_send_app_pkts(qc, frms);
@@ -4967,6 +4976,15 @@
 	if (qc_test_fd(qc))
 		qc_rcv_buf(qc);
 
+	/* Prepare post-handshake frames
+	 * - after connection is instantiated (accept is done)
+	 * - handshake state is completed (may not be the case here in 0-RTT)
+	 */
+	if ((qc->flags & QUIC_FL_CONN_NEED_POST_HANDSHAKE_FRMS) && qc->conn &&
+	    qc->state >= QUIC_HS_ST_COMPLETE) {
+		quic_build_post_handshake_frames(qc);
+	}
+
 	/* Retranmissions */
 	if (qc->flags & QUIC_FL_CONN_RETRANS_NEEDED) {
 		TRACE_STATE("retransmission needed", QUIC_EV_CONN_IO_CB, qc);
@@ -5090,10 +5108,6 @@
 
 	st = qc->state;
 	if (st >= QUIC_HS_ST_COMPLETE) {
-		if (!(qc->flags & QUIC_FL_CONN_POST_HANDSHAKE_FRAMES_BUILT) &&
-		    !quic_build_post_handshake_frames(qc))
-			goto out;
-
 		if (!(qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE].tls_ctx.flags &
 		           QUIC_FL_TLS_SECRETS_DCD)) {
 			/* Discard the Handshake keys. */