MINOR: quic: support CONNECTION_CLOSE_APP emission

Complete quic-conn API for error reporting. A new parameter <app> is
defined in the function quic_set_connection_close(). This will transform
the frame into a CONNECTION_CLOSE_APP type.

This type of frame will be generated by the applicative layer, h3 or
hq-interop for the moment. A new function qcc_emit_cc_app() is exported
by the MUX layer for them.
diff --git a/include/haproxy/mux_quic.h b/include/haproxy/mux_quic.h
index c9bb8bf..1d71abd 100644
--- a/include/haproxy/mux_quic.h
+++ b/include/haproxy/mux_quic.h
@@ -25,6 +25,7 @@
 void qcs_notify_send(struct qcs *qcs);
 void qcs_consume(struct qcs *qcs, uint64_t bytes);
 
+void qcc_emit_cc_app(struct qcc *qcc, int err);
 int qcc_recv(struct qcc *qcc, uint64_t id, uint64_t len, uint64_t offset,
              char fin, char *data);
 int qcc_recv_max_data(struct qcc *qcc, uint64_t max);
diff --git a/include/haproxy/xprt_quic-t.h b/include/haproxy/xprt_quic-t.h
index a85b47c..5231583 100644
--- a/include/haproxy/xprt_quic-t.h
+++ b/include/haproxy/xprt_quic-t.h
@@ -684,10 +684,12 @@
 #define QUIC_FL_CONN_POST_HANDSHAKE_FRAMES_BUILT (1U << 2)
 #define QUIC_FL_CONN_LISTENER                    (1U << 3)
 #define QUIC_FL_CONN_ACCEPT_REGISTERED           (1U << 4)
+/* gap here */
 #define QUIC_FL_CONN_IDLE_TIMER_RESTARTED_AFTER_READ (1U << 6)
 #define QUIC_FL_CONN_RETRANS_NEEDED              (1U << 7)
 #define QUIC_FL_CONN_RETRANS_OLD_DATA            (1U << 8)
 #define QUIC_FL_CONN_TLS_ALERT                   (1U << 9)
+#define QUIC_FL_CONN_APP_ALERT                   (1U << 10) /* A connection error of type CONNECTION_CLOSE_APP must be emitted. */
 #define QUIC_FL_CONN_NOTIFY_CLOSE                (1U << 27) /* MUX notified about quic-conn imminent closure (idle-timeout or CONNECTION_CLOSE emission/reception) */
 #define QUIC_FL_CONN_EXP_TIMER                   (1U << 28) /* timer has expired, quic-conn can be freed */
 #define QUIC_FL_CONN_CLOSING                     (1U << 29)
diff --git a/include/haproxy/xprt_quic.h b/include/haproxy/xprt_quic.h
index ac6a8e2..fa13c4c 100644
--- a/include/haproxy/xprt_quic.h
+++ b/include/haproxy/xprt_quic.h
@@ -1255,7 +1255,7 @@
 
 void chunk_frm_appendf(struct buffer *buf, const struct quic_frame *frm);
 
-void quic_set_connection_close(struct quic_conn *qc, int err);
+void quic_set_connection_close(struct quic_conn *qc, int err, int app);
 void quic_set_tls_alert(struct quic_conn *qc, int alert);
 int quic_set_app_ops(struct quic_conn *qc, const unsigned char *alpn, size_t alpn_len);
 struct task *quic_lstnr_dghdlr(struct task *t, void *ctx, unsigned int state);
diff --git a/src/mux_quic.c b/src/mux_quic.c
index d744f91..91dffdf 100644
--- a/src/mux_quic.c
+++ b/src/mux_quic.c
@@ -106,7 +106,7 @@
  */
 static void qcc_emit_cc(struct qcc *qcc, int err)
 {
-	quic_set_connection_close(qcc->conn->handle.qc, err);
+	quic_set_connection_close(qcc->conn->handle.qc, err, 0);
 	qcc->flags |= QC_CF_CC_EMIT;
 	tasklet_wakeup(qcc->wait_event.tasklet);
 }
@@ -445,6 +445,16 @@
 	return 0;
 }
 
+/* Emit a CONNECTION_CLOSE_APP with error <err>. Reserved for application error
+ * code. This will interrupt all future send/receive operations.
+ */
+void qcc_emit_cc_app(struct qcc *qcc, int err)
+{
+	quic_set_connection_close(qcc->conn->handle.qc, err, 1);
+	qcc->flags |= QC_CF_CC_EMIT;
+	tasklet_wakeup(qcc->wait_event.tasklet);
+}
+
 /* Handle a new STREAM frame for stream with id <id>. Payload is pointed by
  * <data> with length <len> and represents the offset <offset>. <fin> is set if
  * the QUIC frame FIN bit is set.
diff --git a/src/xprt_quic.c b/src/xprt_quic.c
index acb90bf..b081c88 100644
--- a/src/xprt_quic.c
+++ b/src/xprt_quic.c
@@ -1109,20 +1109,23 @@
 /* Prepare the emission of CONNECTION_CLOSE with error <err>. All send/receive
  * activity for <qc> will be interrupted.
  */
-void quic_set_connection_close(struct quic_conn *qc, int err)
+void quic_set_connection_close(struct quic_conn *qc, int err, int app)
 {
 	if (qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE)
 		return;
 
 	qc->err_code = err;
 	qc->flags |= QUIC_FL_CONN_IMMEDIATE_CLOSE;
+
+	if (app)
+		qc->flags |= QUIC_FL_CONN_APP_ALERT;
 }
 
 /* Set <alert> TLS alert as QUIC CRYPTO_ERROR error */
 void quic_set_tls_alert(struct quic_conn *qc, int alert)
 {
 	HA_ATOMIC_DEC(&qc->prx_counters->conn_opening);
-	quic_set_connection_close(qc, QC_ERR_CRYPTO_ERROR | alert);
+	quic_set_connection_close(qc, QC_ERR_CRYPTO_ERROR | alert, 0);
 	qc->flags |= QUIC_FL_CONN_TLS_ALERT;
 	TRACE_PROTO("Alert set", QUIC_EV_CONN_SSLDATA, qc);
 }
@@ -5985,7 +5988,7 @@
 	size_t len, len_sz, len_frms, padding_len;
 	struct quic_frame frm = { .type = QUIC_FT_CRYPTO, };
 	struct quic_frame ack_frm = { .type = QUIC_FT_ACK, };
-	struct quic_frame cc_frm = { . type = QUIC_FT_CONNECTION_CLOSE, };
+	struct quic_frame cc_frm = { };
 	size_t ack_frm_len, head_len;
 	int64_t rx_largest_acked_pn;
 	int add_ping_frm;
@@ -6084,9 +6087,10 @@
 		len += QUIC_TLS_TAG_LEN;
 	/* CONNECTION_CLOSE frame */
 	if (cc) {
-		struct quic_connection_close *cc = &cc_frm.connection_close;
+		cc_frm.type = qc->flags & QUIC_FL_CONN_APP_ALERT ?
+		  QUIC_FT_CONNECTION_CLOSE_APP : QUIC_FT_CONNECTION_CLOSE;
 
-		cc->error_code = qc->err_code;
+		cc_frm.connection_close.error_code = qc->err_code;
 		len += qc_frm_len(&cc_frm);
 	}
 	add_ping_frm = 0;