Skip to content

Commit 663e872

Browse files
committed
MEDIUM: mux-quic: implement STOP_SENDING emission
Implement STOP_SENDING. This is divided in two main functions : * qcc_abort_stream_read() which can be used by application protocol to request for a STOP_SENDING. This set the flag QC_SF_READ_ABORTED. * qcs_send_reset() is a static function called after the preceding one. It will send a STOP_SENDING via qcc_send(). QC_SF_READ_ABORTED flag is now properly used : if activated on a stream during qcc_recv(), <qcc.app_ops.decode_qcs> callback is skipped. Also, abort reading on unknown unidirection remote stream is now fully supported with the emission of a STOP_SENDING as specified by RFC 9000. This commit is part of implementing H3 errors at the stream level. This will allows the H3 layer to request the peer to close its endpoint for an error on a stream. This should be backported up to 2.7.
1 parent 5854fc0 commit 663e872

4 files changed

Lines changed: 87 additions & 16 deletions

File tree

include/haproxy/mux_quic-t.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,10 @@ struct qcc {
117117
#define QC_SF_DETACH 0x00000008 /* sc is detached but there is remaining data to send */
118118
#define QC_SF_BLK_SFCTL 0x00000010 /* stream blocked due to stream flow control limit */
119119
#define QC_SF_DEM_FULL 0x00000020 /* demux blocked on request channel buffer full */
120-
#define QC_SF_READ_ABORTED 0x00000040 /* stream rejected by app layer */
120+
#define QC_SF_READ_ABORTED 0x00000040 /* Rx closed using STOP_SENDING*/
121121
#define QC_SF_TO_RESET 0x00000080 /* a RESET_STREAM must be sent */
122122
#define QC_SF_HREQ_RECV 0x00000100 /* a full HTTP request has been received */
123+
#define QC_SF_TO_STOP_SENDING 0x00000200 /* a STOP_SENDING must be sent */
123124

124125
/* Maximum size of stream Rx buffer. */
125126
#define QC_S_RX_BUF_SZ (global.tune.bufsize - NCB_RESERVED_SZ)

include/haproxy/mux_quic.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ void qcs_notify_send(struct qcs *qcs);
2121

2222
void qcc_emit_cc_app(struct qcc *qcc, int err, int immediate);
2323
void qcc_reset_stream(struct qcs *qcs, int err);
24+
void qcc_abort_stream_read(struct qcs *qcs);
2425
int qcc_recv(struct qcc *qcc, uint64_t id, uint64_t len, uint64_t offset,
2526
char fin, char *data);
2627
int qcc_recv_max_data(struct qcc *qcc, uint64_t max);

src/h3.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ static ssize_t h3_init_uni_stream(struct h3c *h3c, struct qcs *qcs,
220220
* Implementations MUST [...] abort reading on unidirectional
221221
* streams that have unknown or unsupported types.
222222
*/
223-
qcs->flags |= QC_SF_READ_ABORTED;
223+
qcc_abort_stream_read(qcs);
224224
return -1;
225225
};
226226

src/mux_quic.c

Lines changed: 83 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -755,10 +755,16 @@ static int qcc_decode_qcs(struct qcc *qcc, struct qcs *qcs)
755755
if (qcs_is_close_remote(qcs))
756756
fin = 1;
757757

758-
ret = qcc->app_ops->decode_qcs(qcs, &b, fin);
759-
if (ret < 0) {
760-
TRACE_ERROR("decoding error", QMUX_EV_QCS_RECV, qcc->conn, qcs);
761-
goto err;
758+
if (!(qcs->flags & QC_SF_READ_ABORTED)) {
759+
ret = qcc->app_ops->decode_qcs(qcs, &b, fin);
760+
if (ret < 0) {
761+
TRACE_ERROR("decoding error", QMUX_EV_QCS_RECV, qcc->conn, qcs);
762+
goto err;
763+
}
764+
}
765+
else {
766+
TRACE_DATA("ignore read on stream", QMUX_EV_QCS_RECV, qcc->conn, qcs);
767+
ret = b_data(&b);
762768
}
763769

764770
if (ret) {
@@ -813,6 +819,24 @@ void qcc_reset_stream(struct qcs *qcs, int err)
813819
tasklet_wakeup(qcc->wait_event.tasklet);
814820
}
815821

822+
/* Prepare for the emission of STOP_SENDING on <qcs>. */
823+
void qcc_abort_stream_read(struct qcs *qcs)
824+
{
825+
struct qcc *qcc = qcs->qcc;
826+
827+
TRACE_ENTER(QMUX_EV_QCC_NEW, qcc->conn, qcs);
828+
829+
if ((qcs->flags & QC_SF_TO_STOP_SENDING) || qcs_is_close_remote(qcs))
830+
goto end;
831+
832+
TRACE_STATE("abort stream read", QMUX_EV_QCS_END, qcc->conn, qcs);
833+
qcs->flags |= (QC_SF_TO_STOP_SENDING|QC_SF_READ_ABORTED);
834+
tasklet_wakeup(qcc->wait_event.tasklet);
835+
836+
end:
837+
TRACE_LEAVE(QMUX_EV_QCC_NEW, qcc->conn, qcs);
838+
}
839+
816840
/* Install the <app_ops> applicative layer of a QUIC connection on mux <qcc>.
817841
* Returns 0 on success else non-zero.
818842
*/
@@ -977,11 +1001,6 @@ int qcc_recv(struct qcc *qcc, uint64_t id, uint64_t len, uint64_t offset,
9771001
qcc_refresh_timeout(qcc);
9781002
}
9791003

980-
if (qcs->flags & QC_SF_READ_ABORTED) {
981-
/* TODO should send a STOP_SENDING */
982-
qcs_free(qcs);
983-
}
984-
9851004
out:
9861005
TRACE_LEAVE(QMUX_EV_QCC_RECV, qcc->conn);
9871006
return 0;
@@ -1538,6 +1557,58 @@ static int qcs_send_reset(struct qcs *qcs)
15381557
return 0;
15391558
}
15401559

1560+
/* Emit a STOP_SENDING on <qcs>.
1561+
*
1562+
* Returns 0 if the frame has been successfully sent else non-zero.
1563+
*/
1564+
static int qcs_send_stop_sending(struct qcs *qcs)
1565+
{
1566+
struct list frms = LIST_HEAD_INIT(frms);
1567+
struct quic_frame *frm;
1568+
struct qcc *qcc = qcs->qcc;
1569+
1570+
TRACE_ENTER(QMUX_EV_QCS_SEND, qcs->qcc->conn, qcs);
1571+
1572+
/* RFC 9000 3.3. Permitted Frame Types
1573+
*
1574+
* A
1575+
* receiver MAY send a STOP_SENDING frame in any state where it has not
1576+
* received a RESET_STREAM frame -- that is, states other than "Reset
1577+
* Recvd" or "Reset Read". However, there is little value in sending a
1578+
* STOP_SENDING frame in the "Data Recvd" state, as all stream data has
1579+
* been received. A sender could receive either of these two types of
1580+
* frames in any state as a result of delayed delivery of packets.¶
1581+
*/
1582+
if (qcs_is_close_remote(qcs)) {
1583+
TRACE_STATE("skip STOP_SENDING on remote already closed", QMUX_EV_QCS_SEND, qcc->conn, qcs);
1584+
goto done;
1585+
}
1586+
1587+
frm = pool_zalloc(pool_head_quic_frame);
1588+
if (!frm) {
1589+
TRACE_LEAVE(QMUX_EV_QCS_SEND, qcs->qcc->conn, qcs);
1590+
return 1;
1591+
}
1592+
1593+
LIST_INIT(&frm->reflist);
1594+
frm->type = QUIC_FT_STOP_SENDING;
1595+
frm->stop_sending.id = qcs->id;
1596+
frm->stop_sending.app_error_code = qcs->err;
1597+
1598+
LIST_APPEND(&frms, &frm->list);
1599+
if (qc_send_frames(qcs->qcc, &frms)) {
1600+
pool_free(pool_head_quic_frame, frm);
1601+
TRACE_DEVEL("cannot send STOP_SENDING", QMUX_EV_QCS_SEND, qcs->qcc->conn, qcs);
1602+
return 1;
1603+
}
1604+
1605+
done:
1606+
qcs->flags &= ~QC_SF_TO_STOP_SENDING;
1607+
1608+
TRACE_LEAVE(QMUX_EV_QCS_SEND, qcs->qcc->conn, qcs);
1609+
return 0;
1610+
}
1611+
15411612
/* Used internally by qc_send function. Proceed to send for <qcs>. This will
15421613
* transfer data from qcs buffer to its quic_stream counterpart. A STREAM frame
15431614
* is then generated and inserted in <frms> list.
@@ -1650,6 +1721,9 @@ static int qc_send(struct qcc *qcc)
16501721
continue;
16511722
}
16521723

1724+
if (qcs->flags & QC_SF_TO_STOP_SENDING)
1725+
qcs_send_stop_sending(qcs);
1726+
16531727
if (qcs->flags & QC_SF_TO_RESET) {
16541728
qcs_send_reset(qcs);
16551729
node = eb64_next(node);
@@ -1751,11 +1825,6 @@ static int qc_recv(struct qcc *qcc)
17511825

17521826
qcc_decode_qcs(qcc, qcs);
17531827
node = eb64_next(node);
1754-
1755-
if (qcs->flags & QC_SF_READ_ABORTED) {
1756-
/* TODO should send a STOP_SENDING */
1757-
qcs_free(qcs);
1758-
}
17591828
}
17601829

17611830
TRACE_LEAVE(QMUX_EV_QCC_RECV, qcc->conn);

0 commit comments

Comments
 (0)