]> git.kaiwu.me - haproxy.git/commitdiff
MEDIUM: quic/mux-quic: adjust app-ops install
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 3 Mar 2026 08:47:29 +0000 (09:47 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 3 Mar 2026 15:22:57 +0000 (16:22 +0100)
This patch reworks the installation of app-ops layer by QUIC MUX.
Previously, app_ops field was stored directly into the quic_conn
structure. Then the MUX reused it directly during its qmux_init().

This patch removes app_ops field from quic_conn and replaces it with a
copy of the negotiated ALPN. By using quic_alpn_to_app_ops(), it ensures
it remains compatible with a known application layer.

On the MUX layer, qcc_install_app_ops() now uses the standard
conn_get_alpn() to retrieve the ALPN from the transport layer. This is
done via the newly defined <get_alpn> QUIC xprt callback.

This new architecture should be cleaner as it better highlights the
responsibility of each layers in the ALPN/app negotiation.

include/haproxy/mux_quic-t.h
include/haproxy/mux_quic.h
include/haproxy/quic_conn-t.h
include/haproxy/quic_conn.h
src/h3.c
src/hq_interop.c
src/mux_quic.c
src/quic_conn.c
src/quic_ssl.c
src/ssl_sock.c
src/xprt_quic.c

index aa7b17706d35e1c34c02143cc0e9c1f569877f24..44f0ceb18451cd959b87d112900f5b6a4865b06c 100644 (file)
@@ -200,6 +200,8 @@ enum qcc_app_ops_close_side {
 
 /* QUIC application layer operations */
 struct qcc_app_ops {
+       const char *alpn;
+
        /* Initialize <qcc> connection app context. */
        int (*init)(struct qcc *qcc);
        /* Finish connection initialization if prelude required. */
index b44ec2d6b71f1e546cedcbf47c47cf8197ddc593..2c808629781f021bbf905cfc798ad330f113d3e0 100644 (file)
@@ -91,7 +91,7 @@ static inline char *qcs_st_to_str(enum qcs_state st)
        }
 }
 
-int qcc_install_app_ops(struct qcc *qcc, const struct qcc_app_ops *app_ops);
+int qcc_install_app_ops(struct qcc *qcc);
 
 /* Register <qcs> stream for http-request timeout. If the stream is not yet
  * attached in the configured delay, qcc timeout task will be triggered. This
index 3baad3fc1952fabeff8daf3e12c75c3742458894..0a7ce3951ffa03ae48a40f271d7f8513eb6236d4 100644 (file)
@@ -400,6 +400,8 @@ struct quic_conn {
 
        struct eb_root streams_by_id; /* qc_stream_desc tree */
 
+       const char *alpn;
+
        /* MUX */
        struct qcc *qcc;
        struct task *timer_task;
@@ -408,7 +410,6 @@ struct quic_conn {
        /* Handshake expiration date */
        unsigned int hs_expire;
 
-       const struct qcc_app_ops *app_ops;
        /* Callback to close any stream after MUX closure - set by the MUX itself */
        int (*strm_reject)(struct list *out, uint64_t stream_id);
 
index c34681e7d4fabf9021b104092e6d76293e7d08e6..3424389a243659d5e55111dca08f23c288a618d6 100644 (file)
@@ -204,7 +204,7 @@ static inline void *qc_counters(enum obj_type *o, const struct stats_module *m)
 void chunk_frm_appendf(struct buffer *buf, const struct quic_frame *frm);
 void quic_set_connection_close(struct quic_conn *qc, const struct quic_err err);
 void quic_set_tls_alert(struct quic_conn *qc, int alert);
-int quic_set_app_ops(struct quic_conn *qc, const char *alpn, int alpn_len);
+int qc_register_alpn(struct quic_conn *qc, const char *alpn, int alpn_len);
 int qc_check_dcid(struct quic_conn *qc, unsigned char *dcid, size_t dcid_len);
 
 void qc_notify_err(struct quic_conn *qc);
index ebf1830e628a6dc436992bd7c7cfa809c2cf48dc..0b7c53303a3774687d6886714aa147069c893b0f 100644 (file)
--- a/src/h3.c
+++ b/src/h3.c
@@ -3398,6 +3398,8 @@ int h3_reject(struct list *out, uint64_t id)
 
 /* HTTP/3 application layer operations */
 const struct qcc_app_ops h3_ops = {
+       .alpn        = "h3",
+
        .init        = h3_init,
        .finalize    = h3_finalize,
        .attach      = h3_attach,
index cf1cf7c83afb243be5228942af85f870e8f85297..8268e62a726fcf46c43437ce3471b0cf7c323d48 100644 (file)
@@ -324,6 +324,8 @@ static int hq_interop_attach(struct qcs *qcs, void *conn_ctx)
 }
 
 const struct qcc_app_ops hq_interop_ops = {
+       .alpn       = "hq-interop",
+
        .rcv_buf    = hq_interop_rcv_buf,
        .snd_buf    = hq_interop_snd_buf,
        .nego_ff    = hq_interop_nego_ff,
index 512d0eda9b8eac9a1ab9f1dbc464d33616f56ac5..5b672b174a788fef3d79b084109be3bd13b2e9e2 100644 (file)
@@ -1688,26 +1688,38 @@ void qcc_abort_stream_read(struct qcs *qcs)
        TRACE_LEAVE(QMUX_EV_QCC_NEW, qcc->conn, qcs);
 }
 
-/* Install the <app_ops> applicative layer of a QUIC connection on mux <qcc>.
+/* Install the applicative layer of a QUIC connection on mux <qcc>.
  * Returns 0 on success else non-zero.
  */
-int qcc_install_app_ops(struct qcc *qcc, const struct qcc_app_ops *app_ops)
+int qcc_install_app_ops(struct qcc *qcc)
 {
-       TRACE_ENTER(QMUX_EV_QCC_NEW, qcc->conn);
+       struct connection *conn = qcc->conn;
+       const struct qcc_app_ops *app_ops;
+       const char *alpn;
+       int alpn_len;
+
+       TRACE_ENTER(QMUX_EV_QCC_NEW, conn);
+
+       if (!conn_get_alpn(conn, &alpn, &alpn_len))
+               goto err;
+
+       app_ops = quic_alpn_to_app_ops(alpn, alpn_len);
+       if (!app_ops)
+               goto err;
 
        if (app_ops->init && !app_ops->init(qcc)) {
-               TRACE_ERROR("application layer install error", QMUX_EV_QCC_NEW, qcc->conn);
+               TRACE_ERROR("application layer install error", QMUX_EV_QCC_NEW, conn);
                goto err;
        }
 
-       TRACE_PROTO("application layer installed", QMUX_EV_QCC_NEW, qcc->conn);
+       TRACE_PROTO("application layer installed", QMUX_EV_QCC_NEW, conn);
        qcc->app_ops = app_ops;
 
-       TRACE_LEAVE(QMUX_EV_QCC_NEW, qcc->conn);
+       TRACE_LEAVE(QMUX_EV_QCC_NEW, conn);
        return 0;
 
  err:
-       TRACE_LEAVE(QMUX_EV_QCC_NEW, qcc->conn);
+       TRACE_LEAVE(QMUX_EV_QCC_NEW, conn);
        return 1;
 }
 
@@ -3740,7 +3752,7 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
        /* Register conn as app_ops may use it. */
        qcc->conn = conn;
 
-       if (qcc_install_app_ops(qcc, conn->handle.qc->app_ops)) {
+       if (qcc_install_app_ops(qcc)) {
                TRACE_PROTO("Cannot install app layer", QMUX_EV_QCC_NEW|QMUX_EV_QCC_ERR, conn);
                goto err;
        }
index 2d87a42a94ee48c17f7827b900ada45a779298a8..26c67c7d418bdec654076b3932419f9d8e552ad1 100644 (file)
@@ -267,21 +267,23 @@ void quic_set_tls_alert(struct quic_conn *qc, int alert)
        TRACE_LEAVE(QUIC_EV_CONN_SSLALERT, qc);
 }
 
-/* Set the application for <qc> QUIC connection.
- * Return 1 if succeeded, 0 if not.
+/* Register the negotiated TLS ALPN <alpn> of length <alpn_len> for <qc> QUIC
+ * connection. This checks that the protocol is compatible with the QUIC stack.
+ *
+ * Returns 1 on success else 0.
  */
-int quic_set_app_ops(struct quic_conn *qc, const char *alpn, int alpn_len)
+int qc_register_alpn(struct quic_conn *qc, const char *alpn, int alpn_len)
 {
-       if (!(qc->app_ops = quic_alpn_to_app_ops(alpn, alpn_len)))
+       const struct qcc_app_ops *app_ops;
+
+       if (!(app_ops = quic_alpn_to_app_ops(alpn, alpn_len)))
                return 0;
 
+       qc->alpn = app_ops->alpn;
        return 1;
-
 }
 
 /* Try to reuse <alpn> ALPN and <etps> early transport parameters.
- * This function also sets the application operations calling
- * quic_set_app_ops().
  * Return 1 if succeeded, 0 if not.
  */
 int quic_reuse_srv_params(struct quic_conn *qc,
@@ -292,7 +294,7 @@ int quic_reuse_srv_params(struct quic_conn *qc,
 
        TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
 
-       if (!alpn || !quic_set_app_ops(qc, alpn, strlen(alpn)))
+       if (!alpn || !qc_register_alpn(qc, alpn, strlen(alpn)))
                goto err;
 
        qc_early_transport_params_reuse(qc, &qc->tx.params, etps);
@@ -1124,6 +1126,7 @@ struct quic_conn *qc_new_conn(void *target,
        LIST_INIT(&qc->rx.pkt_list);
 
        qc->streams_by_id = EB_ROOT_UNIQUE;
+       qc->alpn = NULL;
 
        /* Required to call free_quic_conn_cids() from quic_conn_release() */
        qc->cids = NULL;
@@ -1143,7 +1146,6 @@ struct quic_conn *qc_new_conn(void *target,
        qc->xprt_ctx = NULL;
        qc->conn = conn;
        qc->qcc = NULL;
-       qc->app_ops = NULL;
        qc->strm_reject = NULL;
        qc->path = NULL;
 
index 88f3268d0a300d1a4dd11831f7fe06734ff810b4..710fef4e49fed952e3979c6354d127fc9c497b54 100644 (file)
@@ -1004,7 +1004,7 @@ int qc_ssl_do_hanshake(struct quic_conn *qc, struct ssl_sock_ctx *ctx)
 
                /* Check the alpn could be negotiated */
                if (!qc_is_back(qc)) {
-                       if (!qc->app_ops) {
+                       if (!qc->alpn) {
                                TRACE_ERROR("No negotiated ALPN", QUIC_EV_CONN_IO_CB, qc, &state);
                                quic_set_tls_alert(qc, SSL_AD_NO_APPLICATION_PROTOCOL);
                                goto err;
@@ -1016,7 +1016,7 @@ int qc_ssl_do_hanshake(struct quic_conn *qc, struct ssl_sock_ctx *ctx)
 
                        qc->conn->flags &= ~(CO_FL_SSL_WAIT_HS | CO_FL_WAIT_L6_CONN);
                        if (!ssl_sock_get_alpn(qc->conn, ctx, &alpn, &alpn_len) ||
-                           !quic_set_app_ops(qc, alpn, alpn_len)) {
+                           !qc_register_alpn(qc, alpn, alpn_len)) {
                                TRACE_ERROR("No negotiated ALPN", QUIC_EV_CONN_IO_CB, qc, &state);
                                quic_set_tls_alert(qc, SSL_AD_NO_APPLICATION_PROTOCOL);
                                goto err;
index ff266f0ea60f7483b310b6b5dcda30a6390cf2f3..aeb408ffa14bda62ea8c5ad1573948b3a3e4c03a 100644 (file)
@@ -2242,7 +2242,7 @@ static int ssl_sock_advertise_alpn_protos(SSL *s, const unsigned char **out,
        }
 
 #ifdef USE_QUIC
-       if (qc && !quic_set_app_ops(qc, (const char *)*out, *outlen)) {
+       if (qc && !qc_register_alpn(qc, (const char *)*out, *outlen)) {
                quic_set_tls_alert(qc, SSL_AD_NO_APPLICATION_PROTOCOL);
                return SSL_TLSEXT_ERR_NOACK;
        }
index ca687f021488535aa1a0c1e0544182939e667431..4670f6a865408814678eeaf8df77800ac36d4074 100644 (file)
@@ -239,6 +239,22 @@ static void qc_xprt_dump_info(struct buffer *msg, const struct connection *conn)
        quic_dump_qc_info(msg, conn->handle.qc);
 }
 
+static int qc_get_alpn(const struct connection *conn, void *xprt_ctx, const char **str, int *len)
+{
+       struct quic_conn *qc = conn->handle.qc;
+       int ret = 0;
+
+       TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
+       if (qc->alpn) {
+               *str = qc->alpn;
+               *len = strlen(qc->alpn);
+               ret = 1;
+       }
+
+       TRACE_LEAVE(QUIC_EV_CONN_NEW, qc);
+       return ret;
+}
+
 /* transport-layer operations for QUIC connections. */
 static struct xprt_ops ssl_quic = {
        .close    = quic_close,
@@ -250,7 +266,7 @@ static struct xprt_ops ssl_quic = {
        .destroy_bind_conf = ssl_sock_destroy_bind_conf,
        .prepare_srv = ssl_sock_prepare_srv_ctx,
        .destroy_srv = ssl_sock_free_srv_ctx,
-       .get_alpn = ssl_sock_get_alpn,
+       .get_alpn = qc_get_alpn,
        .get_ssl_sock_ctx = qc_get_ssl_sock_ctx,
        .dump_info = qc_xprt_dump_info,
        .name     = "QUIC",