]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: mux-quic: do not reuse connection if app already shut
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 23 Jul 2025 07:41:46 +0000 (09:41 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 23 Jul 2025 13:45:18 +0000 (15:45 +0200)
QUIC connection graceful closure is performed in two steps. First, the
application layer is closed. In the context of HTTP/3, this is done with
a GOAWAY frame emission, which forbids opening of new streams. Then the
whole connection is terminated via CONNECTION_CLOSE which is the final
emitted frame.

This commit ensures that when app layer is shut for a backend
connection, this connection is removed from either idle or avail server
tree. The objective is to prevent stream layer to try to reuse a
connection if no new stream can be attached on it.

New BUG_ON checks are inserted in qmux_strm_attach() and h3_attach() to
ensure that this assertion is always true.

src/h3.c
src/mux_quic.c

index e9444c76f0796f1fc944c181fecc6a567d1facf6..d88d9bcd29641ad13196edd7e9c7f29bbf911648 100644 (file)
--- a/src/h3.c
+++ b/src/h3.c
@@ -3005,6 +3005,9 @@ static int h3_attach(struct qcs *qcs, void *conn_ctx)
         */
        if (h3c->flags & H3_CF_GOAWAY_SENT && qcs->id >= h3c->id_goaway &&
            quic_stream_is_bidi(qcs->id)) {
+               /* Local stack should not attached stream on a closed connection. */
+               BUG_ON(quic_stream_is_local(qcs->qcc, qcs->id));
+
                TRACE_STATE("close stream outside of goaway range", H3_EV_H3S_NEW, qcs->qcc->conn, qcs);
                qcc_abort_stream_read(qcs);
                qcc_reset_stream(qcs, H3_ERR_REQUEST_REJECTED);
index cad1644421d4fbf8684f066f2e5f997e2cd2ae83..87d981154dada42b4fe2e6f06857b3c8cfdfe98b 100644 (file)
@@ -3195,6 +3195,10 @@ static void qcc_shutdown(struct qcc *qcc)
        if (!(qcc->conn->handle.qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE))
                qcc->conn->handle.qc->err = qcc->err;
 
+       /* A connection is not reusable if app layer is closed. */
+       if (qcc->flags & QC_CF_IS_BACK)
+               conn_delete_from_tree(qcc->conn);
+
  out:
        qcc->app_st = QCC_APP_ST_SHUT;
        TRACE_LEAVE(QMUX_EV_QCC_END, qcc->conn);
@@ -3719,6 +3723,9 @@ static int qmux_strm_attach(struct connection *conn, struct sedesc *sd, struct s
         */
        BUG_ON(!qcc_fctl_avail_streams(qcc, 1));
 
+       /* Connnection should not be reused if already on error/closed. */
+       BUG_ON(qcc->flags & QC_CF_ERRL || qcc->app_st >= QCC_APP_ST_SHUT);
+
        qcs = qcc_init_stream_local(qcc, 1);
        if (!qcs) {
                TRACE_DEVEL("leaving on error", QMUX_EV_QCS_NEW, qcc->conn);
@@ -3771,7 +3778,8 @@ static void qmux_strm_detach(struct sedesc *sd)
        qcs_destroy(qcs);
 
        /* Backend connection can be reused unless it is already on error/closed. */
-       if (qcc->flags & QC_CF_IS_BACK && !qcc_is_dead(qcc)) {
+       if ((qcc->flags & QC_CF_IS_BACK) && !qcc_is_dead(qcc) &&
+           qcc->app_st == QCC_APP_ST_INIT) {
                if (!(conn->flags & CO_FL_PRIVATE)) {
                        if (!qcc->nb_sc) {
                                TRACE_DEVEL("prepare for idle connection reuse", QMUX_EV_STRM_END, conn);