fix(http2): do not head-of-line block HEADERS, RST_STREAM and GOAWAY behind window-exhausted DATA (#2013) > [!NOTE] > This PR was generated by an AI coding agent (Jetski) on behalf of @mosuem. `ConnectionMessageQueueOut` is a strict FIFO. Once the connection-level send window is exhausted, a `DataMessage` at the head of the queue blocks *everything* behind it, including frames that are not subject to flow control (RFC 9113 Section 6.9: *"Flow control only applies to frames that are identified as being subject to flow control. Of the frame types defined in this document, this includes only DATA frames."*). In practice: while one stream was waiting for `WINDOW_UPDATE`, no new request could be opened (`HEADERS`), no stream could be cancelled (`RST_STREAM`), and the connection could not be shut down gracefully (`GOAWAY`) until the peer granted credit. - **`lib/src/flowcontrol/connection_queues.dart`**: - `_peekSendableMessage()` replaces the "first message or nothing" rule. When the window is exhausted and the head of the queue is a `DataMessage`, it returns the first `GOAWAY`, or the first `HEADERS` / `RST_STREAM` whose stream has no earlier queued message. Skipping a message of a stream blocks every later message of that stream (and, for a `PUSH_PROMISE`, of the promised stream), so per-stream frame order is preserved: trailers never overtake their own `DATA`. - The scan is linear in the queue length, and `StreamMessageQueueOut` keeps forwarding `DATA` into this queue while the connection window is exhausted (it only checks the stream window). To avoid rescanning on every `sendData()` call, `enqueueMessage` skips `_trySendMessages()` when a `DataMessage` is enqueued while `_connectionWindow.positiveWindow.wouldBuffer` is `true` (since the new `DataMessage` cannot be sent and cannot unblock any earlier queued message). Enqueueing a non-DATA message while blocked still costs one pass over the queue. Added to `test/client_test.dart`: - `headers-and-rst-not-blocked-or-reordered-on-exhausted-window`: stream 1 exhausts the connection window; stream 3's `HEADERS` and stream 5's `HEADERS` + `RST_STREAM` must still go out, in that order, before the window is replenished. - `trailers-stay-behind-blocked-data-while-other-headers-bypass`: stream 1 has blocked `DATA` *and* queued trailers; stream 3's `HEADERS` bypass them, stream 1's trailers are only written after its `DATA`. **Before fix:** ```text 00:05 +0 -1: client-tests client-errors headers-and-rst-not-blocked-or-reordered-on-exhausted-window [E] TimeoutException after 0:00:05.000000: Test timed out after 5 seconds. 00:10 +0 -2: client-tests client-errors trailers-stay-behind-blocked-data-while-other-headers-bypass [E] TimeoutException after 0:00:05.000000: Test timed out after 5 seconds. ``` (stream 3's `HEADERS` never leave the client while stream 1 waits for credit) **After fix:** ```text 00:00 +2: All tests passed! ```
A composable, Future-based library for making HTTP requests.
package:http contains a set of high-level functions and classes that make it easy to consume HTTP resources. It's multi-platform, and supports mobile, desktop, and the browser.
| Package | Description | Version |
|---|---|---|
| cronet_http | An Android Flutter plugin that provides access to the Cronet HTTP client. | |
| cupertino_http | A macOS/iOS Flutter plugin that provides access to the Foundation URL Loading System. | |
| flutter_http_example | Demonstrates how to use package:http in a Flutter app. | — |
| http | A composable, multi-platform, Future-based API for HTTP requests. | |
| http2 | A HTTP/2 implementation in Dart. | |
| http_client_conformance_tests | A library that tests whether implementations of package:http's Client class behave as expected. | — |
| http_multi_server | A dart:io HttpServer wrapper that handles requests from multiple servers. | |
| http_parser | A platform-independent package for parsing and serializing HTTP formats. | |
| http_profile | A library used by HTTP client authors to integrate with the DevTools Network View. | |
| ok_http | An Android Flutter plugin that provides access to the OkHttp HTTP client. | |
| web_socket | Any easy-to-use library for communicating with WebSockets that has multiple implementations. | |
| web_socket_channel | A WebSocket API that enables cross-platform communication via StreamChannel. | |
| web_socket_conformance_tests | A library that tests whether implementations of package:web_socket's WebSocket class behave as expected. | — |
If you'd like to contribute to any of these packages, see the Contributing Guide.