]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: quic: support wait-for-handshake
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 15 Oct 2024 15:37:00 +0000 (17:37 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 16 Oct 2024 09:51:35 +0000 (11:51 +0200)
wait-for-handshake http-request action was completely ineffective with
QUIC protocol. This commit implements its support for QUIC.

QUIC MUX layer is extended to support wait-for-handshake. A new function
qcc_handle_wait_for_hs() is executed during qcc_io_process(). It detects
if MUX processing occurs after underlying QUIC handshake completion. If
this is the case, it indicates that early data may be received. As such,
connection is flagged with CO_FL_EARLY_SSL_HS, which is necessary to
block stream processing on wait-for-handshake action.

After this, qcc subscribs on quic_conn layer for RECV notification. This
is used to detect QUIC handshake completion. Thus,
qcc_handle_wait_for_hs() can be reexecuted one last time, to remove
CO_FL_EARLY_SSL_HS and notify every streams flagged as
SE_FL_WAIT_FOR_HS.

This patch must be backported up to 2.6, after a mandatory period of
observation. Note that it relies on the backport of the two previous
patches :
- MINOR: quic: notify connection layer on handshake completion
- BUG/MINOR: stream: unblock stream on wait-for-handshake completion

include/haproxy/mux_quic-t.h
src/mux_quic.c

index cb9e27befcc1fdd800e78ea5e227e60d8b1fdcf6..fcdd38b9bd14ee700461fc9cf270647ce967a8a2 100644 (file)
@@ -216,6 +216,7 @@ struct qcc_app_ops {
 #define QC_CF_CONN_FULL 0x00000008 /* no stream buffers available on connection */
 #define QC_CF_APP_SHUT  0x00000010 /* Application layer shutdown done. */
 #define QC_CF_ERR_CONN  0x00000020 /* fatal error reported by transport layer */
+#define QC_CF_WAIT_FOR_HS 0x00000040 /* QUIC handshake has been completed */
 
 /* This function is used to report flags in debugging tools. Please reflect
  * below any single-bit flag addition above in the same order via the
index e87b97e4727373cdf652166ad60e027cf5864914..ac2c491e3d9b28582eac0be7a67cf5987eaeb3c1 100644 (file)
@@ -2565,6 +2565,44 @@ static int qcc_wake_some_streams(struct qcc *qcc)
        return 0;
 }
 
+/* Checks whether QUIC handshake is still active or not. This is necessary to
+ * mark that connection may convey early data to delay stream processing if
+ * wait-for-handshake is active. On handshake completion, any SE_FL_WAIT_FOR_HS
+ * streams are woken up to restart their processing.
+ */
+static void qcc_wait_for_hs(struct qcc *qcc)
+{
+       struct connection *conn = qcc->conn;
+       struct quic_conn *qc = conn->handle.qc;
+       struct eb64_node *node;
+       struct qcs *qcs;
+
+       if (qc->state < QUIC_HS_ST_COMPLETE) {
+               if (!(conn->flags & CO_FL_EARLY_SSL_HS)) {
+                       TRACE_STATE("flag connection with early data", QMUX_EV_QCC_WAKE, conn);
+                       conn->flags |= CO_FL_EARLY_SSL_HS;
+                       /* subscribe for handshake completion */
+                       conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_RECV,
+                                             &qcc->wait_event);
+               }
+       }
+       else {
+               if (conn->flags & CO_FL_EARLY_SSL_HS) {
+                       TRACE_STATE("mark early data as ready", QMUX_EV_QCC_WAKE, conn);
+                       conn->flags &= ~CO_FL_EARLY_SSL_HS;
+               }
+               qcc->flags |= QC_CF_WAIT_FOR_HS;
+
+               node = eb64_first(&qcc->streams_by_id);
+               while (node) {
+                       qcs = container_of(node, struct qcs, by_id);
+                       if (se_fl_test(qcs->sd, SE_FL_WAIT_FOR_HS))
+                               qcs_notify_recv(qcs);
+                       node = eb64_next(node);
+               }
+       }
+}
+
 /* Conduct operations which should be made for <qcc> connection after
  * input/output. Most notably, closed streams are purged which may leave the
  * connection has ready to be released.
@@ -2575,6 +2613,9 @@ static int qcc_io_process(struct qcc *qcc)
 {
        qcc_purge_streams(qcc);
 
+       if (!(qcc->flags & QC_CF_WAIT_FOR_HS))
+               qcc_wait_for_hs(qcc);
+
        /* Check if a soft-stop is in progress.
         *
         * TODO this is relevant for frontend connections only.