diff --git a/include/haproxy/mux_quic.h b/include/haproxy/mux_quic.h index 908a02fba..0a88f6b4b 100644 --- a/include/haproxy/mux_quic.h +++ b/include/haproxy/mux_quic.h @@ -25,6 +25,7 @@ int qcc_recv(struct qcc *qcc, uint64_t id, uint64_t len, uint64_t offset, char fin, char *data); int qcc_recv_max_data(struct qcc *qcc, uint64_t max); int qcc_recv_max_stream_data(struct qcc *qcc, uint64_t id, uint64_t max); +int qcc_recv_reset_stream(struct qcc *qcc, uint64_t id, uint64_t err, uint64_t final_size); int qcc_recv_stop_sending(struct qcc *qcc, uint64_t id, uint64_t err); void qcc_streams_sent_done(struct qcs *qcs, uint64_t data, uint64_t offset); diff --git a/src/mux_quic.c b/src/mux_quic.c index e4721fd39..c780ebcad 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -888,6 +888,11 @@ int qcc_recv(struct qcc *qcc, uint64_t id, uint64_t len, uint64_t offset, goto err; } + if (qcs_is_close_remote(qcs)) { + TRACE_DATA("skipping STREAM for remotely closed", QMUX_EV_QCC_RECV, qcc->conn); + goto out; + } + if (offset + len <= qcs->rx.offset) { /* TODO offset may have been received without FIN first and now * with it. In this case, it must be notified to be able to @@ -1055,6 +1060,60 @@ int qcc_recv_max_stream_data(struct qcc *qcc, uint64_t id, uint64_t max) return 0; } +/* Handle a new RESET_STREAM frame from stream ID with error code + * and final stream size . + * + * Returns 0 on success else non-zero. On error, the received frame should not + * be acknowledged. + */ +int qcc_recv_reset_stream(struct qcc *qcc, uint64_t id, uint64_t err, uint64_t final_size) +{ + struct qcs *qcs; + + TRACE_ENTER(QMUX_EV_QCC_RECV, qcc->conn); + + /* RFC 9000 19.4. RESET_STREAM Frames + * + * An endpoint that receives a RESET_STREAM frame for a send-only stream + * MUST terminate the connection with error STREAM_STATE_ERROR. + */ + if (qcc_get_qcs(qcc, id, 1, 0, &qcs)) { + TRACE_ERROR("RESET_STREAM for send-only stream received", QMUX_EV_QCC_RECV|QMUX_EV_QCS_RECV, qcc->conn, qcs); + qcc_emit_cc(qcc, QC_ERR_STREAM_STATE_ERROR); + goto err; + } + + if (!qcs || qcs_is_close_remote(qcs)) + goto out; + + TRACE_PROTO("receiving RESET_STREAM", QMUX_EV_QCC_RECV|QMUX_EV_QCS_RECV, qcc->conn, qcs); + qcs_idle_open(qcs); + + 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_emit_cc(qcc, QC_ERR_FINAL_SIZE_ERROR); + goto err; + } + + qcs->flags |= QC_SF_SIZE_KNOWN; + qcs_close_remote(qcs); + qc_free_ncbuf(qcs, &qcs->rx.ncbuf); + + if (qcs_sc(qcs)) { + se_fl_set_error(qcs->sd); + qcs_alert(qcs); + } + + out: + TRACE_LEAVE(QMUX_EV_QCC_RECV, qcc->conn); + return 0; + + err: + TRACE_LEAVE(QMUX_EV_QCC_RECV, qcc->conn); + return 1; +} + /* Handle a new STOP_SENDING frame for stream ID . The error code should be * specified in . * diff --git a/src/quic_conn.c b/src/quic_conn.c index ddb4bbbc6..e3a8b4634 100644 --- a/src/quic_conn.c +++ b/src/quic_conn.c @@ -2802,8 +2802,11 @@ static int qc_parse_pkt_frms(struct quic_conn *qc, struct quic_rx_packet *pkt, break; } case QUIC_FT_RESET_STREAM: - /* TODO: handle this frame at STREAM level */ - break; + if (qc->mux_state == QC_MUX_READY) { + struct quic_reset_stream *rs = &frm.reset_stream; + qcc_recv_reset_stream(qc->qcc, rs->id, rs->app_error_code, rs->final_size); + } + break; case QUIC_FT_STOP_SENDING: { struct quic_stop_sending *stop_sending = &frm.stop_sending;