]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: mux-quic/h3: emit SETTINGS via MUX tasklet handler
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 17 Feb 2025 15:00:03 +0000 (16:00 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 19 Feb 2025 10:03:40 +0000 (11:03 +0100)
Previously, QUIC MUX application layer was installed and initialized via
MUX init. However, the latter stage involve I/O operations, for example
when using HTTP/3 with the emission of a SETTINGS frame.

Change this to prevent any I/O operations during MUX init. As such,
finalize app_ops callback is now called during the first invokation of
qcc_io_send(), in the context of MUX tasklet. To implement this, a new
application state value is added, to detect the transition from NULL to
INIT stage.

include/haproxy/mux_quic-t.h
src/h3.c
src/mux_quic.c
src/qmux_trace.c

index e9e3bf5e72cb53fb09d266343f75820f98ce9c3d..f0c3577081eca7df692cbdae77dc4b8445b9d358 100644 (file)
@@ -33,6 +33,7 @@ enum qcs_type {
 };
 
 enum qcc_app_st {
+       QCC_APP_ST_NULL,
        QCC_APP_ST_INIT,
        QCC_APP_ST_SHUT,
 } __attribute__((packed));
index 06d954ebc952a1caf17b83b6eaf69365f71f223e..04aeada53f933999e48948f43c7f1a226e91c406 100644 (file)
--- a/src/h3.c
+++ b/src/h3.c
@@ -1488,7 +1488,7 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
        return -1;
 }
 
-/* Function used to emit stream data from <qcs> control uni-stream.
+/* Emit SETTINGS frame on <qcs> control uni-stream.
  *
  * On success return the number of sent bytes. A negative code is used on
  * error.
@@ -2406,7 +2406,8 @@ static int h3_init(struct qcc *qcc)
        return 0;
 }
 
-/* Initialize H3 control stream and prepare SETTINGS emission.
+/* Open control stream for <ctx> HTTP/3 connection and schedule a SETTINGS
+ * frame emission on it.
  *
  * Returns 0 on success else non-zero.
  */
@@ -2429,6 +2430,13 @@ static int h3_finalize(void *ctx)
        qcs_send_metadata(qcs);
        h3c->ctrl_strm = qcs;
 
+       /* RFC 9114 7.2.4.2. Initialization
+        *
+        * Endpoints MUST NOT require any data to be
+        * received from the peer prior to sending the SETTINGS frame;
+        * settings MUST be sent as soon as the transport is ready to
+        * send data.
+        */
        if (h3_control_send(qcs, h3c) < 0) {
                qcc_set_error(qcc, H3_ERR_INTERNAL_ERROR, 1);
                goto err;
index 50f2665333fdcea5d131722d9d77737a863be002..e6938bdfff80d00aa6fab6ea777c17a6425e8832 100644 (file)
@@ -1474,28 +1474,13 @@ int qcc_install_app_ops(struct qcc *qcc, const struct qcc_app_ops *app_ops)
        TRACE_ENTER(QMUX_EV_QCC_NEW, qcc->conn);
 
        if (app_ops->init && !app_ops->init(qcc)) {
-               TRACE_ERROR("app ops init error", QMUX_EV_QCC_NEW, qcc->conn);
+               TRACE_ERROR("application layer install error", QMUX_EV_QCC_NEW, qcc->conn);
                goto err;
        }
 
-       TRACE_PROTO("application layer initialized", QMUX_EV_QCC_NEW, qcc->conn);
+       TRACE_PROTO("application layer installed", QMUX_EV_QCC_NEW, qcc->conn);
        qcc->app_ops = app_ops;
 
-       /* RFC 9114 7.2.4.2. Initialization
-        *
-        * Endpoints MUST NOT require any data to be
-        * received from the peer prior to sending the SETTINGS frame;
-        * settings MUST be sent as soon as the transport is ready to
-        * send data.
-        */
-       if (qcc->app_ops->finalize) {
-               if (qcc->app_ops->finalize(qcc->ctx)) {
-                       TRACE_ERROR("app ops finalize error", QMUX_EV_QCC_NEW, qcc->conn);
-                       goto err;
-               }
-               tasklet_wakeup(qcc->wait_event.tasklet);
-       }
-
        TRACE_LEAVE(QMUX_EV_QCC_NEW, qcc->conn);
        return 0;
 
@@ -2480,6 +2465,29 @@ static void qcc_wakeup_pacing(struct qcc *qcc)
        ++qcc->tx.paced_sent_ctr;
 }
 
+/* Finalize <qcc> app layer initialization with I/O operations.
+ *
+ * Returns 0 on success else non-zero.
+ */
+static int qcc_app_init(struct qcc *qcc)
+{
+       TRACE_ENTER(QMUX_EV_QCC_SEND, qcc->conn);
+
+       if (qcc->app_ops->finalize && qcc->app_ops->finalize(qcc->ctx)) {
+               TRACE_ERROR("app ops finalize error", QMUX_EV_QCC_NEW, qcc->conn);
+               goto err;
+       }
+
+       qcc->app_st = QCC_APP_ST_INIT;
+
+       TRACE_LEAVE(QMUX_EV_QCC_SEND, qcc->conn);
+       return 0;
+
+ err:
+       TRACE_DEVEL("leaving on error", QMUX_EV_QCC_SEND, qcc->conn);
+       return 1;
+}
+
 /* Proceed to sending. Loop through all available streams for the <qcc>
  * instance and try to send as much as possible.
  *
@@ -2530,6 +2538,11 @@ static int qcc_io_send(struct qcc *qcc)
                goto out;
        }
 
+       if (qcc->app_st < QCC_APP_ST_INIT) {
+               if (qcc_app_init(qcc))
+                       goto out;
+       }
+
        if (!LIST_ISEMPTY(&qcc->lfctl.frms)) {
                if (qcc_send_frames(qcc, &qcc->lfctl.frms, 0)) {
                        TRACE_DEVEL("flow-control frames rejected by transport, aborting send", QMUX_EV_QCC_SEND, qcc->conn);
@@ -3042,7 +3055,7 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
        conn->ctx = qcc;
        qcc->nb_hreq = qcc->nb_sc = 0;
        qcc->flags = 0;
-       qcc->app_st = QCC_APP_ST_INIT;
+       qcc->app_st = QCC_APP_ST_NULL;
        qcc->glitches = 0;
        qcc->err = quic_err_transport(QC_ERR_NO_ERROR);
 
index 3788e3d91032c32081bdd36783a027d7eacdc413..4998cbd00f02dd77c106844a4645779e21914b8b 100644 (file)
@@ -133,6 +133,7 @@ INITCALL1(STG_REGISTER, trace_register_source, TRACE_SOURCE);
 static char *qcc_app_st_to_str(const enum qcc_app_st st)
 {
        switch (st) {
+       case QCC_APP_ST_NULL: return "NULL";
        case QCC_APP_ST_INIT: return "INIT";
        case QCC_APP_ST_SHUT: return "SHUT";
        default:              return "";