diff --git a/include/haproxy/xprt_quic-t.h b/include/haproxy/xprt_quic-t.h index fbe2ff773..e2999a571 100644 --- a/include/haproxy/xprt_quic-t.h +++ b/include/haproxy/xprt_quic-t.h @@ -664,6 +664,7 @@ enum qc_mux_state { #define QUIC_FL_CONN_LISTENER (1U << 3) #define QUIC_FL_CONN_ACCEPT_REGISTERED (1U << 4) #define QUIC_FL_CONN_IDLE_TIMER_RESTARTED_AFTER_READ (1U << 6) +#define QUIC_FL_CONN_NOTIFY_CLOSE (1U << 27) /* MUX notified about quic-conn imminent closure (idle-timeout or CONNECTION_CLOSE emission/reception) */ #define QUIC_FL_CONN_EXP_TIMER (1U << 28) /* timer has expired, quic-conn can be freed */ #define QUIC_FL_CONN_CLOSING (1U << 29) #define QUIC_FL_CONN_DRAINING (1U << 30) diff --git a/include/haproxy/xprt_quic.h b/include/haproxy/xprt_quic.h index 9bf71e906..94488b04a 100644 --- a/include/haproxy/xprt_quic.h +++ b/include/haproxy/xprt_quic.h @@ -1251,5 +1251,7 @@ int qc_send_app_pkts(struct quic_conn *qc, struct list *frms); struct qc_stream_desc *qc_stream_desc_new(uint64_t id, void *ctx); void qc_stream_desc_release(struct qc_stream_desc *stream, struct quic_conn *qc); +void qc_notify_close(struct quic_conn *qc); + #endif /* USE_QUIC */ #endif /* _HAPROXY_XPRT_QUIC_H */ diff --git a/src/mux_quic.c b/src/mux_quic.c index cdac9a6f5..8b85c3314 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -1255,6 +1255,9 @@ static int qc_wake(struct connection *conn) if (unlikely(prx->flags & (PR_FL_DISABLED|PR_FL_STOPPED))) goto release; + if (conn->qc->flags & QUIC_FL_CONN_NOTIFY_CLOSE) + qcc->conn->flags |= (CO_FL_SOCK_RD_SH|CO_FL_SOCK_WR_SH); + qc_send(qcc); qc_wake_some_streams(qcc); diff --git a/src/xprt_quic.c b/src/xprt_quic.c index 65414fde0..9dd280a18 100644 --- a/src/xprt_quic.c +++ b/src/xprt_quic.c @@ -2590,6 +2590,7 @@ static int qc_parse_pkt_frms(struct quic_rx_packet *pkt, struct ssl_sock_ctx *ct */ qc_idle_timer_do_rearm(qc); qc->flags |= QUIC_FL_CONN_DRAINING|QUIC_FL_CONN_IMMEDIATE_CLOSE; + qc_notify_close(qc); } break; case QUIC_FT_HANDSHAKE_DONE: @@ -3045,6 +3046,8 @@ int qc_send_ppkts(struct qring *qr, struct ssl_sock_ctx *ctx) if (!(qc->flags & QUIC_FL_CONN_CLOSING) && (pkt->flags & QUIC_FL_TX_PACKET_CC)) { qc->flags |= QUIC_FL_CONN_CLOSING; + qc_notify_close(qc); + /* RFC 9000 10.2. Immediate Close: * The closing and draining connection states exist to ensure * that connections close cleanly and that delayed or reordered @@ -4115,6 +4118,11 @@ static struct task *qc_idle_timer_task(struct task *t, void *ctx, unsigned int s { struct quic_conn *qc = ctx; + /* Notify the MUX before settings QUIC_FL_CONN_EXP_TIMER or the MUX + * might free the quic-conn too early via quic_close(). + */ + qc_notify_close(qc); + /* If the MUX is still alive, keep the quic-conn. The MUX is * responsible to call quic_close to release it. */ @@ -5925,6 +5933,19 @@ void qc_stream_desc_release(struct qc_stream_desc *stream, eb64_insert(&qc->streams_by_id, &stream->by_id); } +/* Notify the MUX layer if alive about an imminent close of . */ +void qc_notify_close(struct quic_conn *qc) +{ + if (qc->flags & QUIC_FL_CONN_NOTIFY_CLOSE) + return; + + qc->flags |= QUIC_FL_CONN_NOTIFY_CLOSE; + + /* wake up the MUX */ + if (qc->mux_state == QC_MUX_READY && qc->conn->mux->wake) + qc->conn->mux->wake(qc->conn); +} + /* Function to automatically activate QUIC traces on stdout. * Activated via the compilation flag -DENABLE_QUIC_STDOUT_TRACES. * Main use for now is in the docker image for QUIC interop testing.