int qcc_realign_stream_txbuf(const struct qcs *qcs, struct buffer *out);
int qcc_release_stream_txbuf(struct qcs *qcs);
int qcc_stream_can_send(const struct qcs *qcs);
-void qcc_reset_stream(struct qcs *qcs, int err);
+void qcc_reset_stream(struct qcs *qcs, int err, int term_evt);
void qcc_send_stream(struct qcs *qcs, int urg, int count);
void qcc_abort_stream_read(struct qcs *qcs);
void qcc_update_shut_id(struct qcc *qcc, uint64_t val);
/* FIN received, ensure body length is conform to any content-length header. */
if ((h3s->flags & H3_SF_HAVE_CLEN) && h3_check_body_size(qcs, 1)) {
qcc_abort_stream_read(qcs);
- qcc_reset_stream(qcs, h3s->err);
+ qcc_reset_stream(qcs, h3s->err, se_tevt_type_proto_err);
goto done;
}
/* Interrupt decoding on stream/connection error detected. */
if (h3s->err) {
+ /* TODO Only unimplemented CONNECT reports H3_ERR_REQUEST_REJECTED here. */
+ const int tevt =
+ (h3s->err == H3_ERR_REQUEST_REJECTED) ? 0 : se_tevt_type_proto_err;
qcc_abort_stream_read(qcs);
- qcc_reset_stream(qcs, h3s->err);
+ qcc_reset_stream(qcs, h3s->err, tevt);
total = b_data(b);
goto done;
}
TRACE_STATE("close stream outside of GOAWAY range", H3_EV_H3S_NEW, qcs->qcc->conn, qcs);
qcc_abort_stream_read(qcs);
- qcc_reset_stream(qcs, H3_ERR_REQUEST_REJECTED);
+ qcc_reset_stream(qcs, H3_ERR_REQUEST_REJECTED, 0);
}
/* TODO support push uni-stream rejection. */
}
}
-/* Prepare for the emission of RESET_STREAM on <qcs> with error code <err>. */
-void qcc_reset_stream(struct qcs *qcs, int err)
+/* Prepare for the emission of RESET_STREAM on <qcs> with error code <err>. If
+ * <tevt> is non null, it is used as a stream level termination event code.
+ */
+void qcc_reset_stream(struct qcs *qcs, int err, int tevt)
{
struct qcc *qcc = qcs->qcc;
const uint64_t diff = qcs_prep_bytes(qcs);
/* Report send error to stream-endpoint layer. */
if (qcs_sc(qcs)) {
se_fl_set_error(qcs->sd);
+ if (tevt)
+ se_report_term_evt(qcs->sd, tevt);
qcs_alert(qcs);
}
/* Manually set EOS if FIN already reached as futures RESET_STREAM will be ignored in this case. */
if (qcs_sc(qcs) && se_fl_test(qcs->sd, SE_FL_EOI)) {
se_fl_set(qcs->sd, SE_FL_EOS);
+ se_report_term_evt(qcs->sd, (qcc->flags & QC_CF_ERR_CONN ? se_tevt_type_rcv_err : se_tevt_type_eos));
qcs_alert(qcs);
}
+ else {
+ se_report_term_evt(qcs->sd, se_tevt_type_rst_rcvd);
+ }
/* If not defined yet, set abort info for the sedesc */
if (!qcs->sd->abort_info.info) {
* the RESET_STREAM frame it sends, but it can use any application error
* code.
*/
- qcc_reset_stream(qcs, err);
+ qcc_reset_stream(qcs, err, 0);
if (qcc_may_expire(qcc) && !qcc->nb_hreq)
qcc_refresh_timeout(qcc);
if (!se_fl_test(qcs->sd, SE_FL_EOI)) {
TRACE_STATE("report error on stream aborted", QMUX_EV_STRM_RECV, qcc->conn, qcs);
se_fl_set(qcs->sd, SE_FL_ERROR);
+ se_report_term_evt(qcs->sd, (qcc->flags & QC_CF_ERR_CONN ? se_tevt_type_truncated_rcv_err : se_tevt_type_truncated_eos));
+ }
+ else {
+ se_report_term_evt(qcs->sd, (qcc->flags & QC_CF_ERR_CONN ? se_tevt_type_rcv_err : se_tevt_type_eos));
}
}
}
else {
/* RESET_STREAM necessary. */
- qcc_reset_stream(qcs, 0);
+ qcc_reset_stream(qcs, 0, 0);
}
tasklet_wakeup(qcc->wait_event.tasklet);
dbg_ctx->ret.buf = *buf;
return ret;
+ case MUX_SCTL_TEVTS:
+ return qcs->sd->term_evts_log;
+
default:
return -1;
}
if (qcs->sd) {
chunk_appendf(msg, " .sd=%p", qcs->sd);
- chunk_appendf(msg, "(.flg=0x%08x)", se_fl_get(qcs->sd));
+ chunk_appendf(msg, "(.flg=0x%08x .evts=%s)",
+ se_fl_get(qcs->sd), tevt_evts2str(qcs->sd->term_evts_log));
}
chunk_appendf(msg, " .rx=%llu/%llu rxb=%u(%u)",