diff --git a/include/haproxy/mux_quic-t.h b/include/haproxy/mux_quic-t.h index e9e3bf5e7..f0c357708 100644 --- a/include/haproxy/mux_quic-t.h +++ b/include/haproxy/mux_quic-t.h @@ -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)); diff --git a/src/h3.c b/src/h3.c index 06d954ebc..04aeada53 100644 --- 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 control uni-stream. +/* Emit SETTINGS frame on 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 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; diff --git a/src/mux_quic.c b/src/mux_quic.c index 50f266533..e6938bdff 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -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 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 * 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); diff --git a/src/qmux_trace.c b/src/qmux_trace.c index 3788e3d91..4998cbd00 100644 --- a/src/qmux_trace.c +++ b/src/qmux_trace.c @@ -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 "";