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 */
_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);
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;
}
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;
}
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;
}
* 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;
*/
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;
}
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;
}
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;
}
*/
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;
}
/* 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;
}
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;
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;
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;
}
/* 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;
}
*/
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;
}
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;
}
*/
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;
}
* 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;
}
/* 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);
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
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.
*/
(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;
}
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;
}
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;
}
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;
}
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;
}
* 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 {
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;
}
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;
}
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;
}
*/
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;
}
}
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;
}
*/
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:
*/
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;
}
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;
}
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;
}
}
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);
}
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)) {
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)) {
case MUX_CTL_GET_MAXSTRM:
return qcc->lfctl.ms_bidi_init;
+ case MUX_CTL_TEVTS:
+ return qcc->term_evts_log;
+
default:
return -1;
}
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)
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);
* 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;
}
* 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;
}
* 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;
}
*/
/* 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) {