Fix HTTP/2 response stalls behind concurrent network reads
Summary
Fixes #1300.
Under concurrent HTTP/2 requests, a response can remain undelivered even after its events have been received and queued. Another stream may acquire the shared read lock and block on a subsequent network read, delaying an already-completed response.
Changes
- Introduce per-connection read-completion notifications, allowing waiting streams to process queued events without acquiring the read lock.
- Preserve single-reader coordination and recheck queued responses and flow-control availability before additional reads.
- Update both synchronous and asynchronous implementations.
- Add deterministic regression tests covering asyncio, Trio and readiness races.
Testing
Full test suite passed: Python 3.11–3.15.
- Ruff, mypy, packaging and documentation checks passed.
- 100% test coverage achieved.
- Generated synchronous/asynchronous code consistency verified.
- Security analysis passed.
- CodSpeed benchmarks executed, but results could not be uploaded because of fork-specific 401 authorisation.
CI: https://github.com/AdityaSreevatsaK/httpx2/actions/runs/43293561851
Security: https://github.com/AdityaSreevatsaK/httpx2/actions/runs/43293561652
Related work
PR #1298 refactors HTTP/2 stream ownership and modifies overlapping code. This change focuses specifically on coordinating network reads and waking streams with queued events.
The implementation has been prepared separately to allow review and integration with the ongoing HTTP/2 work.
Checklist
- Discussed the issue and proposed approach in #1300.
- Added regression tests for the fix.
- Verified the complete test suite and coverage.
- Kept the change focused without modifying public APIs.
Documentation was not changed because the fix corrects internal scheduling behaviour.
Prepared with AI assistance. The changes and test results have been reviewed.