- tune.quic.fe.sock-per-conn
- tune.quic.fe.stream.data-ratio
- tune.quic.fe.stream.max-concurrent
+ - tune.quic.fe.stream.max-total
- tune.quic.fe.stream.rxbuf
- tune.quic.fe.tx.pacing
- tune.quic.fe.tx.udp-gso
See also: "tune.quic.be.stream.rxbuf", "tune.quic.fe.stream.rxbuf",
"tune.quic.be.stream.data-ratio", "tune.quic.fe.stream.data-ratio"
+tune.quic.fe.stream.max-total <number>
+ Sets the maximum number of requests that can be handled by a single QUIC
+ connection. Once this total is reached, the connection will be gracefully
+ shutdown. In HTTP/3, this translates in a GOAWAY frame.
+
+ This setting is applied as a hard limit on the connection via the QUIC flow
+ control mechanism. If a peer violates it, the connection will be immediately
+ closed.
+
+ This setting can be used to force clients to open new connections once in a
+ while to continue the emission of requests and avoid maintaining connections
+ for too many times. However, low values will increase latency on the client
+ side, as well as CPU consumption on both sides due to TLS handshakes.
+
+ The default value is 0 which implies no specific limit outside of the QUIC
+ protocol encoding limitation (2^60, more that a billion billion).
+
tune.quic.frontend.max-streams-bidi <number> (deprecated)
This keyword has been deprecated in 3.3 and will be removed in 3.5. It is
part of the streamlining process apply on QUIC configuration. If used, this
uint sec_retry_threshold;
uint stream_data_ratio;
uint stream_max_concurrent;
+ uint stream_max_total;
uint stream_rxbuf;
uint opts; /* QUIC_TUNE_FE_* options specific to FE side */
uint fb_opts; /* QUIC_TUNE_FB_* options shared by both side */
.sec_retry_threshold = QUIC_DFLT_SEC_RETRY_THRESHOLD,
.stream_data_ratio = QUIC_DFLT_FE_STREAM_DATA_RATIO,
.stream_max_concurrent = QUIC_DFLT_FE_STREAM_MAX_CONCURRENT,
+ .stream_max_total = 0,
.stream_rxbuf = 0,
.fb_opts = QUIC_TUNE_FB_TX_PACING|QUIC_TUNE_FB_TX_UDP_GSO,
.opts = QUIC_TUNE_FE_SOCK_PER_CONN,
&quic_tune.fe.stream_max_concurrent;
*ptr = arg;
}
+ else if (strcmp(suffix, "fe.stream.max-total") == 0) {
+ quic_tune.fe.stream_max_total = arg;
+ }
else if (strcmp(suffix, "be.stream.rxbuf") == 0 ||
strcmp(suffix, "fe.stream.rxbuf") == 0) {
uint *ptr = (suffix[0] == 'b') ? &quic_tune.be.stream_rxbuf :
{ CFG_GLOBAL, "tune.quic.fe.sock-per-conn", cfg_parse_quic_tune_sock_per_conn },
{ CFG_GLOBAL, "tune.quic.fe.stream.data-ratio", cfg_parse_quic_tune_setting },
{ CFG_GLOBAL, "tune.quic.fe.stream.max-concurrent", cfg_parse_quic_tune_setting },
+ { CFG_GLOBAL, "tune.quic.fe.stream.max-total", cfg_parse_quic_tune_setting },
{ CFG_GLOBAL, "tune.quic.fe.stream.rxbuf", cfg_parse_quic_tune_setting },
{ CFG_GLOBAL, "tune.quic.fe.tx.pacing", cfg_parse_quic_tune_on_off },
{ CFG_GLOBAL, "tune.quic.fe.tx.udp-gso", cfg_parse_quic_tune_on_off },
struct ist meth = IST_NULL, path = IST_NULL;
struct ist scheme = IST_NULL, authority = IST_NULL;
struct ist uri;
+ uint64_t id_goaway;
int hdr_idx, ret;
int cookie = -1, last_cookie = -1, i;
int relaxed = !!(h3c->qcc->proxy->options2 & PR_O2_REQBUG_OK);
htx_to_buf(htx, &htx_buf);
htx = NULL;
- if (qcs_attach_sc(qcs, &htx_buf, fin)) {
- len = -1;
- goto out;
- }
+ /* Stream attach may need the new GOAWAY ID, so update it before.
+ * Keep a copy of the older value to restore it in case of error.
+ */
+ id_goaway = h3c->id_goaway;
/* RFC 9114 5.2. Connection Shutdown
*
if (qcs->id >= h3c->id_goaway)
h3c->id_goaway = qcs->id + 4;
+ if (qcs_attach_sc(qcs, &htx_buf, fin)) {
+ /* Stream not handled, restore old GOAWAY ID. */
+ h3c->id_goaway = id_goaway;
+ len = -1;
+ goto out;
+ }
+
out:
/* HTX may be non NULL if error before previous htx_to_buf(). */
if (htx)
}
}
+/* Retrieves the maximum number of bidirectional remote streams that the peer
+ * will be allowed to use during <conn> connection lifetime. This is guaranteed
+ * to be a positive integer.
+ */
+static uint64_t qcc_max_strm_bidi_remote(const struct connection *conn)
+{
+ /* On FE side, streams may be limited by stream.max-total configuration. */
+ if (!conn_is_back(conn) && quic_tune.fe.stream_max_total)
+ return quic_tune.fe.stream_max_total;
+ return (uint64_t)1 << 60;
+}
+
/* 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.
se_fl_set_error(qcs->sd);
}
+ /* Graceful shutdown is initiated as soon as max stream is reached. */
+ if (qcs->id == (qcc_max_strm_bidi_remote(qcc->conn) - 1) * 4) {
+ TRACE_STATE("initiate shutdown as max remote bidi stream reached", QMUX_EV_STRM_RECV, qcc->conn, qcs);
+ qcc_app_shutdown(qcc);
+ }
+
out:
TRACE_LEAVE(QMUX_EV_STRM_RECV, qcc->conn, qcs);
return 0;
return 1;
}
-#define QUIC_MAX_STREAMS_MAX_ID (1ULL<<60)
-
/* Signal the closing of remote stream with id <id>. Flow-control for new
* streams may be allocated for the peer if needed.
*/
TRACE_ENTER(QMUX_EV_QCS_END, qcc->conn);
if (quic_stream_is_bidi(id)) {
- /* RFC 9000 4.6. Controlling Concurrency
- *
- * If a max_streams transport parameter or a MAX_STREAMS frame is
- * received with a value greater than 260, this would allow a maximum
- * stream ID that cannot be expressed as a variable-length integer; see
- * Section 16. If either is received, the connection MUST be closed
- * immediately with a connection error of type TRANSPORT_PARAMETER_ERROR
- * if the offending value was received in a transport parameter or of
- * type FRAME_ENCODING_ERROR if it was received in a frame; see Section
- * 10.2.
- */
- if (qcc->lfctl.ms_bidi == QUIC_MAX_STREAMS_MAX_ID) {
+ const uint64_t max = qcc_max_strm_bidi_remote(qcc->conn);
+ /* The peer must not have been authorized to open a stream outside of this range. */
+ BUG_ON(qcc->lfctl.ms_bidi > max);
+ if (qcc->lfctl.ms_bidi == max) {
TRACE_DATA("maximum streams value reached", QMUX_EV_QCC_SEND, qcc->conn);
goto out;
}
* the initial window or reaching the stream ID limit.
*/
if (qcc->lfctl.cl_bidi_r > qcc->lfctl.ms_bidi_init / 2 ||
- qcc->lfctl.cl_bidi_r + qcc->lfctl.ms_bidi == QUIC_MAX_STREAMS_MAX_ID) {
+ qcc->lfctl.cl_bidi_r + qcc->lfctl.ms_bidi == max) {
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) {
const uint64_t stream_rx_bufsz = qmux_stream_rx_bufsz();
const uint stream_rxbuf = server ?
quic_tune.fe.stream_rxbuf : quic_tune.be.stream_rxbuf;
- const int max_streams_bidi = server ?
- quic_tune.fe.stream_max_concurrent : quic_tune.be.stream_max_concurrent;
+ /* On FE side, check if stream.max-total is set and inferior to stream.max-concurrent */
+ const int max_streams_bidi = server && quic_tune.fe.stream_max_total ?
+ MIN(quic_tune.fe.stream_max_concurrent, quic_tune.fe.stream_max_total) :
+ server ? quic_tune.fe.stream_max_concurrent : quic_tune.be.stream_max_concurrent;
/* TODO value used to conform with HTTP/3, should be derived from app_ops */
const int max_streams_uni = 3;