Skip to content

fix(http2): make StreamException a StreamTransportException and forward ALPN errors to onError - #2009

Draft
mosuem wants to merge 1 commit into
mosum/http2-4b-no-hol-blocking-on-exhausted-windowfrom
mosum/http2-5-frame-reader-cancel-and-goaway-exception
Draft

mosuem wants to merge 1 commit into
mosum/http2-4b-no-hol-blocking-on-exhausted-windowfrom
mosum/http2-5-frame-reader-cancel-and-goaway-exception

Conversation

@mosuem

@mosuem mosuem commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Note

This PR was generated by an AI coding agent (Jetski) on behalf of @mosuem.

Summary

Two small error-handling fixes:

  1. StreamException (lib/src/sync_errors.dart): used by StreamHandler.processGoawayFrame when aborting streams with id > lastStreamId ("this stream was not processed and can therefore be retried") and by _rejectOversizedHeaders. It implemented Exception directly instead of extending StreamTransportException, so callers that catch TransportException / StreamTransportException as documented by package:http2/transport.dart missed exactly the error that tells them a retry is safe.
  2. MultiProtocolHttpServer.startServing (lib/multiprotocol_server.dart): when a client negotiated an unexpected ALPN protocol, startServing threw synchronously inside the _serverSocket.listen data callback instead of forwarding the error to onError when one was provided.

Changes

  • lib/src/sync_errors.dart: StreamException extends StreamTransportException, toString() format unchanged. StreamHandler.processStreamFrame catches the concrete subtypes (StreamClosedException, StreamRefusedException, StreamException), so its RST_STREAM error-code mapping is unaffected.
  • lib/multiprotocol_server.dart: forward the unexpected-protocol error to onError if provided.

Test Verification (Fails Before $\rightarrow$ Passes After)

goaway-terminates-nonprocessed-streams in test/client_test.dart now also asserts e is StreamTransportException.

Before fix:

00:00 +0 -1: test/client_test.dart: client-tests client-errors goaway-terminates-nonprocessed-streams [E]
  Expected: <Instance of 'StreamTransportException'>
    Actual: StreamException:<StreamException(stream id: 1): Remote end was telling us to stop. This stream was not processed and can therefore be retried (on a new connection).>
     Which: is not an instance of 'StreamTransportException'

After fix:

00:00 +1: All tests passed!

@mosuem
mosuem added this pull request to stack #2011 October 8, 2026 08:32
@mosuem
mosuem force-pushed the mosum/http2-5-frame-reader-cancel-and-goaway-exception branch from 166ae6a to 9300221 Compare October 8, 2026 10:36
@mosuem mosuem changed the title fix(http2): cancel byte stream on FrameReader cancel and make StreamException extend StreamTransportException fix(http2): make StreamException a StreamTransportException and forward ALPN errors to onError Oct 8, 2026
@mosuem
mosuem removed this pull request from stack #2011 October 8, 2026 10:38
@mosuem
mosuem changed the base branch from mosum/http2-4-control-frame-queue-and-rst-order to mosum/http2-4b-no-hol-blocking-on-exhausted-window October 8, 2026 10:38
@mosuem
mosuem added this pull request to stack #2016 October 8, 2026 10:42
@mosuem
mosuem force-pushed the mosum/http2-5-frame-reader-cancel-and-goaway-exception branch from 9300221 to 6ddca40 Compare October 8, 2026 12:04
@mosuem
mosuem force-pushed the mosum/http2-5-frame-reader-cancel-and-goaway-exception branch from 6ddca40 to a288db5 Compare October 9, 2026 07:54
@mosuem
mosuem removed this pull request from stack #2016 October 9, 2026 07:55
@mosuem
mosuem added this pull request to stack #2020 October 9, 2026 07:55
@mosuem
mosuem force-pushed the mosum/http2-5-frame-reader-cancel-and-goaway-exception branch from a288db5 to 29db67f Compare October 9, 2026 08:18
@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

PR Health

Coverage ⚠️
File Coverage
pkgs/http2/lib/multiprotocol_server.dart 💔 85 % ⬇️ 5 %
pkgs/http2/lib/src/sync_errors.dart 💚 74 % ⬆️ 2 %

This check for test coverage is informational (issues shown here will not fail the PR).

This check can be disabled by tagging the PR with skip-coverage-check.

License Headers ✔️
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
Files
no missing headers

All source files should start with a license header.

Unrelated files missing license headers
Files
pkgs/http_multi_server/test/cert.dart

This check can be disabled by tagging the PR with skip-license-check.

Breaking changes ✔️
Package Change Current Version New Version Needed Version Looking good?
http2 Non-Breaking 3.1.0 3.2.0-wip 3.2.0-wip ✔️

This check can be disabled by tagging the PR with skip-breaking-check.

Unused Dependencies ✔️
Package Status
http2 ✔️ All dependencies utilized correctly.

For details on how to fix these, see dependency_validator.

This check can be disabled by tagging the PR with skip-unused-dependencies-check.

API leaks ✔️

The following packages contain symbols visible in the public API, but not exported by the library. Export these symbols or remove them from your publicly visible API.

Package Leaked API symbol Leaking sources

This check can be disabled by tagging the PR with skip-leaking-check.

Changelog Entry ✔️
Package Changed Files

Changes to files need to be accounted for in their respective changelogs.

This check can be disabled by tagging the PR with skip-changelog-check.

…rd ALPN errors to onError

> [!NOTE]
> This PR was generated by an AI coding agent (Jetski) on behalf of @mosuem.

### Summary

Two small error-handling fixes:

1. **`StreamException` (`lib/src/sync_errors.dart`)**: used by `StreamHandler.processGoawayFrame` when aborting streams with `id > lastStreamId` ("this stream was not processed and can therefore be retried") and by `_rejectOversizedHeaders`. It implemented `Exception` directly instead of extending `StreamTransportException`, so callers that catch `TransportException` / `StreamTransportException` as documented by `package:http2/transport.dart` missed exactly the error that tells them a retry is safe.
2. **`MultiProtocolHttpServer.startServing` (`lib/multiprotocol_server.dart`)**: when a client negotiated an unexpected ALPN protocol, `startServing` threw synchronously inside the `_serverSocket.listen` data callback instead of forwarding the error to `onError` when one was provided.

### Changes
- **`lib/src/sync_errors.dart`**: `StreamException extends StreamTransportException`, `toString()` format unchanged. `StreamHandler.processStreamFrame` catches the concrete subtypes (`StreamClosedException`, `StreamRefusedException`, `StreamException`), so its `RST_STREAM` error-code mapping is unaffected.
- **`lib/multiprotocol_server.dart`**: forward the unexpected-protocol error to `onError` if provided.

### Test Verification (Fails Before $\rightarrow$ Passes After)

`goaway-terminates-nonprocessed-streams` in `test/client_test.dart` now also asserts `e is StreamTransportException`.

**Before fix:**
```text
00:00 +0 -1: test/client_test.dart: client-tests client-errors goaway-terminates-nonprocessed-streams [E]
  Expected: <Instance of 'StreamTransportException'>
    Actual: StreamException:<StreamException(stream id: 1): Remote end was telling us to stop. This stream was not processed and can therefore be retried (on a new connection).>
     Which: is not an instance of 'StreamTransportException'
```

**After fix:**
```text
00:00 +1: All tests passed!
```
@mosuem
mosuem force-pushed the mosum/http2-5-frame-reader-cancel-and-goaway-exception branch from 29db67f to e2cda6d Compare October 9, 2026 08:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant