MEDIUM: mux-quic: adjust transport layer error handling
Following previous patch, error notification from quic_conn has been
adjusted to rely on standard connection flags. Most notably, CO_FL_ERROR
on the connection instance when a fatal error is detected.
Check for CO_FL_ERROR is implemented by qc_send(). If set the new flag
QC_CF_ERR_CONN will be set for the MUX instance. This flag is similar to
the local error flag and will abort most of the futur processing. To
ensure stream upper layer is also notified, qc_wake_some_streams()
called by qc_process() will put the stream on error if this new flag is
set.
This should be backported up to 2.7.
diff --git a/include/haproxy/mux_quic-t.h b/include/haproxy/mux_quic-t.h
index 327d347..b501c90 100644
--- a/include/haproxy/mux_quic-t.h
+++ b/include/haproxy/mux_quic-t.h
@@ -33,6 +33,7 @@
#define QC_CF_BLK_MFCTL 0x00000004 /* sending blocked due to connection flow-control */
#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 */
struct qcc {
struct connection *conn;
diff --git a/src/mux_quic.c b/src/mux_quic.c
index 45efb79..01698f0 100644
--- a/src/mux_quic.c
+++ b/src/mux_quic.c
@@ -216,7 +216,7 @@
* - error detected locally
* - MUX timeout expired or unset
*/
- if (qcc->conn->flags & CO_FL_ERROR || qcc->flags & QC_CF_ERRL_DONE ||
+ if (qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL_DONE) ||
!qcc->task) {
return 1;
}
@@ -1900,6 +1900,12 @@
* apply for STREAM frames.
*/
+ /* Check for transport error. */
+ if (qcc->flags & QC_CF_ERR_CONN || qcc->conn->flags & CO_FL_ERROR) {
+ TRACE_DEVEL("connection on error", QMUX_EV_QCC_SEND, qcc->conn);
+ goto out;
+ }
+
/* Check for locally detected connection error. */
if (qcc->flags & QC_CF_ERRL) {
/* Prepare a CONNECTION_CLOSE if not already done. */
@@ -2039,6 +2045,12 @@
}
out:
+ if (qcc->conn->flags & CO_FL_ERROR && !(qcc->flags & QC_CF_ERR_CONN)) {
+ TRACE_ERROR("error reported by transport layer",
+ QMUX_EV_QCC_SEND, qcc->conn);
+ qcc->flags |= QC_CF_ERR_CONN;
+ }
+
TRACE_LEAVE(QMUX_EV_QCC_SEND, qcc->conn);
return total;
}
@@ -2134,7 +2146,7 @@
{
TRACE_ENTER(QMUX_EV_QCC_END, qcc->conn);
- if (qcc->flags & QC_CF_ERRL) {
+ if (qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL)) {
TRACE_DATA("connection on error", QMUX_EV_QCC_END, qcc->conn);
goto out;
}
@@ -2183,7 +2195,7 @@
if (!qcs_sc(qcs))
continue;
- if (qcc->conn->flags & CO_FL_ERROR || qcc->flags & QC_CF_ERRL) {
+ if (qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL)) {
TRACE_POINT(QMUX_EV_QCC_WAKE, qcc->conn, qcs);
se_fl_set_error(qcs->sd);
qcs_alert(qcs);
@@ -2243,7 +2255,7 @@
}
/* Report error if set on stream endpoint layer. */
- if (qcc->flags & QC_CF_ERRL)
+ if (qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL))
qc_wake_some_streams(qcc);
out:
@@ -2571,8 +2583,8 @@
qcc_rm_sc(qcc);
- if (!qcs_is_close_local(qcs) && !(qcc->conn->flags & CO_FL_ERROR) &&
- !(qcc->flags & QC_CF_ERRL)) {
+ if (!qcs_is_close_local(qcs) &&
+ !(qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL))) {
TRACE_STATE("remaining data, detaching qcs", QMUX_EV_STRM_END, qcc->conn, qcs);
qcs->flags |= QC_SF_DETACH;
qcc_refresh_timeout(qcc);
@@ -2671,7 +2683,7 @@
BUG_ON_HOT(qcs->flags & QC_SF_DETACH);
/* Report error if set on stream endpoint layer. */
- if (qcs->qcc->flags & QC_CF_ERRL) {
+ if (qcs->qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL)) {
se_fl_set(qcs->sd, SE_FL_ERROR);
TRACE_DEVEL("connection in error", QMUX_EV_STRM_SEND, qcs->qcc->conn, qcs);
goto end;
@@ -2766,7 +2778,7 @@
if (qcs->flags & QC_SF_UNKNOWN_PL_LENGTH) {
/* Close stream with a FIN STREAM frame. */
- if (!(qcc->flags & QC_CF_ERRL)) {
+ 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;
@@ -2775,7 +2787,7 @@
}
else {
/* RESET_STREAM necessary. */
- if (!(qcc->flags & QC_CF_ERRL))
+ if (!(qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL)))
qcc_reset_stream(qcs, 0);
se_fl_set_error(qcs->sd);
}