From 08aa12392d80e807bb697060e9397f7dc58f3415 Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Fri, 12 Jun 2026 15:40:46 +0200 Subject: [PATCH] BUG/MINOR: quic: fix rxbuf settings on backend side QUIC flow control on bidirectional streams ensure that the peer cannot emit more than what haproxy has allowed, which guarantees that buffering is under controlled on the receiver side. This limit is first announced on the transport parameter via initial_max_stream_data_bidi_remote which is derived from configuration value. QUIC MUX calculation for its streams is then directly based on the transport parameter. This mechanism works as expected on the frontend side, as in this case all exchanges occur on remote streams opened by the opposite side. However, this is not working as expected on the backend side, as in this case transfers occur on streams opened locally by haproxy as the client. Thus, configuration has no impact on backend side rxbuf which remains set to a single buffer, causing important latency when retrieving large objects. This patch removes this limitation on the backend side by adjusting quic_transport_params_init(). If parameter is false, limitation is set for initial_max_stream_data_bidi_local TP. This must be backported up to 3.3. --- src/quic_tp.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/quic_tp.c b/src/quic_tp.c index 75243b44d..b38c5318a 100644 --- a/src/quic_tp.c +++ b/src/quic_tp.c @@ -58,6 +58,7 @@ void quic_transport_params_init(struct quic_transport_params *p, int server) 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; + uint64_t max_stream_data_bidi; /* Set RFC default values for unspecified parameters. */ quic_dflt_transport_params_cpy(p); @@ -81,21 +82,30 @@ void quic_transport_params_init(struct quic_transport_params *p, int server) p->initial_max_data = stream_rxbuf ? stream_rxbuf : max_streams_bidi * stream_rx_bufsz; - /* Set remote streams flow-control data limit. This is calculated as a - * ratio from max-data, then rounded up to bufsize. + /* Calculate the limit for the Rx capability of bidirectional streams. + * This is a ratio from max-data rounded up to bufsize. */ - p->initial_max_stream_data_bidi_remote = server ? + max_stream_data_bidi = server ? p->initial_max_data * quic_tune.fe.stream_data_ratio / 100 : p->initial_max_data * quic_tune.be.stream_data_ratio / 100; - p->initial_max_stream_data_bidi_remote = - stream_rx_bufsz * ((p->initial_max_stream_data_bidi_remote + (stream_rx_bufsz - 1)) / stream_rx_bufsz); + max_stream_data_bidi = + stream_rx_bufsz * ((max_stream_data_bidi + (stream_rx_bufsz - 1)) / stream_rx_bufsz); - /* Set remaining flow-control data limit. Local bidi streams are unused - * on server side. Uni streams are only used for control exchange, so - * only a single buffer for in flight data should be enough. + /* Apply bidirectional streams Rx cap. This depends on the connection + * side. On FE side, exchange will occur on remote streams; on BE side, + * exchange will occur on local streams. */ - p->initial_max_stream_data_bidi_local = stream_rx_bufsz; - p->initial_max_stream_data_uni = stream_rx_bufsz; + if (server) { + p->initial_max_stream_data_bidi_remote = max_stream_data_bidi; + p->initial_max_stream_data_bidi_local = stream_rx_bufsz; + } + else { + p->initial_max_stream_data_bidi_remote = stream_rx_bufsz; + p->initial_max_stream_data_bidi_local = max_stream_data_bidi; + } + + /* Unidirectional streams exchange should be minimal. */ + p->initial_max_stream_data_uni = stream_rx_bufsz; if (server) { p->with_stateless_reset_token = 1; -- 2.47.3