)]}'
{
  "commit": "a641fac119ab67a82df2bc43025aa3dbdcdd59e0",
  "tree": "5f3d88516e7c9ce3b4e2e177a4b684e2a56a6338",
  "parents": [
    "5d896c7c89c900a45e2cbcd30bc055d5412b5fc5"
  ],
  "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): preserve the complete response on RST_STREAM(NO_ERROR) (#2012)\n\n\u003e [!NOTE]\n\u003e This PR was generated by an AI coding agent (Jetski) on behalf of @mosuem.\n\n### Summary\n\nRFC 9113 Section 8.1: *\"A server can send a complete response prior to the client sending an entire request if the response does not depend on any portion of the request that has not been sent and received. When this is true, a server MAY request that the client abort transmission of a request without error by sending a RST_STREAM with an error code of NO_ERROR after sending a complete response.\"* grpc-go, for example, does exactly this after sending trailers on client-streaming calls.\n\nPreviously `StreamHandler._handleRstFrame` treated every `RST_STREAM` as abnormal: it called `_closeStreamAbnormally`, which dropped the messages still buffered in `ConnectionMessageQueueIn` / `StreamMessageQueueIn` (anything the application had not consumed yet, e.g. because its listener was paused) and failed the stream with `StreamTransportException (errorCode: 0)` — even though the complete response, including trailers, had already arrived.\n\n### Changes\n- **`lib/src/flowcontrol/connection_queues.dart`**: Add `ConnectionMessageQueueIn.forceDispatchStreamMessages(streamId)`, which hands the buffered messages of one stream to its `StreamMessageQueueIn` (crediting the connection window for the DATA bytes, as the regular dispatch path does).\n- **`lib/src/streams/stream_handler.dart`**: In `_handleRstFrame`, when `errorCode \u003d\u003d NO_ERROR` and the stream is `HalfClosedRemote` or `Closed` (the peer has already sent `END_STREAM`), flush the buffered messages, close the outgoing side and drop pending outgoing data, and complete the incoming side normally. `onTerminated` is still invoked with `NO_ERROR`. The stream keeps counting as active until the application has drained the preserved messages, exactly like a normally completed stream.\n\n### Test Verification (Fails Before $\\rightarrow$ Passes After)\n\nAdded `rst-stream-no-error-after-end-stream-preserves-messages` in `test/client_test.dart`: the server sends `HEADERS`, `DATA`, trailers with `END_STREAM`, `RST_STREAM(NO_ERROR)` while the client\u0027s listener is paused (a `PING`/ACK round trip is used as the barrier instead of a sleep).\n\n**Before fix:**\n```text\n00:00 +0 -1: client-tests client-errors rst-stream-no-error-after-end-stream-preserves-messages [E]\n  Expected: empty\n    Actual: [\n              StreamTransportException:HTTP/2 error: Stream error: Stream was terminated by peer (errorCode: 0).\n            ]\n```\n\n**After fix:**\n```text\n00:00 +1: All tests passed!\n```",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "cd6604edcc9ceabf5dbc113853d61943c082a3e9",
      "old_mode": 33188,
      "old_path": "pkgs/http2/CHANGELOG.md",
      "new_id": "04ef724dc99ad95ffe6666fd63afd0aaa63b49ee",
      "new_mode": 33188,
      "new_path": "pkgs/http2/CHANGELOG.md"
    },
    {
      "type": "modify",
      "old_id": "29ca79ec4ff52f4dae91b5d91435fe4ea5faf23a",
      "old_mode": 33188,
      "old_path": "pkgs/http2/lib/src/flowcontrol/connection_queues.dart",
      "new_id": "e315c08bf2c960b30bdcc720b1b0bed130a1c893",
      "new_mode": 33188,
      "new_path": "pkgs/http2/lib/src/flowcontrol/connection_queues.dart"
    },
    {
      "type": "modify",
      "old_id": "0269025efd31b30540940213a1065460bf079f58",
      "old_mode": 33188,
      "old_path": "pkgs/http2/lib/src/streams/stream_handler.dart",
      "new_id": "1120e2af039995dfb654175b6c4b60e434ff6f56",
      "new_mode": 33188,
      "new_path": "pkgs/http2/lib/src/streams/stream_handler.dart"
    },
    {
      "type": "modify",
      "old_id": "d1a404a4408a95bc23e3cd743add0f71e80cb280",
      "old_mode": 33188,
      "old_path": "pkgs/http2/test/client_test.dart",
      "new_id": "344dbb0d2aec834147f17b8950320968336edb8e",
      "new_mode": 33188,
      "new_path": "pkgs/http2/test/client_test.dart"
    }
  ]
}
