]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: mux-quic: implement attach for new streams on backend side
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 18 Jun 2025 07:59:50 +0000 (09:59 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 18 Jun 2025 15:25:27 +0000 (17:25 +0200)
Implement attach and avail_streams mux-ops callbacks, which are used on
backend side for connection reuse.

Attach operation is used to initiate new streams on the connection
outside of the first one. It simply relies on qcc_init_stream_local() to
instantiate a new QCS instance, which is immediately linked to its
stream data layer.

Outside of attach, it is also necessary to implement avail_streams so
that the stream layer will try to initiate connection reuse. This method
reports the number of bidirectional streams which can still be opened
for the QUIC connection. It depends directly to the flow-control value
advertised by the peer. Thus, this ensures that attach won't cause any
flow control violation.

src/mux_quic.c

index 3a19fddfdf20b7847b1c78892f9728a583741d23..17fe8d6bea58b063b8720fe0a6f2d83e97f28e4f 100644 (file)
@@ -3122,6 +3122,20 @@ static int qcc_io_recv(struct qcc *qcc)
        return 0;
 }
 
+/* Calculate the number of bidirectional streams which can still be opened for
+ * <conn> connection. This depends on flow-control set by the peer.
+ *
+ * Returns the value which is a positive integer or 0 if no new stream
+ * currently available.
+ */
+static int qmux_avail_streams(struct connection *conn)
+{
+       struct server *srv = __objt_server(conn->target);
+       struct qcc *qcc = conn->ctx;
+
+       BUG_ON(srv->max_reuse >= 0); /* TODO ensure max-reuse is enforced. */
+       return qcc_fctl_avail_streams(qcc, 1);
+}
 
 /* Release all streams which have their transfer operation achieved. */
 static void qcc_purge_streams(struct qcc *qcc)
@@ -3687,6 +3701,37 @@ static void qmux_destroy(void *ctx)
        TRACE_LEAVE(QMUX_EV_QCC_END);
 }
 
+static int qmux_strm_attach(struct connection *conn, struct sedesc *sd, struct session *sess)
+{
+       struct qcs *qcs;
+       struct qcc *qcc = conn->ctx;
+
+       TRACE_ENTER(QMUX_EV_QCS_NEW, conn);
+
+       /* Flow control limit on bidi streams should already have
+        * been checked by a prior qmux_avail_streams() invokation.
+        */
+       BUG_ON(!qcc_fctl_avail_streams(qcc, 1));
+
+       qcs = qcc_init_stream_local(qcc, 1);
+       if (!qcs) {
+               TRACE_DEVEL("leaving on error", QMUX_EV_QCS_NEW, qcc->conn);
+               return -1;
+       }
+
+       if (sc_attach_mux(sd->sc, qcs, conn)) {
+               TRACE_DEVEL("leaving on error", QMUX_EV_QCS_NEW, qcc->conn);
+               qcs_free(qcs);
+               return -1;
+       }
+
+       qcs->sd = sd->sc->sedesc;
+       qcc->nb_sc++;
+
+       TRACE_LEAVE(QMUX_EV_QCS_NEW, conn);
+       return 0;
+}
+
 static void qmux_strm_detach(struct sedesc *sd)
 {
        struct qcs *qcs = sd->se;
@@ -4209,6 +4254,8 @@ static const struct mux_ops qmux_ops = {
        .subscribe   = qmux_strm_subscribe,
        .unsubscribe = qmux_strm_unsubscribe,
        .wake        = qmux_wake,
+       .avail_streams = qmux_avail_streams,
+       .attach      = qmux_strm_attach,
        .shut        = qmux_strm_shut,
        .ctl         = qmux_ctl,
        .sctl        = qmux_sctl,