From d666d740d206b1fde607fd6ca6097fd7bf9c11bd Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Wed, 13 Jul 2022 15:15:58 +0200 Subject: [PATCH] MINOR: mux-quic: support app graceful shutdown Adjust qcc_emit_cc_app() to allow the delay of emission of a CONNECTION_CLOSE. This will only set the error code but the quic-conn layer is not flagged for immediate close. The quic-conn will be responsible to shut the connection when deemed suitable. This change will allow to implement application graceful shutdown, such as HTTP/3 with GOAWAY emission. This will allow to emit closing frames on MUX release. Once all work is done at the lower layer, the quic-conn should emit a CONNECTION_CLOSE with the registered error code. --- include/haproxy/mux_quic.h | 2 +- src/h3.c | 14 +++++++------- src/mux_quic.c | 21 ++++++++++++++++----- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/include/haproxy/mux_quic.h b/include/haproxy/mux_quic.h index dbb6fe6fa..0ad94cf32 100644 --- a/include/haproxy/mux_quic.h +++ b/include/haproxy/mux_quic.h @@ -21,7 +21,7 @@ int qcs_subscribe(struct qcs *qcs, int event_type, struct wait_event *es); void qcs_notify_recv(struct qcs *qcs); void qcs_notify_send(struct qcs *qcs); -void qcc_emit_cc_app(struct qcc *qcc, int err); +void qcc_emit_cc_app(struct qcc *qcc, int err, int immediate); void qcc_reset_stream(struct qcs *qcs, int err); int qcc_recv(struct qcc *qcc, uint64_t id, uint64_t len, uint64_t offset, char fin, char *data); diff --git a/src/h3.c b/src/h3.c index 074016699..7fb5f9489 100644 --- a/src/h3.c +++ b/src/h3.c @@ -168,7 +168,7 @@ static ssize_t h3_init_uni_stream(struct h3c *h3c, struct qcs *qcs, switch (type) { case H3_UNI_S_T_CTRL: if (h3c->flags & H3_CF_UNI_CTRL_SET) { - qcc_emit_cc_app(qcs->qcc, H3_STREAM_CREATION_ERROR); + qcc_emit_cc_app(qcs->qcc, H3_STREAM_CREATION_ERROR, 1); return -1; } h3c->flags |= H3_CF_UNI_CTRL_SET; @@ -182,7 +182,7 @@ static ssize_t h3_init_uni_stream(struct h3c *h3c, struct qcs *qcs, case H3_UNI_S_T_QPACK_DEC: if (h3c->flags & H3_CF_UNI_QPACK_DEC_SET) { - qcc_emit_cc_app(qcs->qcc, H3_STREAM_CREATION_ERROR); + qcc_emit_cc_app(qcs->qcc, H3_STREAM_CREATION_ERROR, 1); return -1; } h3c->flags |= H3_CF_UNI_QPACK_DEC_SET; @@ -192,7 +192,7 @@ static ssize_t h3_init_uni_stream(struct h3c *h3c, struct qcs *qcs, case H3_UNI_S_T_QPACK_ENC: if (h3c->flags & H3_CF_UNI_QPACK_ENC_SET) { - qcc_emit_cc_app(qcs->qcc, H3_STREAM_CREATION_ERROR); + qcc_emit_cc_app(qcs->qcc, H3_STREAM_CREATION_ERROR, 1); return -1; } h3c->flags |= H3_CF_UNI_QPACK_ENC_SET; @@ -621,7 +621,7 @@ static ssize_t h3_decode_qcs(struct qcs *qcs, struct buffer *b, int fin) total += hlen; if (!h3_is_frame_valid(h3c, qcs, ftype)) { - qcc_emit_cc_app(qcs->qcc, H3_FRAME_UNEXPECTED); + qcc_emit_cc_app(qcs->qcc, H3_FRAME_UNEXPECTED, 1); return -1; } @@ -643,7 +643,7 @@ static ssize_t h3_decode_qcs(struct qcs *qcs, struct buffer *b, int fin) * excessive decompressed size. */ if (flen > QC_S_RX_BUF_SZ) { - qcc_emit_cc_app(qcs->qcc, H3_EXCESSIVE_LOAD); + qcc_emit_cc_app(qcs->qcc, H3_EXCESSIVE_LOAD, 1); return -1; } break; @@ -665,7 +665,7 @@ static ssize_t h3_decode_qcs(struct qcs *qcs, struct buffer *b, int fin) * only close the stream once RESET_STREAM is * supported. */ - qcc_emit_cc_app(qcs->qcc, h3c->err); + qcc_emit_cc_app(qcs->qcc, h3c->err, 1); return -1; } break; @@ -679,7 +679,7 @@ static ssize_t h3_decode_qcs(struct qcs *qcs, struct buffer *b, int fin) case H3_FT_SETTINGS: ret = h3_parse_settings_frm(qcs->qcc->ctx, b, flen); if (ret < 0) { - qcc_emit_cc_app(qcs->qcc, h3c->err); + qcc_emit_cc_app(qcs->qcc, h3c->err, 1); return -1; } h3c->flags |= H3_CF_SETTINGS_RECV; diff --git a/src/mux_quic.c b/src/mux_quic.c index 065ffa57f..658a97437 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -667,13 +667,24 @@ static int qcc_decode_qcs(struct qcc *qcc, struct qcs *qcs) } /* Emit a CONNECTION_CLOSE_APP with error . Reserved for application error - * code. This will interrupt all future send/receive operations. + * code. To close the connection right away, set : this is useful + * when dealing with a connection fatal error. Else a graceful shutdown will be + * conducted : the error-code is only registered. The lower layer is + * responsible to close the connection when deemed suitable. Note that in this + * case the error code might be overwritten if an immediate close is requested + * in the interval. */ -void qcc_emit_cc_app(struct qcc *qcc, int err) +void qcc_emit_cc_app(struct qcc *qcc, int err, int immediate) { - quic_set_connection_close(qcc->conn->handle.qc, quic_err_app(err)); - qcc->flags |= QC_CF_CC_EMIT; - tasklet_wakeup(qcc->wait_event.tasklet); + if (immediate) { + quic_set_connection_close(qcc->conn->handle.qc, quic_err_app(err)); + qcc->flags |= QC_CF_CC_EMIT; + tasklet_wakeup(qcc->wait_event.tasklet); + } + else { + /* Only register the error code for graceful shutdown. */ + qcc->conn->handle.qc->err = quic_err_app(err); + } } /* Prepare for the emission of RESET_STREAM on with error code . */