]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: quic: send CONNECTION_CLOSE on released MUX
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 13 Jul 2022 13:18:16 +0000 (15:18 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 15 Jul 2022 13:56:13 +0000 (15:56 +0200)
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.

src/xprt_quic.c

index d736dcbaa1c23a92ed09988ea9586f7a9085dead..8e3c5c22c683e4565024a52f36265ea835764196 100644 (file)
@@ -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 <qc> if the MUX has been released
+ * and all STREAM data are acknowledged. The MUX is responsible to have set
+ * <qc.err> 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 <stream> 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);
 }