)]}'
{
  "commit": "3b504cf09cb70ae4071d4c2549c903aad5cf4bc4",
  "tree": "1d35eb9f8b812d3eb89ffe814e9cbe6ff5c12e77",
  "parents": [
    "a641fac119ab67a82df2bc43025aa3dbdcdd59e0"
  ],
  "author": {
    "name": "Moritz",
    "email": "27859082+mosuem@users.noreply.github.com",
    "time": "Thu Oct 22 06:28:12 2026 +0200"
  },
  "committer": {
    "name": "GitHub",
    "email": "noreply@github.com",
    "time": "Thu Oct 22 04:28:12 2026 +0000"
  },
  "message": "fix(http2): never write RST_STREAM ahead of the stream\u0027s still-queued HEADERS (#2008)\n\n\u003e [!NOTE]\n\u003e This PR was generated by an AI coding agent (Jetski) on behalf of @mosuem.\n\n### Summary\n\n`StreamHandler._terminateStream` (what `TransportStream.terminate()` ends up in) wrote the `RST_STREAM(CANCEL)` straight to the `FrameWriter`, bypassing `ConnectionMessageQueueOut`. If the stream\u0027s opening `HEADERS` were still sitting in that queue — because the socket was applying backpressure, or because the queue was head-of-line blocked behind flow-controlled `DATA` of another stream — the peer received `RST_STREAM` for a stream it had never seen, which is a connection error (RFC 9113 Section 6.4: *\"RST_STREAM frames MUST NOT be sent for a stream in the \"idle\" state. If a RST_STREAM frame identifying an idle stream is received, the recipient MUST treat this as a connection error of type PROTOCOL_ERROR\"*). A client cancelling a request shortly after starting it could thus take down the whole connection.\n\n### Changes\n- **`lib/src/flowcontrol/connection_queues.dart`**: Add `ConnectionMessageQueueOut.cancelStreamMessages(streamId)`, which drops the stream\u0027s queued `DataMessage`s (they would be discarded by the peer anyway) and reports whether a `HeadersMessage` for the stream is still queued.\n- **`lib/src/streams/stream_handler.dart`**: In `_terminateStream`, enqueue a `ResetStreamMessage` behind the queued `HEADERS` when there are any; otherwise write the `RST_STREAM` directly as before.\n\n### Test Verification (Fails Before $\\rightarrow$ Passes After)\n\nAdded `rst-stream-is-sent-after-queued-headers-of-the-stream` in `test/client_test.dart`. The test keeps the client\u0027s `FrameWriter` in its \"would buffer\" state (the server\u0027s `StreamIterator` pauses the frame stream after each frame, and that pause propagates synchronously into the client\u0027s `BufferedSink`), opens a stream and cancels it immediately, then checks the wire order.\n\n**Before fix:**\n```text\n00:00 +0 -1: client-tests client-errors rst-stream-is-sent-after-queued-headers-of-the-stream [E]\n  Expected: \u003cInstance of \u0027HeadersFrame\u0027\u003e with `streamId`: \u003c3\u003e\n    Actual: \u003cInstance of \u0027RstStreamFrame\u0027\u003e\n     Which: is not an instance of \u0027HeadersFrame\u0027\n```\n\n**After fix:**\n```text\n00:00 +1: All tests passed!\n```",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "04ef724dc99ad95ffe6666fd63afd0aaa63b49ee",
      "old_mode": 33188,
      "old_path": "pkgs/http2/CHANGELOG.md",
      "new_id": "edf6fb1f0782f2d3f250188d88c188eea0792f11",
      "new_mode": 33188,
      "new_path": "pkgs/http2/CHANGELOG.md"
    },
    {
      "type": "modify",
      "old_id": "e315c08bf2c960b30bdcc720b1b0bed130a1c893",
      "old_mode": 33188,
      "old_path": "pkgs/http2/lib/src/flowcontrol/connection_queues.dart",
      "new_id": "77bb52c01a26da434bb2586423fc577dda7662fc",
      "new_mode": 33188,
      "new_path": "pkgs/http2/lib/src/flowcontrol/connection_queues.dart"
    },
    {
      "type": "modify",
      "old_id": "1120e2af039995dfb654175b6c4b60e434ff6f56",
      "old_mode": 33188,
      "old_path": "pkgs/http2/lib/src/streams/stream_handler.dart",
      "new_id": "685ba9f3c53c517aca06f8b4707faac2cd244190",
      "new_mode": 33188,
      "new_path": "pkgs/http2/lib/src/streams/stream_handler.dart"
    },
    {
      "type": "modify",
      "old_id": "344dbb0d2aec834147f17b8950320968336edb8e",
      "old_mode": 33188,
      "old_path": "pkgs/http2/test/client_test.dart",
      "new_id": "19e208d285b15bb1ae29dee10f2fc0440bc1ddd1",
      "new_mode": 33188,
      "new_path": "pkgs/http2/test/client_test.dart"
    }
  ]
}
