Skip to content

WebTransport: add anticipatedConcurrentIncomingUni/BidirectionalStreams support - #4011

Merged
jesup merged 1 commit into
mainfrom
users/jesup/anticipated_incoming_streams
Oct 20, 2026
Merged

jesup merged 1 commit into
mainfrom
users/jesup/anticipated_incoming_streams

Conversation

@jesup

@jesup jesup commented Sep 20, 2026 •

Copy link
Copy Markdown
Member

Implements anticipatedConcurrentIncomingUnidirectionalStreams/anticipatedConcurrentIncomingBidirectionalStreams on the client: webtransport_set_anticipated_incoming_{uni,bidi}_streams store a per-session value and raise the connection's incoming stream limit (via Connection::set_remote_max_streams from #4010) to the sum over WebTransport sessions that are not closing, so the peer gets that headroom up front instead of waiting a round trip for MAX_STREAMS.

  • Sessions still negotiating count, since the W3C API supplies these values at construction; closing or closed sessions do not, and setting one returns InvalidStreamId.
  • The sum is capped at MAX_ANTICIPATED_INCOMING_STREAMS (10,000) so pages cannot ratchet the monotonic limit arbitrarily high; the unidirectional limit adds HTTP/3's own three control/QPACK streams on top.
  • Lowering a value never lowers the transport limit.

@jesup

jesup commented Sep 20, 2026 •

Copy link
Copy Markdown
Member Author

This PR is part of a stack of 3 bookmarks:

  1. trunk()
  2. users/jesup/cap_max_streams_frame
  3. users/jesup/add_remote_max_streams
  4. users/jesup/anticipated_incoming_streams ← this PR

Created with jj-stack

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Closing sessions can still accept ineffective updates, and the public API does not document its 10,000-stream cap.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 2 Medium severity

Open (2)
What changed in this PR

Adds WebTransport APIs for configuring anticipated concurrent incoming streams and adjusts QUIC stream limits accordingly.

Changes:

  • Tracks per-session anticipated unidirectional and bidirectional streams.
  • Computes capped connection-wide limits with HTTP/3 control-stream overhead.
  • Adds comprehensive stream-limit and lifecycle tests.
File Description
neqo-http3/​src/​connection.rs Computes and applies connection-wide stream limits.
neqo-http3/​src/​connection_client.rs Exposes public client configuration APIs.
neqo-http3/​src/​features/​extended_connect/​session.rs Stores per-session anticipated counts and closing state.
neqo-http3/​src/​features/​extended_connect/​tests/​webtransport/​mod.rs Registers the new test module.
neqo-http3/​src/​features/​extended_connect/​tests/​webtransport/​anticipated_streams.rs Tests validation, aggregation, limits, closure, and capacity.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread neqo-http3/src/connection.rs Outdated
Comment thread neqo-http3/src/connection_client.rs Outdated
@codecov

codecov Bot commented Sep 20, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.04%. Comparing base (e0f8bbf) to head (cbbb263).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4011      +/-   ##
==========================================
- Coverage   97.19%   97.04%   -0.15%     
==========================================
  Files         113      118       +5     
  Lines       41742    42161     +419     
  Branches    41742    42161     +419     
==========================================
+ Hits        40570    40917     +347     
- Misses       1156     1220      +64     
- Partials       16       24       +8     
Flag Coverage Δ
freebsd 94.67% <100.00%> (-0.13%) ⬇️
linux 97.26% <100.00%> (+0.03%) ⬆️
macos 95.44% <100.00%> (+0.02%) ⬆️
windows 95.54% <100.00%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
neqo-common 99.31% <ø> (ø)
neqo-http3 95.44% <100.00%> (+0.03%) ⬆️
neqo-qpack 97.10% <ø> (ø)
neqo-transport 98.05% <ø> (+<0.01%) ⬆️
neqo-udp 95.37% <ø> (ø)
mtu 89.13% <ø> (ø)

@codspeed

codspeed Bot commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Merging this PR will degrade performance by 1.05%

⚠️ 1 benchmark spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 2 improved benchmarks
❌ 4 regressed benchmarks
✅ 95 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
❌ Walltime walltime/pacing-true/same-seed 4.3 ms 4.5 ms -4.86%
❌ CPU Simulation simulated/pacing-true/varying-seeds 58.2 ms 60.4 ms -3.79%
❌ Walltime 1-conn/1-100mb-req (aka. Upload) 59 ms 60.3 ms -2.16%
❌ Walltime walltime/pacing-false/same-seed 3.7 ms 3.8 ms -2.05%
⚡ Walltime walltime/pacing-true/varying-seeds 4.6 ms 4.4 ms +4.65%
⚡ Walltime walltime/1000-streams/each-1-bytes 4.6 ms 4.5 ms +2.26%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing users/jesup/anticipated_incoming_streams (cbbb263) with main (e0f8bbf)

Open in CodSpeed

@mxinden

mxinden commented Sep 21, 2026

Copy link
Copy Markdown
Member

For the record, not required for Interop 2026, thus I will delay reviewing. Ping me in case I am missing something.

@mxinden

mxinden commented Sep 22, 2026

Copy link
Copy Markdown
Member

For the record, not required for Interop 2026, thus I will delay reviewing. Ping me in case I am missing something.

I stand corrected. It is required for Interop 2026.

@jesup
jesup force-pushed the users/jesup/add_remote_max_streams branch from 435fa81 to 7c9dbf5 Compare September 23, 2026 06:27
@jesup
jesup requested a review from omansfeld as a code owner September 23, 2026 06:27
@jesup
jesup force-pushed the users/jesup/anticipated_incoming_streams branch from c40f3ca to b34babe Compare September 23, 2026 06:27
@github-actions

Copy link
Copy Markdown
Contributor

Performance profiles for profiler.firefox.com

Benchmarks (14)
  • neqo-bin-main: pr · base
  • neqo-common-decoder: pr · base
  • neqo-http3-streams_simulated: pr · base
  • neqo-http3-streams_walltime: pr · base
  • neqo-transport-frame_decode: pr · base
  • neqo-transport-min_bandwidth: pr · base
  • neqo-transport-pacer: pr · base
  • neqo-transport-packet_codec: pr · base
  • neqo-transport-range_tracker: pr · base
  • neqo-transport-rx_stream_orderer: pr · base
  • neqo-transport-send_streams: pr · base
  • neqo-transport-sent_packets: pr · base
  • neqo-transport-transfer_simulated: pr · base
  • neqo-transport-transfer_walltime: pr · base
Comparisons (5)

@mxinden mxinden left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future, we might want to update the transport stream limit on session closing. I don't think introducing the complexity today is worth it, i.e. pull request is fine as is.

Comment thread neqo-http3/src/webtransport.rs Outdated
Comment thread neqo-http3/src/webtransport.rs Outdated
Comment thread neqo-http3/src/connection.rs
@jesup
jesup force-pushed the users/jesup/add_remote_max_streams branch from d9d75c2 to dff942e Compare October 16, 2026 14:51
@jesup
jesup force-pushed the users/jesup/anticipated_incoming_streams branch from df25786 to abf9452 Compare October 16, 2026 14:51
@jesup
jesup force-pushed the users/jesup/add_remote_max_streams branch from dff942e to c7a0cd2 Compare October 17, 2026 17:08
@jesup
jesup force-pushed the users/jesup/anticipated_incoming_streams branch from abf9452 to b9ffdcb Compare October 17, 2026 17:08
@jesup
jesup force-pushed the users/jesup/add_remote_max_streams branch from c7a0cd2 to d59950a Compare October 19, 2026 15:49
@jesup
jesup force-pushed the users/jesup/anticipated_incoming_streams branch from b9ffdcb to f46dc00 Compare October 19, 2026 15:49
@jesup
jesup force-pushed the users/jesup/add_remote_max_streams branch from d59950a to 14f49ee Compare October 19, 2026 17:41
@jesup
jesup force-pushed the users/jesup/anticipated_incoming_streams branch from f46dc00 to 00989db Compare October 19, 2026 17:41
@jesup
jesup force-pushed the users/jesup/add_remote_max_streams branch from 14f49ee to 8ff9868 Compare October 20, 2026 05:33
@jesup
jesup force-pushed the users/jesup/anticipated_incoming_streams branch from 00989db to 0b55226 Compare October 20, 2026 05:33
@jesup
jesup force-pushed the users/jesup/add_remote_max_streams branch from 8ff9868 to c6eaaef Compare October 20, 2026 12:32
@jesup
jesup force-pushed the users/jesup/anticipated_incoming_streams branch from 0b55226 to 06b7327 Compare October 20, 2026 12:32
@mxinden

mxinden commented Oct 20, 2026

Copy link
Copy Markdown
Member

@jesup friendly ping for the comments above.

Base automatically changed from users/jesup/add_remote_max_streams to main October 20, 2026 15:54
…ms support

The spec lets an application supply, at session construction, how
many incoming streams of each type it expects the peer to open
concurrently, so the transport can grant that much headroom up front
rather than only after the peer bumps into the default limit and
waits a round trip for MAX_STREAMS.

webtransport_set_anticipated_incoming_{uni,bidi}_streams stores the
per-session value and recomputes the connection-wide total across
every not-yet-closed WebTransport session, clamped to
MAX_ANTICIPATED_INCOMING_STREAMS so an application cannot ratchet the
advertised limit arbitrarily high (it is monotonic and never comes
back down as sessions close). The unidirectional total also adds
HTTP3_UNI_CONTROL_STREAMS, since HTTP/3's own control and QPACK
streams draw from the same limit. Closed sessions are excluded from
the sum; sessions still negotiating are included, since that is
exactly when the API supplies this value.

Client-only: the anticipated-streams attributes are on the
WebTransport interface, which has no server-side equivalent.
@jesup
jesup force-pushed the users/jesup/anticipated_incoming_streams branch from 06b7327 to cbbb263 Compare October 20, 2026 17:34
@jesup
jesup enabled auto-merge October 20, 2026 17:36

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No findings.

Comment thread neqo-http3/src/connection.rs Dismissed
@github-actions

Copy link
Copy Markdown
Contributor

Client/server transfer results

Performance differences relative to e0f8bbf.

Transfer of 33554432 bytes over loopback, min. 100 runs. All unit-less numbers are in milliseconds.

Client vs. server Mean±σ Min–Max Median±MAD MiB/s±σ ΔMedian
google-neqo-cubic 81.1 ± 0.5 80.0 – 82.1 81.1 ± 0.6 394.7 ± 2.5 💔 +1.4 (+1.7%)
neqo-neqo-cubic 23.4 ± 0.1 23.1 – 23.8 23.4 ± 0.2 1366.1 ± 8.5 💚 -0.3 (-1.2%)
neqo-neqo-newreno 24.0 ± 0.2 23.7 – 24.5 24.0 ± 0.2 1331.2 ± 8.6 💔 +0.3 (+1.2%)

Table above only shows statistically significant changes. See all results below.

All results

Transfer of 33554432 bytes over loopback, min. 100 runs. All unit-less numbers are in milliseconds.

Client vs. server Mean±σ Min–Max Median±MAD MiB/s±σ ΔMedian
google-google 155.5 ± 0.5 154.1 – 156.9 155.5 ± 0.6 205.8 ± 0.7
google-neqo-cubic 81.1 ± 0.5 80.0 – 82.1 81.1 ± 0.6 394.7 ± 2.5 💔 +1.4 (+1.7%)
neqo-google-cubic ⚠️ 243.6 ± 43.3 205.1 – 330.5 208.7 ± 3.6 131.4 ± 23.4 -39.2 (-15.8%)
neqo-neqo-cubic 23.4 ± 0.1 23.1 – 23.8 23.4 ± 0.2 1366.1 ± 8.5 💚 -0.3 (-1.2%)
neqo-neqo-cubic-nopacing 23.1 ± 0.2 22.9 – 23.7 23.1 ± 0.2 1383.5 ± 9.3 -0.2 (-0.9%)
neqo-neqo-newreno 24.0 ± 0.2 23.7 – 24.5 24.0 ± 0.2 1331.2 ± 8.6 💔 +0.3 (+1.2%)
neqo-neqo-newreno-nopacing 23.1 ± 0.2 22.8 – 23.5 23.1 ± 0.1 1385.3 ± 9.2 -0.1 (-0.5%)
neqo-quiche-cubic 34.4 ± 0.4 33.8 – 35.4 34.4 ± 0.4 929.0 ± 10.5 +0.0 (+0.0%)
neqo-s2n-cubic 44.6 ± 0.2 44.2 – 45.0 44.6 ± 0.3 717.2 ± 3.6 -0.0 (-0.1%)
quiche-neqo-cubic 36.0 ± 0.4 34.6 – 38.0 35.9 ± 0.4 888.5 ± 11.0 -0.2 (-0.6%)
quiche-quiche 41.6 ± 0.2 41.2 – 42.3 41.6 ± 0.2 769.0 ± 4.0
s2n-neqo-cubic 127.5 ± 0.5 126.6 – 128.6 127.3 ± 0.4 251.1 ± 0.9 +0.0 (+0.0%)
s2n-s2n ⚠️ 178.6 ± 26.8 146.8 – 272.5 170.9 ± 1.7 179.2 ± 26.9

Download data for profiler.firefox.com or download performance comparison data.

@github-actions

Copy link
Copy Markdown
Contributor

Benchmark results

No significant performance differences relative to e0f8bbf.

All results
transfer/1-conn/1-100mb-req (aka. Upload)/mtu-1500
       time:   [60.218 ms 60.280 ms 60.342 ms]
       thrpt:  [1.6184 GiB/s 1.6201 GiB/s 1.6217 GiB/s]
transfer/1-conn/1-100mb-resp (aka. Download)/mtu-1500
       time:   [60.595 ms 60.642 ms 60.691 ms]
       thrpt:  [1.6091 GiB/s 1.6104 GiB/s 1.6116 GiB/s]
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high mild
transfer/1-conn/1-1b-resp (aka. HPS)/mtu-1500
       time:   [3.5476 ms 3.5482 ms 3.5489 ms]
       thrpt:  [281.78   B/s 281.83   B/s 281.88   B/s]
Found 5 outliers among 100 measurements (5.00%)
3 (3.00%) high mild
2 (2.00%) high severe
transfer/1-conn/10_000-parallel-1b-resp (aka. RPS)/mtu-1500
       time:   [81.104 ms 81.243 ms 81.387 ms]
       thrpt:  [122.87 Kelem/s 123.09 Kelem/s 123.30 Kelem/s]
Found 3 outliers among 100 measurements (3.00%)
3 (3.00%) high mild
streams-flow-controlled/walltime/1-streams/each-4194304-bytes
       time:   [13.127 ms 13.132 ms 13.136 ms]
       thrpt:  [304.51 MiB/s 304.61 MiB/s 304.70 MiB/s]
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high mild
streams-flow-controlled/walltime/10-streams/each-1048576-bytes
       time:   [33.836 ms 33.852 ms 33.868 ms]
       thrpt:  [295.27 MiB/s 295.40 MiB/s 295.54 MiB/s]
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high mild
streams/walltime/1-streams/each-1000-bytes
       time:   [156.06 µs 156.67 µs 157.45 µs]
       thrpt:  [6.0572 MiB/s 6.0873 MiB/s 6.1109 MiB/s]
Found 2 outliers among 100 measurements (2.00%)
2 (2.00%) high mild
streams/walltime/1000-streams/each-1-bytes
       time:   [4.4608 ms 4.4654 ms 4.4700 ms]
       thrpt:  [218.47 KiB/s 218.70 KiB/s 218.92 KiB/s]
Found 5 outliers among 100 measurements (5.00%)
2 (2.00%) low mild
3 (3.00%) high mild
streams/walltime/1000-streams/each-1000-bytes
       time:   [14.802 ms 14.812 ms 14.823 ms]
       thrpt:  [64.336 MiB/s 64.384 MiB/s 64.429 MiB/s]
Found 2 outliers among 100 measurements (2.00%)
2 (2.00%) high mild
transfer/walltime/pacing-false/same-seed
       time:   [3.8967 ms 3.8990 ms 3.9016 ms]
       thrpt:  [1.0012 GiB/s 1.0019 GiB/s 1.0024 GiB/s]
transfer/walltime/pacing-false/varying-seeds
       time:   [3.9232 ms 3.9250 ms 3.9272 ms]
       thrpt:  [1018.5 MiB/s 1019.1 MiB/s 1019.6 MiB/s]
transfer/walltime/pacing-true/same-seed
       time:   [4.1942 ms 4.1962 ms 4.1986 ms]
       thrpt:  [952.70 MiB/s 953.25 MiB/s 953.70 MiB/s]
transfer/walltime/pacing-true/varying-seeds
       time:   [4.0688 ms 4.0723 ms 4.0765 ms]
       thrpt:  [981.24 MiB/s 982.24 MiB/s 983.08 MiB/s]

Instructions per cycle

Criterion reported no significant timing changes.

All benchmarks
Benchmark IPC before IPC after ΔIPC
streams-flow-controlled/walltime/1-streams/each-4194304-bytes 3.02 2.90 -4.2%
transfer/1-conn/10_000-parallel-1b-resp (aka. RPS)/mtu-1500 3.15 3.20 +1.6%
transfer/walltime/pacing-true/same-seed 2.90 2.86 -1.2%
transfer/1-conn/1-100mb-resp (aka. Download)/mtu-1500 3.17 3.19 +0.7%
streams-flow-controlled/walltime/10-streams/each-1048576-bytes 3.15 3.17 +0.6%
streams/walltime/1-streams/each-1000-bytes 2.55 2.56 +0.4%
transfer/walltime/pacing-false/same-seed 2.86 2.87 +0.4%
transfer/walltime/pacing-false/varying-seeds 2.88 2.87 -0.2%
transfer/1-conn/1-100mb-req (aka. Upload)/mtu-1500 3.13 3.12 -0.2%
transfer/walltime/pacing-true/varying-seeds 2.86 2.87 +0.2%
streams/walltime/1000-streams/each-1-bytes 3.26 3.27 +0.2%
transfer/1-conn/1-1b-resp (aka. HPS)/mtu-1500 2.22 2.22 -0.2%
streams/walltime/1000-streams/each-1000-bytes 2.96 2.96 -0.0%
Profiles for profiler.firefox.com (64)

Download data for profiler.firefox.com or download performance comparison data.

@github-actions

Copy link
Copy Markdown
Contributor

Failed Interop Tests

QUIC Interop Runner, client vs. server, differences relative to main at e0f8bbf.

neqo-pr as clientneqo-pr as server
neqo-pr vs. go-x-net: BP BA
neqo-pr vs. haproxy: BP BA
neqo-pr vs. kwik: 🚀DC ⚠️R Z L1 C1 🚀BA
neqo-pr vs. linuxquic: run cancelled after 20 min
neqo-pr vs. lsquic: L1 C1
neqo-pr vs. msquic: Z A L1 C1
neqo-pr vs. mvfst: A ⚠️BA
neqo-pr vs. neqo: Z A
neqo-pr vs. nginx: BP BA
neqo-pr vs. ngtcp2: Z L1 C1 CM
neqo-pr vs. picoquic: Z A
neqo-pr vs. quic-go: A
neqo-pr vs. quiche: ⚠️C1 BP BA
neqo-pr vs. s2n-quic: CM
neqo-pr vs. tquic: S BP BA
neqo-pr vs. xquic: S R Z A L1 C1
go-x-net vs. neqo-pr: baseline result missing
kwik vs. neqo-pr: BP BA CM
linuxquic vs. neqo-pr: run cancelled after 20 min
msquic vs. neqo-pr: CM
mvfst vs. neqo-pr: Z L1 C1 CM
neqo vs. neqo-pr: Z A
openssl vs. neqo-pr: LR M A CM
quic-go vs. neqo-pr: CM
quic-zig vs. neqo-pr: CM
quiche vs. neqo-pr: CM
quinn vs. neqo-pr: V2 CM
s2n-quic vs. neqo-pr: CM
tquic vs. neqo-pr: CM
xquic vs. neqo-pr: M ⚠️C1 CM
All results

Succeeded Interop Tests

QUIC Interop Runner, client vs. server

neqo-pr as client

neqo-pr as server

Unsupported Interop Tests

QUIC Interop Runner, client vs. server

neqo-pr as client

neqo-pr as server

@jesup
jesup added this pull request to the merge queue Oct 20, 2026
Merged via the queue into main with commit a04c990 Oct 20, 2026
307 of 312 checks passed
@jesup
jesup deleted the users/jesup/anticipated_incoming_streams branch October 20, 2026 18:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants