diff --git a/include/haproxy/mux_quic-t.h b/include/haproxy/mux_quic-t.h index 02db4e8a5..6a0bfe6cb 100644 --- a/include/haproxy/mux_quic-t.h +++ b/include/haproxy/mux_quic-t.h @@ -214,6 +214,12 @@ enum qcc_app_ops_close_side { QCC_APP_OPS_CLOSE_SIDE_WR /* Write channel closed (STOP_SENDING received). */ }; +enum qcc_app_ops_lclose_mode { + QCC_APP_OPS_LCLO_MODE_NORMAL, + QCC_APP_OPS_LCLO_MODE_ABORT, + QCC_APP_OPS_LCLO_MODE_KILL_CONN, +}; + /* QUIC application layer operations */ struct qcc_app_ops { const char *alpn; @@ -236,8 +242,10 @@ struct qcc_app_ops { size_t (*nego_ff)(struct qcs *qcs, size_t count); size_t (*done_ff)(struct qcs *qcs); - /* Notify about stream closure. */ + /* Notify about stream remote closure. */ int (*close)(struct qcs *qcs, enum qcc_app_ops_close_side side); + /* Notify about stream upper layer closure. */ + void (*lclose)(struct qcs *qcs, enum qcc_app_ops_lclose_mode mode); /* Free stream app context. */ void (*detach)(struct qcs *qcs); diff --git a/src/h3.c b/src/h3.c index a899370ce..8d633bfc7 100644 --- a/src/h3.c +++ b/src/h3.c @@ -3095,6 +3095,33 @@ static int h3_close(struct qcs *qcs, enum qcc_app_ops_close_side side) return 0; } +static void h3_lclose(struct qcs *qcs, enum qcc_app_ops_lclose_mode mode) +{ + TRACE_ENTER(H3_EV_H3S_END, qcs->qcc->conn, qcs); + + switch (mode) { + case QCC_APP_OPS_LCLO_MODE_NORMAL: + /* Close stream with FIN. This can only be performed if at + * least HEADERS frame was emitted, or else some clients close + * the connection with H3_FRAME_UNEXPECTED. + */ + if (qcs->tx.fc.off_soft) { + qcs->flags |= QC_SF_FIN_STREAM; + qcc_send_stream(qcs, 0, 0); + } + else { + qcc_reset_stream(qcs, 0, se_tevt_type_shutw); + } + break; + + default: + qcc_reset_stream(qcs, 0, 0); + break; + } + + TRACE_LEAVE(H3_EV_H3S_END, qcs->qcc->conn, qcs); +} + /* Allocates HTTP/3 stream context relative to . If the operation cannot * be performed, an error is returned and context is unchanged. * @@ -3500,6 +3527,7 @@ const struct qcc_app_ops h3_ops = { .nego_ff = h3_nego_ff, .done_ff = h3_done_ff, .close = h3_close, + .lclose = h3_lclose, .detach = h3_detach, .shutdown = h3_shutdown, .inc_err_cnt = h3_stats_inc_err_cnt, diff --git a/src/hq_interop.c b/src/hq_interop.c index 8268e62a7..35a0e3358 100644 --- a/src/hq_interop.c +++ b/src/hq_interop.c @@ -323,6 +323,20 @@ static int hq_interop_attach(struct qcs *qcs, void *conn_ctx) return 0; } +static void hq_interop_lclose(struct qcs *qcs, enum qcc_app_ops_lclose_mode mode) +{ + switch (mode) { + case QCC_APP_OPS_LCLO_MODE_NORMAL: + qcs->flags |= QC_SF_FIN_STREAM; + qcc_send_stream(qcs, 0, 0); + break; + + default: + qcc_reset_stream(qcs, 0, 0); + break; + } +} + const struct qcc_app_ops hq_interop_ops = { .alpn = "hq-interop", @@ -331,4 +345,5 @@ const struct qcc_app_ops hq_interop_ops = { .nego_ff = hq_interop_nego_ff, .done_ff = hq_interop_done_ff, .attach = hq_interop_attach, + .lclose = hq_interop_lclose, }; diff --git a/src/mux_quic.c b/src/mux_quic.c index 1bf13dc0f..4208029aa 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -4591,25 +4591,12 @@ static void qmux_strm_shut(struct stconn *sc, unsigned int mode, struct se_abort /* Early closure reported if QC_SF_FIN_STREAM not yet set. */ if (!qcs_is_close_local(qcs) && !(qcs->flags & (QC_SF_FIN_STREAM|QC_SF_TO_RESET))) { - - /* Close stream with FIN if length unknown and some data are - * ready to be/already transmitted. - * TODO select closure method on app proto layer - */ - if (qcs->flags & QC_SF_UNKNOWN_PL_LENGTH && - qcs->tx.fc.off_soft) { - if (!(qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL))) { - TRACE_STATE("set FIN STREAM", - QMUX_EV_STRM_SHUT, qcc->conn, qcs); - qcs->flags |= QC_SF_FIN_STREAM; - qcc_send_stream(qcs, 0, 0); - } - } - else { - /* RESET_STREAM necessary. */ - qcc_reset_stream(qcs, 0, 0); - } - + if (qcs->flags & QC_SF_UNKNOWN_PL_LENGTH) + qcc->app_ops->lclose(qcs, QCC_APP_OPS_LCLO_MODE_NORMAL); + else if (se_fl_test(qcs->sd, SE_FL_KILL_CONN)) + qcc->app_ops->lclose(qcs, QCC_APP_OPS_LCLO_MODE_KILL_CONN); + else + qcc->app_ops->lclose(qcs, QCC_APP_OPS_LCLO_MODE_ABORT); tasklet_wakeup(qcc->wait_event.tasklet); }