MINOR: mux-quic: do not reuse connection if app already shut

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.
This commit is contained in:
Amaury Denoyelle 2025-07-23 09:41:46 +02:00
parent 3217835b1d
commit 00d668549e
2 changed files with 12 additions and 1 deletions

View File

@ -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 && if (h3c->flags & H3_CF_GOAWAY_SENT && qcs->id >= h3c->id_goaway &&
quic_stream_is_bidi(qcs->id)) { 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); TRACE_STATE("close stream outside of goaway range", H3_EV_H3S_NEW, qcs->qcc->conn, qcs);
qcc_abort_stream_read(qcs); qcc_abort_stream_read(qcs);
qcc_reset_stream(qcs, H3_ERR_REQUEST_REJECTED); qcc_reset_stream(qcs, H3_ERR_REQUEST_REJECTED);

View File

@ -3195,6 +3195,10 @@ static void qcc_shutdown(struct qcc *qcc)
if (!(qcc->conn->handle.qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE)) if (!(qcc->conn->handle.qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE))
qcc->conn->handle.qc->err = qcc->err; 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: out:
qcc->app_st = QCC_APP_ST_SHUT; qcc->app_st = QCC_APP_ST_SHUT;
TRACE_LEAVE(QMUX_EV_QCC_END, qcc->conn); 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)); 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); qcs = qcc_init_stream_local(qcc, 1);
if (!qcs) { if (!qcs) {
TRACE_DEVEL("leaving on error", QMUX_EV_QCS_NEW, qcc->conn); 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); qcs_destroy(qcs);
/* Backend connection can be reused unless it is already on error/closed. */ /* 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 (!(conn->flags & CO_FL_PRIVATE)) {
if (!qcc->nb_sc) { if (!qcc->nb_sc) {
TRACE_DEVEL("prepare for idle connection reuse", QMUX_EV_STRM_END, conn); TRACE_DEVEL("prepare for idle connection reuse", QMUX_EV_STRM_END, conn);