From: Amaury Denoyelle Date: Wed, 13 Jul 2022 13:18:16 +0000 (+0200) Subject: MEDIUM: quic: send CONNECTION_CLOSE on released MUX X-Git-Tag: v2.7-dev2~58 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=240b1b108b2292ef73fea68ce273be9d26c33cfa;p=thirdparty%2Fhaproxy.git MEDIUM: quic: send CONNECTION_CLOSE on released MUX Send a CONNECTION_CLOSE if the MUX has been released and all STREAM data are acknowledged. This is useful to prevent a client from trying to use a connection which have the upper layer closed. To implement this a new function qc_check_close_on_released_mux() has been added. It is called on QUIC MUX release notification and each time a qc_stream_desc has been released. This commit is associated with the previous one : MINOR: mux-quic/h3: schedule CONNECTION_CLOSE on app release Both patches are required to prevent the risk of browsers stuck on webpage loading if MUX has been released. On CONNECTION_CLOSE reception, the client will reopen a new QUIC connection. --- diff --git a/src/xprt_quic.c b/src/xprt_quic.c index d736dcbaa1..8e3c5c22c6 100644 --- a/src/xprt_quic.c +++ b/src/xprt_quic.c @@ -1444,6 +1444,23 @@ void qc_release_frm(struct quic_conn *qc, struct quic_frame *frm) pool_free(pool_head_quic_frame, frm); } +/* Schedule a CONNECTION_CLOSE emission on if the MUX has been released + * and all STREAM data are acknowledged. The MUX is responsible to have set + * before as it is reused for the CONNECTION_CLOSE frame. + * + * TODO this should also be called on lost packet detection + */ +static void qc_check_close_on_released_mux(struct quic_conn *qc) +{ + struct ssl_sock_ctx *ctx = qc->xprt_ctx; + + if (qc->mux_state == QC_MUX_RELEASED && eb_is_empty(&qc->streams_by_id)) { + /* Reuse errcode which should have been previously set by the MUX on release. */ + quic_set_connection_close(qc, qc->err); + tasklet_wakeup(ctx->wait_event.tasklet); + } +} + /* Remove from the acknowledged frames. * * Returns 1 if at least one frame was removed else 0. @@ -1479,8 +1496,10 @@ static int quic_stream_try_to_consume(struct quic_conn *qc, * has been freed. with the stream frames tree. Nothing to do * anymore in here. */ - if (!stream) + if (!stream) { + qc_check_close_on_released_mux(qc); return 1; + } frm_node = eb64_next(frm_node); eb64_delete(&strm->offset); @@ -1537,6 +1556,7 @@ static inline void qc_treat_acked_tx_frm(struct quic_conn *qc, /* no need to continue if stream freed. */ TRACE_PROTO("stream released and freed", QUIC_EV_CONN_ACKSTRM, qc); qc_release_frm(qc, frm); + qc_check_close_on_released_mux(qc); break; } @@ -4148,6 +4168,8 @@ static void quic_close(struct connection *conn, void *xprt_ctx) return; } + qc_check_close_on_released_mux(qc); + TRACE_LEAVE(QUIC_EV_CONN_CLOSE, qc); }