/* flow-control fields set by the peer which we must respect. */
struct {
+ uint64_t ms_uni; /* max sub-ID of uni stream allowed by the peer */
+
uint64_t md; /* connection flow control limit updated on MAX_DATA frames reception */
uint64_t msd_bidi_l; /* initial max-stream-data from peer on local bidi streams */
uint64_t msd_bidi_r; /* initial max-stream-data from peer on remote bidi streams */
void qcc_set_error(struct qcc *qcc, int err, int app);
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);
void qcs_send_metadata(struct qcs *qcs);
int qcs_attach_sc(struct qcs *qcs, struct buffer *buf, char fin);
TRACE_ENTER(H3_EV_H3C_NEW, qcc->conn);
if (!qcs) {
+ /* RFC 9114 6.2. Unidirectional Streams
+ *
+ * Each endpoint needs to create at least one unidirectional stream for
+ * the HTTP control stream. QPACK requires two additional unidirectional
+ * streams, and other extensions might require further streams.
+ * Therefore, the transport parameters sent by both clients and servers
+ * MUST allow the peer to create at least three unidirectional streams.
+ */
+ 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_report_glitch(qcc, 1);
+ goto err;
+ }
+
qcs = qcc_init_stream_local(qcc, 0);
if (!qcs) {
/* Error must be set by qcc_init_stream_local(). */
return 0;
}
+/* Returns the number of streams which can still be opened until flow-control limit. */
+int qcc_fctl_avail_streams(const struct qcc *qcc, int bidi)
+{
+ if (bidi) {
+ /* TODO */
+ return 0;
+ }
+ else {
+ const uint64_t next = qcc->next_uni_l / 4;
+ BUG_ON(qcc->rfctl.ms_uni < next);
+ return qcc->rfctl.ms_uni - next;
+ }
+}
+
/* Open a locally initiated stream for the connection <qcc>. Set <bidi> for a
* bidirectional stream, else an unidirectional stream is opened. The next
* available ID on the connection will be used according to the stream type.
type = conn_is_back(qcc->conn) ? QCS_CLT_BIDI : QCS_SRV_BIDI;
}
else {
+ BUG_ON(qcc->rfctl.ms_uni * 4 < qcc->next_uni_l);
next = &qcc->next_uni_l;
type = conn_is_back(qcc->conn) ? QCS_CLT_UNI : QCS_SRV_UNI;
}
- /* TODO ensure that we won't overflow remote peer flow control limit on
- * streams. Else, we should emit a STREAMS_BLOCKED frame.
- */
-
qcs = qcs_new(qcc, *next, type);
if (!qcs) {
qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0);
}
TRACE_PROTO("opening local stream", QMUX_EV_QCS_NEW, qcc->conn, qcs);
+ /* TODO emit STREAMS_BLOCKED if cannot create future streams. */
*next += 4;
TRACE_LEAVE(QMUX_EV_QCS_NEW, qcc->conn, qcs);
rparams = &conn->handle.qc->tx.params;
qfctl_init(&qcc->tx.fc, rparams->initial_max_data);
+ qcc->rfctl.ms_uni = rparams->initial_max_streams_uni;
qcc->rfctl.msd_bidi_l = rparams->initial_max_stream_data_bidi_local;
qcc->rfctl.msd_bidi_r = rparams->initial_max_stream_data_bidi_remote;
qcc->rfctl.msd_uni_l = rparams->initial_max_stream_data_uni;