]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: mux_quic/h3: report termination events at connection level
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 20 Apr 2026 12:06:21 +0000 (14:06 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 27 Apr 2026 13:02:58 +0000 (15:02 +0200)
Implement termination event log in QUIC MUX layer at the connection
level. The objective is to reuse similar codes already used on H2.

Most of the error values are reported via qcc_set_error() which have now
an extra argument to specify the term event code. This is in particular
used in H3 application protocol code.

Also, qmux_ctl() now implements MUX_CTL_TEVTS to report the current
connection termination code to the upper layer. Callback qmux_sctl()
display the connection term event code on MUX_SCTL_DBG_STR.

include/haproxy/mux_quic-t.h
include/haproxy/mux_quic.h
src/h3.c
src/mux_quic.c
src/qmux_trace.c
src/qpack-dec.c

index 6cf488e2ce674e4266d573dbaa98dd751d3250db..02db4e8a5aa6ab1d44d50a489a2dddbe4268396e 100644 (file)
@@ -46,6 +46,8 @@ struct qcc {
        enum qcc_app_st app_st; /* application layer state */
        int glitches;   /* total number of glitches on this connection */
 
+       uint32_t term_evts_log;  /* termination events log */
+
        /* flow-control fields set by us enforced on our side. */
        struct {
                struct list frms; /* prepared frames related to flow-control  */
index 31332ea5cb685330dcef204b7f7d1d57c5a17916..c2cad153655acc132333f04a1a93027b0d10408e 100644 (file)
@@ -20,7 +20,8 @@
                _qcc_report_glitch(qcc, inc);           \
        })
 
-void qcc_set_error(struct qcc *qcc, int err, int app);
+void qcc_set_error(struct qcc *qcc, int err, int app, int tevt);
+void qcc_report_term_evt(struct qcc *qcc, enum muxc_term_event_type type);
 int _qcc_report_glitch(struct qcc *qcc, int inc);
 int qcc_fctl_avail_streams(const struct qcc *qcc, int bidi);
 struct qcs *qcc_init_stream_local(struct qcc *qcc, int bidi);
index c15f873c6070e79abf34a22317f4c923c12be482..2c3726843871dc965fac8edac3c4a1e41c10aa9c 100644 (file)
--- a/src/h3.c
+++ b/src/h3.c
@@ -202,7 +202,8 @@ static ssize_t h3_init_uni_stream(struct h3c *h3c, struct qcs *qcs,
        case H3_UNI_S_T_CTRL:
                if (h3c->flags & H3_CF_UNI_CTRL_SET) {
                        TRACE_ERROR("duplicated control stream", H3_EV_H3S_NEW, qcs->qcc->conn, qcs);
-                       qcc_set_error(qcs->qcc, H3_ERR_STREAM_CREATION_ERROR, 1);
+                       qcc_set_error(qcs->qcc, H3_ERR_STREAM_CREATION_ERROR, 1,
+                                     muxc_tevt_type_proto_err);
                        qcc_report_glitch(qcs->qcc, 1);
                        goto err;
                }
@@ -218,7 +219,8 @@ 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) {
                        TRACE_ERROR("duplicated qpack decoder stream", H3_EV_H3S_NEW, qcs->qcc->conn, qcs);
-                       qcc_set_error(qcs->qcc, H3_ERR_STREAM_CREATION_ERROR, 1);
+                       qcc_set_error(qcs->qcc, H3_ERR_STREAM_CREATION_ERROR, 1,
+                                     muxc_tevt_type_proto_err);
                        qcc_report_glitch(qcs->qcc, 1);
                        goto err;
                }
@@ -230,7 +232,8 @@ 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) {
                        TRACE_ERROR("duplicated qpack encoder stream", H3_EV_H3S_NEW, qcs->qcc->conn, qcs);
-                       qcc_set_error(qcs->qcc, H3_ERR_STREAM_CREATION_ERROR, 1);
+                       qcc_set_error(qcs->qcc, H3_ERR_STREAM_CREATION_ERROR, 1,
+                                     muxc_tevt_type_proto_err);
                        qcc_report_glitch(qcs->qcc, 1);
                        goto err;
                }
@@ -1715,6 +1718,7 @@ static ssize_t h3_parse_goaway_frm(struct h3c *h3c, const struct buffer *buf,
         * establish a new connection to send additional requests.
         */
        h3c->qcc->flags |= QC_CF_CONN_SHUT;
+       qcc_report_term_evt(h3c->qcc, muxc_tevt_type_goaway_rcvd);
 
        TRACE_LEAVE(H3_EV_RX_FRAME, h3c->qcc->conn);
        return ret;
@@ -1773,7 +1777,8 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
         */
        if (h3s->type == H3S_T_CTRL && fin) {
                TRACE_ERROR("control stream closed by remote peer", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
-               qcc_set_error(qcs->qcc, H3_ERR_CLOSED_CRITICAL_STREAM, 1);
+               qcc_set_error(qcs->qcc, H3_ERR_CLOSED_CRITICAL_STREAM, 1,
+                             muxc_tevt_type_proto_err);
                qcc_report_glitch(qcs->qcc, 1);
                goto err;
        }
@@ -1790,7 +1795,8 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
 
                if (qcs_http_handle_standalone_fin(qcs)) {
                        TRACE_ERROR("cannot set EOM", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
-                       qcc_set_error(qcs->qcc, H3_ERR_INTERNAL_ERROR, 1);
+                       qcc_set_error(qcs->qcc, H3_ERR_INTERNAL_ERROR, 1,
+                                     muxc_tevt_type_internal_err);
                        goto err;
                }
 
@@ -1816,7 +1822,7 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
 
                        if ((ret = h3_check_frame_valid(h3c, qcs, ftype))) {
                                TRACE_ERROR("received an invalid frame", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
-                               qcc_set_error(qcs->qcc, ret, 1);
+                               qcc_set_error(qcs->qcc, ret, 1, muxc_tevt_type_proto_err);
                                qcc_report_glitch(qcs->qcc, 1);
                                goto err;
                        }
@@ -1857,7 +1863,8 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
                         */
                        if (flen > qmux_stream_rx_bufsz()) {
                                TRACE_ERROR("received a too big frame", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
-                               qcc_set_error(qcs->qcc, H3_ERR_EXCESSIVE_LOAD, 1);
+                               qcc_set_error(qcs->qcc, H3_ERR_EXCESSIVE_LOAD, 1,
+                                             muxc_tevt_type_other_err);
                                qcc_report_glitch(qcs->qcc, 1);
                                goto err;
                        }
@@ -1865,7 +1872,8 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
                        /* TODO extend parser to support the realignment of a frame. */
                        if (b_head(b) + b_data(b) > b_wrap(b)) {
                                TRACE_ERROR("cannot parse unaligned data frame", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
-                               qcc_set_error(qcs->qcc, H3_ERR_EXCESSIVE_LOAD, 1);
+                               qcc_set_error(qcs->qcc, H3_ERR_EXCESSIVE_LOAD, 1,
+                                             muxc_tevt_type_other_err);
                                qcc_report_glitch(qcs->qcc, 1);
                                goto err;
                        }
@@ -1926,7 +1934,7 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
                        ret = h3_parse_goaway_frm(qcs->qcc->ctx, b, flen);
                        if (ret < 0) {
                                TRACE_ERROR("error on GOAWAY parsing", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
-                               qcc_set_error(qcs->qcc, h3c->err, 1);
+                               qcc_set_error(qcs->qcc, h3c->err, 1, muxc_tevt_type_proto_err);
                                goto err;
                        }
                        break;
@@ -1940,7 +1948,7 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
                        ret = h3_parse_settings_frm(qcs->qcc->ctx, b, flen);
                        if (ret < 0) {
                                TRACE_ERROR("error on SETTINGS parsing", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
-                               qcc_set_error(qcs->qcc, h3c->err, 1);
+                               qcc_set_error(qcs->qcc, h3c->err, 1, muxc_tevt_type_proto_err);
                                goto err;
                        }
                        h3c->flags |= H3_CF_SETTINGS_RECV;
@@ -1975,12 +1983,12 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
                goto done;
        }
        else if (h3c->err) {
-               qcc_set_error(qcs->qcc, h3c->err, 1);
+               qcc_set_error(qcs->qcc, h3c->err, 1, muxc_tevt_type_proto_err);
                total = b_data(b);
                goto done;
        }
        else if (unlikely(ret < 0)) {
-               qcc_set_error(qcs->qcc, H3_ERR_INTERNAL_ERROR, 1);
+               qcc_set_error(qcs->qcc, H3_ERR_INTERNAL_ERROR, 1, muxc_tevt_type_internal_err);
                goto err;
        }
 
@@ -2915,7 +2923,7 @@ static size_t h3_snd_buf(struct qcs *qcs, struct buffer *buf, size_t count, char
 
        /* Interrupt sending on fatal error. */
        if (unlikely(ret < 0)) {
-               qcc_set_error(qcs->qcc, H3_ERR_INTERNAL_ERROR, 1);
+               qcc_set_error(qcs->qcc, H3_ERR_INTERNAL_ERROR, 1, muxc_tevt_type_internal_err);
                goto out;
        }
 
@@ -3075,7 +3083,8 @@ static int h3_close(struct qcs *qcs, enum qcc_app_ops_close_side side)
         */
        if (qcs == h3c->ctrl_strm || h3s->type == H3S_T_CTRL) {
                TRACE_ERROR("closure detected on control stream", H3_EV_H3S_END, qcs->qcc->conn, qcs);
-               qcc_set_error(qcs->qcc, H3_ERR_CLOSED_CRITICAL_STREAM, 1);
+               qcc_set_error(qcs->qcc, H3_ERR_CLOSED_CRITICAL_STREAM, 1,
+                             muxc_tevt_type_proto_err);
                qcc_report_glitch(qcs->qcc, 1);
                return 1;
        }
@@ -3257,7 +3266,7 @@ static int h3_init(struct qcc *qcc)
        return 1;
 
  fail_no_h3:
-       qcc_set_error(qcc, H3_ERR_INTERNAL_ERROR, 1);
+       qcc_set_error(qcc, H3_ERR_INTERNAL_ERROR, 1, muxc_tevt_type_internal_err);
        TRACE_DEVEL("leaving on error", H3_EV_H3C_NEW, qcc->conn);
        return 0;
 }
@@ -3288,7 +3297,8 @@ static int h3_finalize(void *ctx)
                 */
                if (qcc_fctl_avail_streams(qcc, 0) < 3) {
                        TRACE_ERROR("peer flow-control limit does not allow control stream creation", H3_EV_H3C_NEW, qcc->conn);
-                       qcc_set_error(qcc, H3_ERR_GENERAL_PROTOCOL_ERROR, 1);
+                       qcc_set_error(qcc, H3_ERR_GENERAL_PROTOCOL_ERROR, 1,
+                                     muxc_tevt_type_other_err);
                        qcc_report_glitch(qcc, 1);
                        goto err;
                }
@@ -3318,7 +3328,7 @@ static int h3_finalize(void *ctx)
         * send data.
         */
        if (h3_control_send(qcs, h3c) < 0) {
-               qcc_set_error(qcc, H3_ERR_INTERNAL_ERROR, 1);
+               qcc_set_error(qcc, H3_ERR_INTERNAL_ERROR, 1, muxc_tevt_type_internal_err);
                goto err;
        }
 
index 7757ce76fae28c896df70eda14982d84a8f2dda9..de21f8c13a9328bb73862a8256e5f7062af16e22 100644 (file)
@@ -769,9 +769,10 @@ void qcc_notify_buf(struct qcc *qcc, uint64_t free_size)
 /* A fatal error is detected locally for <qcc> connection. It should be closed
  * with a CONNECTION_CLOSE using <err> code. Set <app> to true to indicate that
  * the code must be considered as an application level error. This function
- * must not be called more than once by connection.
+ * must not be called more than once by connection. If <tevt> is non null, it
+ * is used as a connection level termination event code.
  */
-void qcc_set_error(struct qcc *qcc, int err, int app)
+void qcc_set_error(struct qcc *qcc, int err, int app, int tevt)
 {
        /* This must not be called multiple times per connection. */
        BUG_ON(qcc->flags & QC_CF_ERRL);
@@ -781,6 +782,9 @@ void qcc_set_error(struct qcc *qcc, int err, int app)
        qcc->flags |= QC_CF_ERRL;
        qcc->err = app ? quic_err_app(err) : quic_err_transport(err);
 
+       if (tevt)
+               qcc_report_term_evt(qcc, tevt);
+
        /* TODO
         * Ensure qcc_io_send() will be conducted to convert QC_CF_ERRL in
         * QC_CF_ERRL_DONE with CONNECTION_CLOSE frame emission. This may be
@@ -791,6 +795,15 @@ void qcc_set_error(struct qcc *qcc, int err, int app)
        tasklet_wakeup(qcc->wait_event.tasklet);
 }
 
+void qcc_report_term_evt(struct qcc *qcc, enum muxc_term_event_type type)
+{
+       enum term_event_loc loc = tevt_loc_muxc;
+
+       if (qcc->flags & QC_CF_IS_BACK)
+               loc += 8;
+       qcc->term_evts_log = tevt_report_event(qcc->term_evts_log, loc, type);
+}
+
 /* Increment glitch counter for <qcc> connection by <inc> steps. If configured
  * threshold reached, close the connection with an error code.
  */
@@ -803,10 +816,12 @@ int _qcc_report_glitch(struct qcc *qcc, int inc)
            (th_ctx->idle_pct <= global.tune.glitch_kill_maxidle)) {
                if (qcc->app_ops->report_susp) {
                        qcc->app_ops->report_susp(qcc->ctx);
-                       qcc_set_error(qcc, qcc->err.code, 1);
+                       qcc_set_error(qcc, qcc->err.code, 1,
+                                     muxc_tevt_type_graceful_shut);
                }
                else {
-                       qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0);
+                       qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0,
+                                     muxc_tevt_type_internal_err);
                }
                return 1;
        }
@@ -869,7 +884,8 @@ struct qcs *qcc_init_stream_local(struct qcc *qcc, int bidi)
 
        qcs = qcs_new(qcc, *next, type);
        if (!qcs) {
-               qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0);
+               qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0,
+                             muxc_tevt_type_internal_err);
                TRACE_DEVEL("leaving on error", QMUX_EV_QCS_NEW, qcc->conn);
                return NULL;
        }
@@ -919,7 +935,8 @@ static struct qcs *qcc_init_stream_remote(struct qcc *qcc, uint64_t id)
                                           qcc->lfctl.ms_uni * 4;
        if (id >= max_id) {
                TRACE_ERROR("flow control error", QMUX_EV_QCS_NEW|QMUX_EV_PROTO_ERR, qcc->conn);
-               qcc_set_error(qcc, QC_ERR_STREAM_LIMIT_ERROR, 0);
+               qcc_set_error(qcc, QC_ERR_STREAM_LIMIT_ERROR, 0,
+                             muxc_tevt_type_proto_err);
                goto err;
        }
 
@@ -936,7 +953,8 @@ static struct qcs *qcc_init_stream_remote(struct qcc *qcc, uint64_t id)
                qcs = qcs_new(qcc, *largest, type);
                if (!qcs) {
                        TRACE_ERROR("stream fallocation failure", QMUX_EV_QCS_NEW, qcc->conn);
-                       qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0);
+                       qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0,
+                                     muxc_tevt_type_internal_err);
                        goto err;
                }
 
@@ -1113,13 +1131,15 @@ int qcc_get_qcs(struct qcc *qcc, uint64_t id, int receive_only, int send_only,
 
        if (!receive_only && quic_stream_is_uni(id) && quic_stream_is_remote(qcc, id)) {
                TRACE_ERROR("receive-only stream not allowed", QMUX_EV_QCC_RECV|QMUX_EV_QCC_NQCS|QMUX_EV_PROTO_ERR, qcc->conn, NULL, &id);
-               qcc_set_error(qcc, QC_ERR_STREAM_STATE_ERROR, 0);
+               qcc_set_error(qcc, QC_ERR_STREAM_STATE_ERROR, 0,
+                             muxc_tevt_type_proto_err);
                goto err;
        }
 
        if (!send_only && quic_stream_is_uni(id) && quic_stream_is_local(qcc, id)) {
                TRACE_ERROR("send-only stream not allowed", QMUX_EV_QCC_RECV|QMUX_EV_QCC_NQCS|QMUX_EV_PROTO_ERR, qcc->conn, NULL, &id);
-               qcc_set_error(qcc, QC_ERR_STREAM_STATE_ERROR, 0);
+               qcc_set_error(qcc, QC_ERR_STREAM_STATE_ERROR, 0,
+                             muxc_tevt_type_proto_err);
                goto err;
        }
 
@@ -1151,7 +1171,8 @@ int qcc_get_qcs(struct qcc *qcc, uint64_t id, int receive_only, int send_only,
                 * stream.
                 */
                TRACE_ERROR("locally initiated stream not yet created", QMUX_EV_QCC_RECV|QMUX_EV_QCC_NQCS|QMUX_EV_PROTO_ERR, qcc->conn, NULL, &id);
-               qcc_set_error(qcc, QC_ERR_STREAM_STATE_ERROR, 0);
+               qcc_set_error(qcc, QC_ERR_STREAM_STATE_ERROR, 0,
+                             muxc_tevt_type_proto_err);
                goto err;
        }
        else {
@@ -1330,7 +1351,8 @@ static void qcs_consume(struct qcs *qcs, uint64_t bytes, struct qc_stream_rxbuf
                if (!frm) {
                        frm = qc_frm_alloc(QUIC_FT_MAX_STREAM_DATA);
                        if (!frm) {
-                               qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0);
+                               qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0,
+                                             muxc_tevt_type_internal_err);
                                return;
                        }
 
@@ -1354,7 +1376,8 @@ static void qcs_consume(struct qcs *qcs, uint64_t bytes, struct qc_stream_rxbuf
                TRACE_DATA("increase conn credit via MAX_DATA", QMUX_EV_QCS_RECV, qcc->conn, qcs);
                frm = qc_frm_alloc(QUIC_FT_MAX_DATA);
                if (!frm) {
-                       qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0);
+                       qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0,
+                                     muxc_tevt_type_internal_err);
                        return;
                }
 
@@ -1922,7 +1945,8 @@ int qcc_recv(struct qcc *qcc, uint64_t id, uint64_t len, uint64_t offset,
        if (qcs->flags & QC_SF_SIZE_KNOWN &&
            (offset + len > qcs->rx.offset_max || (fin && offset + len < qcs->rx.offset_max))) {
                TRACE_ERROR("final size error", QMUX_EV_QCC_RECV|QMUX_EV_QCS_RECV|QMUX_EV_PROTO_ERR, qcc->conn, qcs);
-               qcc_set_error(qcc, QC_ERR_FINAL_SIZE_ERROR, 0);
+               qcc_set_error(qcc, QC_ERR_FINAL_SIZE_ERROR, 0,
+                             muxc_tevt_type_proto_err);
                goto err;
        }
 
@@ -1955,7 +1979,8 @@ int qcc_recv(struct qcc *qcc, uint64_t id, uint64_t len, uint64_t offset,
                         */
                        TRACE_ERROR("flow control error", QMUX_EV_QCC_RECV|QMUX_EV_QCS_RECV|QMUX_EV_PROTO_ERR,
                                    qcc->conn, qcs);
-                       qcc_set_error(qcc, QC_ERR_FLOW_CONTROL_ERROR, 0);
+                       qcc_set_error(qcc, QC_ERR_FLOW_CONTROL_ERROR, 0,
+                                     muxc_tevt_type_proto_err);
                        goto err;
                }
        }
@@ -1987,7 +2012,8 @@ int qcc_recv(struct qcc *qcc, uint64_t id, uint64_t len, uint64_t offset,
                buf = qcs_get_rxbuf(qcs, offset, &len);
                if (!buf) {
                        TRACE_ERROR("rxbuf alloc failure", QMUX_EV_QCC_RECV|QMUX_EV_QCS_RECV, qcc->conn, qcs);
-                       qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0);
+                       qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0,
+                                     muxc_tevt_type_internal_err);
                        goto err;
                }
 
@@ -2012,7 +2038,8 @@ int qcc_recv(struct qcc *qcc, uint64_t id, uint64_t len, uint64_t offset,
                         */
                        TRACE_ERROR("overlapping data rejected", QMUX_EV_QCC_RECV|QMUX_EV_QCS_RECV|QMUX_EV_PROTO_ERR,
                                    qcc->conn, qcs);
-                       qcc_set_error(qcc, QC_ERR_PROTOCOL_VIOLATION, 0);
+                       qcc_set_error(qcc, QC_ERR_PROTOCOL_VIOLATION, 0,
+                                     muxc_tevt_type_proto_err);
                        return 1;
 
                case NCB_RET_GAP_SIZE:
@@ -2192,7 +2219,8 @@ int qcc_recv_max_streams(struct qcc *qcc, uint64_t max, int bidi)
         */
        if (max > QUIC_VARINT_8_BYTE_MAX) {
                TRACE_ERROR("invalid MAX_STREAMS value", QMUX_EV_QCC_RECV, qcc->conn);
-               qcc_set_error(qcc, QC_ERR_FRAME_ENCODING_ERROR, 0);
+               qcc_set_error(qcc, QC_ERR_FRAME_ENCODING_ERROR, 0,
+                             muxc_tevt_type_proto_err);
                goto err;
        }
 
@@ -2271,7 +2299,8 @@ int qcc_recv_reset_stream(struct qcc *qcc, uint64_t id, uint64_t err, uint64_t f
        if (qcs->rx.offset_max > final_size ||
            ((qcs->flags & QC_SF_SIZE_KNOWN) && qcs->rx.offset_max != final_size)) {
                TRACE_ERROR("final size error on RESET_STREAM", QMUX_EV_QCC_RECV|QMUX_EV_QCS_RECV, qcc->conn, qcs);
-               qcc_set_error(qcc, QC_ERR_FINAL_SIZE_ERROR, 0);
+               qcc_set_error(qcc, QC_ERR_FINAL_SIZE_ERROR, 0,
+                             muxc_tevt_type_proto_err);
                goto err;
        }
 
@@ -2437,7 +2466,8 @@ static int qcc_release_remote_stream(struct qcc *qcc, uint64_t id)
                        TRACE_DATA("increase max stream limit with MAX_STREAMS_BIDI", QMUX_EV_QCC_SEND, qcc->conn);
                        frm = qc_frm_alloc(QUIC_FT_MAX_STREAMS_BIDI);
                        if (!frm) {
-                               qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0);
+                               qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0,
+                                             muxc_tevt_type_internal_err);
                                goto err;
                        }
 
@@ -3434,6 +3464,7 @@ static void qcc_app_shutdown(struct qcc *qcc)
        }
 
        TRACE_STATE("perform graceful shutdown", QMUX_EV_QCC_END, qcc->conn);
+       qcc_report_term_evt(qcc, muxc_tevt_type_graceful_shut);
        if (qcc->app_ops && qcc->app_ops->shutdown) {
                qcc->app_ops->shutdown(qcc->ctx);
                qcc_io_send(qcc);
@@ -3504,10 +3535,13 @@ static void qcc_release(struct qcc *qcc)
        }
 
        tasklet_free(qcc->wait_event.tasklet);
-       if (conn && qcc->wait_event.events) {
-               conn->xprt->unsubscribe(conn, conn->xprt_ctx,
-                                       qcc->wait_event.events,
-                                       &qcc->wait_event);
+       if (conn) {
+               qcc_report_term_evt(qcc, muxc_tevt_type_shutw);
+               if (qcc->wait_event.events) {
+                       conn->xprt->unsubscribe(conn, conn->xprt_ctx,
+                                               qcc->wait_event.events,
+                                               &qcc->wait_event);
+               }
        }
 
        while (!LIST_ISEMPTY(&qcc->lfctl.frms)) {
@@ -3782,6 +3816,7 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
        qcc->flags = conn_is_back(conn) ? QC_CF_IS_BACK : 0;
        qcc->app_st = QCC_APP_ST_NULL;
        qcc->glitches = 0;
+       qcc->term_evts_log = 0;
        qcc->err = quic_err_transport(QC_ERR_NO_ERROR);
 
        if (conn_is_quic(conn)) {
@@ -4593,6 +4628,9 @@ static int qmux_ctl(struct connection *conn, enum mux_ctl_type mux_ctl, void *ou
        case MUX_CTL_GET_MAXSTRM:
                return qcc->lfctl.ms_bidi_init;
 
+       case MUX_CTL_TEVTS:
+               return qcc->term_evts_log;
+
        default:
                return -1;
        }
@@ -4624,8 +4662,9 @@ static int qmux_sctl(struct stconn *sc, enum mux_sctl_type mux_sctl, void *outpu
                        qmux_dump_qcc_info(buf, qcc);
 
                if (dbg_ctx->arg.debug_flags & MUX_SCTL_DBG_STR_L_CONN) {
-                       chunk_appendf(buf, " conn.flg=%#08x conn.err_code=%u",
-                                     conn->flags, conn->err_code);
+                       chunk_appendf(buf, " conn.flg=%#08x conn.err_code=%u conn.evts=%s",
+                                     conn->flags, conn->err_code,
+                                     tevt_evts2str(conn->term_evts_log));
                }
 
                if (dbg_ctx->arg.debug_flags & MUX_SCTL_DBG_STR_L_XPRT)
index 812a4881bb876322dba0d73458965ae036cb0d00..41016d13a6ec83e0f0155570b1e2813719816642 100644 (file)
@@ -149,9 +149,9 @@ void qmux_dump_qcc_info(struct buffer *msg, const struct qcc *qcc)
        chunk_appendf(msg, " qcc=%p(%c)", qcc, (qcc->flags & QC_CF_IS_BACK) ? 'B' : 'F');
        if (qc)
                chunk_appendf(msg, " qc=%p", qcc->conn->handle.qc);
-       chunk_appendf(msg, " .st=%s .sc=%llu .hreq=%llu .flg=0x%04x",
+       chunk_appendf(msg, " .st=%s .sc=%llu .hreq=%llu .flg=0x%04x .evts=%s",
                      qcc_app_st_to_str(qcc->app_st), (ullong)qcc->nb_sc,
-                     (ullong)qcc->nb_hreq, qcc->flags);
+                     (ullong)qcc->nb_hreq, qcc->flags, tevt_evts2str(qcc->term_evts_log));
 
        chunk_appendf(msg, " .tx=%llu %llu/%llu",
                      (ullong)qcc->tx.fc.off_soft, (ullong)qcc->tx.fc.off_real, (ullong)qcc->tx.fc.limit);
index e36eb75f31da5dc3d091d9c59a63d10ec8758d6e..e6ecb6dbbac006bade3c4371c54c4c627c65e41c 100644 (file)
@@ -119,7 +119,8 @@ int qpack_decode_enc(struct buffer *buf, int fin, void *ctx)
         * connection error of type H3_CLOSED_CRITICAL_STREAM.
         */
        if (fin) {
-               qcc_set_error(qcs->qcc, H3_ERR_CLOSED_CRITICAL_STREAM, 1);
+               qcc_set_error(qcs->qcc, H3_ERR_CLOSED_CRITICAL_STREAM, 1,
+                             muxc_tevt_type_proto_err);
                return -1;
        }
 
@@ -152,7 +153,8 @@ int qpack_decode_enc(struct buffer *buf, int fin, void *ctx)
                 * QPACK_ENCODER_STREAM_ERROR.
                 */
                if (capacity) {
-                       qcc_set_error(qcs->qcc, QPACK_ERR_ENCODER_STREAM_ERROR, 1);
+                       qcc_set_error(qcs->qcc, QPACK_ERR_ENCODER_STREAM_ERROR, 1,
+                                     muxc_tevt_type_proto_err);
                        return -1;
                }
 
@@ -179,7 +181,8 @@ int qpack_decode_dec(struct buffer *buf, int fin, void *ctx)
         * connection error of type H3_CLOSED_CRITICAL_STREAM.
         */
        if (fin) {
-               qcc_set_error(qcs->qcc, H3_ERR_CLOSED_CRITICAL_STREAM, 1);
+               qcc_set_error(qcs->qcc, H3_ERR_CLOSED_CRITICAL_STREAM, 1,
+                             muxc_tevt_type_proto_err);
                return -1;
        }
 
@@ -204,7 +207,8 @@ int qpack_decode_dec(struct buffer *buf, int fin, void *ctx)
                 */
 
                /* For the moment haproxy does not emit dynamic table insertion. */
-               qcc_set_error(qcs->qcc, QPACK_ERR_DECODER_STREAM_ERROR, 1);
+               qcc_set_error(qcs->qcc, QPACK_ERR_DECODER_STREAM_ERROR, 1,
+                             muxc_tevt_type_proto_err);
                return -1;
        }
        else if (inst & QPACK_DEC_INST_SACK) {