BUG/MEDIUM: quic: support wait-for-handshake

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

(cherry picked from commit 0918c41ef63964a986c627d20b8a1324de639cc2)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
This commit is contained in:
Amaury Denoyelle 2024-10-15 17:37:00 +02:00 committed by Christopher Faulet
parent 35dbd3ea0f
commit 05658956ae
2 changed files with 42 additions and 0 deletions

View File

@ -35,6 +35,7 @@ enum qcs_type {
#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 */
struct qcc {
struct connection *conn;

View File

@ -2464,6 +2464,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.
@ -2474,6 +2512,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.