MEDIUM: mux-quic: implement shutw
Implement mux_ops shutw operation for QUIC mux. A RESET_STREAM is
emitted unless the stream is already closed due to all data or
RESET_STREAM already transmitted.
This operation is notably useful when upper stream layer wants to close
the connection early due to an error.
This was tested by using a HTTP server which listens with PROXY protocol
support. The corresponding server line on haproxy configuration
deliberately not specify send-proxy. This causes the server to close
abruptly the connection. Without this patch, nothing was done on the QUIC
stream which was kept open until the whole connection is closed. Now, a
proper RESET_STREAM is emitted to report the error.
This should be backported up to 2.7.
diff --git a/include/haproxy/qmux_trace.h b/include/haproxy/qmux_trace.h
index c612c96..0bc89a3 100644
--- a/include/haproxy/qmux_trace.h
+++ b/include/haproxy/qmux_trace.h
@@ -34,17 +34,19 @@
{ .mask = QMUX_EV_STRM_RECV, .name = "strm_recv", .desc = "receiving data for stream" },
#define QMUX_EV_STRM_SEND (1ULL << 11)
{ .mask = QMUX_EV_STRM_SEND, .name = "strm_send", .desc = "sending data for stream" },
-#define QMUX_EV_STRM_END (1ULL << 12)
+#define QMUX_EV_STRM_SHUT (1ULL << 12)
+ { .mask = QMUX_EV_STRM_SHUT, .name = "strm_shut", .desc = "stream shutdown" },
+#define QMUX_EV_STRM_END (1ULL << 13)
{ .mask = QMUX_EV_STRM_END, .name = "strm_end", .desc = "detaching app-layer stream" },
-#define QMUX_EV_SEND_FRM (1ULL << 13)
+#define QMUX_EV_SEND_FRM (1ULL << 14)
{ .mask = QMUX_EV_SEND_FRM, .name = "send_frm", .desc = "sending QUIC frame" },
/* special event dedicated to qcs_xfer_data */
-#define QMUX_EV_QCS_XFER_DATA (1ULL << 14)
+#define QMUX_EV_QCS_XFER_DATA (1ULL << 15)
{ .mask = QMUX_EV_QCS_XFER_DATA, .name = "qcs_xfer_data", .desc = "qcs_xfer_data" },
/* special event dedicated to qcs_build_stream_frm */
-#define QMUX_EV_QCS_BUILD_STRM (1ULL << 15)
+#define QMUX_EV_QCS_BUILD_STRM (1ULL << 16)
{ .mask = QMUX_EV_QCS_BUILD_STRM, .name = "qcs_build_stream_frm", .desc = "qcs_build_stream_frm" },
-#define QMUX_EV_PROTO_ERR (1ULL << 16)
+#define QMUX_EV_PROTO_ERR (1ULL << 17)
{ .mask = QMUX_EV_PROTO_ERR, .name = "proto_err", .desc = "protocol error" },
{ }
};
diff --git a/src/mux_quic.c b/src/mux_quic.c
index 8e48493..a5e443f 100644
--- a/src/mux_quic.c
+++ b/src/mux_quic.c
@@ -2269,6 +2269,25 @@
return 1;
}
+static void qc_shutw(struct stconn *sc, enum co_shw_mode mode)
+{
+ struct qcs *qcs = __sc_mux_strm(sc);
+
+ TRACE_ENTER(QMUX_EV_STRM_SHUT, qcs->qcc->conn, qcs);
+
+ /* If QC_SF_FIN_STREAM is not set and stream is not closed locally, it
+ * means that upper layer reported an early closure. A RESET_STREAM is
+ * necessary if not already scheduled.
+ */
+
+ if (!qcs_is_close_local(qcs) &&
+ !(qcs->flags & (QC_SF_FIN_STREAM|QC_SF_TO_RESET))) {
+ qcc_reset_stream(qcs, 0);
+ se_fl_set_error(qcs->sd);
+ }
+
+ TRACE_LEAVE(QMUX_EV_STRM_SHUT, qcs->qcc->conn, qcs);
+}
/* for debugging with CLI's "show sess" command. May emit multiple lines, each
* new one being prefixed with <pfx>, if <pfx> is not NULL, otherwise a single
@@ -2306,6 +2325,7 @@
.subscribe = qc_subscribe,
.unsubscribe = qc_unsubscribe,
.wake = qc_wake,
+ .shutw = qc_shutw,
.show_sd = qc_show_sd,
.flags = MX_FL_HTX|MX_FL_NO_UPG|MX_FL_FRAMED,
.name = "QUIC",