Skip to content

fix(listener): abort the request signal when an HTTP/2 client resets the stream - #404

Open
affanmomin wants to merge 1 commit into
honojs:mainfrom
affanmomin:fix/http2-abort-signal
Open

affanmomin wants to merge 1 commit into
honojs:mainfrom
affanmomin:fix/http2-abort-signal

Conversation

@affanmomin

Copy link
Copy Markdown

Fixes #398

When an HTTP/2 client cancels a request (for example Ctrl+C on curl --http2-prior-knowledge), the client resets the stream with RST_STREAM. c.req.raw.signal never fires on that reset.

The close handler in makeCloseHandler aborts the request when outgoing.writableFinished is false. On HTTP/2 that check doesn't work: once the client resets the stream, Http2ServerResponse reports writableFinished: true even though the response was never sent. Logged inside the close listener after a client RST_STREAM (CANCEL):

writableFinished: true, finished: false, req.aborted: true, stream.rstCode: 8

The handler therefore treated the reset as a normal finish and skipped the abort.

The fix also aborts when the request is an Http2ServerRequest with aborted set. Node only sets aborted when the stream is cut off before the response's writable side has ended, so a normally completed HTTP/2 response is unaffected. HTTP/1 goes through the same check as before.

Tests

  • Added two cases to the Abort request block in test/listener.test.ts. Each starts an HTTP/2 server through getRequestListener, sends a request to a handler that never resolves, then either resets the stream (NGHTTP2_CANCEL) or destroys the session. Both assert that the request signal aborts.
  • The stream-reset case fails on main ("abort signal did not fire") and passes with this change. The session case passes on both, and stays in as a regression guard.
  • Full suite: 495/495 on Node 22. listener.test.ts also passes on Node 20. lint, format, typecheck and build are clean.
  • In one full run on my machine, server.test.ts > should serve on ipv4 failed. That test uses the fixed port 3000. It passes on its own, and the next two full runs passed, so the failure was a local port collision.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

c.req.raw.signal is not triggered with node:http2

1 participant