Repository navigation
HTTP/2: peer MAX_CONCURRENT_STREAMS=0 leaves stale capacity and a LocalProtocolError #1124
Replies: 1 comment
|
Follow-up: this also reproduces with a positive limit on unchanged master Using a real loopback hyper-h2 server and public
I repeated each case three times on Linux/Python 3.12 and Windows/Python 3.12 and 3.14: all nine limit-2 controls finished, and both requests stayed pending in all nine limit-1 cases. Linux used AnyIO 4.15.1 / h2 4.4.1; Windows used AnyIO 4.11.0 / h2 4.3.0. All 32 HTTPcore package files matched the pinned commit's Git blobs; no socket/transport mocks or source changes. This is consistent with the shrink loop acquiring permits under A repair therefore needs to preserve active-response progress for positive decreases below the active count too. This adds an asyncio TCP regression case, not a validated fix or a TLS/Trio/sync/full-CI claim. |
Uh oh!
There was an error while loading. Please reload this page.
I reproduced a zero-limit edge case on current master (
10a658221deb38a4c5b16db55ab554b0bf731707) before proposing a fix.A loopback hyper-h2 server advertises 10 concurrent streams, sends a response, then advertises 0 before ending that response. The first request finishes normally. A second public
AsyncConnectionPool.request()raises:The same test with the update set to 50 completes both requests. The zero-limit peer observes only stream 1: h2 correctly rejects the extra stream locally; this is not a claim that httpcore sends invalid frames.
In
_receive_remote_settings_change,if new_max_streamsignores zero. h2 records 0 while httpcore's_max_streamsremains 10. RFC 9113 §6.5.2 permits zero as a temporary limit; §5.1.2 allows existing streams to complete after a decrease.Simply changing that guard to
is not Noneis unsafe: using the existing MockStream-style test, both asyncio and Trio block while processing the zero update before the response's final body frame. The shrink loop tries to acquire the permit still held by the response being read. This was an independent predicate-only diagnostic, not a proposed patch.What behavior would you prefer here: waiting for a later SETTINGS increase, or another pool-level handling strategy? A repair needs to preserve existing-response progress, cancellation and later 0→positive updates, rather than only removing the truthiness check. I checked related #1037 / #1035; they discuss connection capacity selection, but I did not find this exact zero-update reproduction in them. Please point me to existing work if I missed it.
Tested on Linux/Python 3.12.13, AnyIO 4.15.1, h2 4.4.1, h11 0.16.0, hpack 4.2.0 and hyperframe 6.1.0. The real TCP check is asyncio with HTTP/2 prior knowledge, not TLS or a claim about prevalence in deployed servers. AI-assisted investigation; the full-package reproductions and the counterexample above were executed independently.
Self-contained loopback TCP reproduction (no external server)
All reactions