Skip to content

http2: do not end compat requests reset before END_STREAM - #67061

Open
mcollina wants to merge 1 commit into
nodejs:mainfrom
mcollina:http2-compat-no-end-after-abort
Open

mcollina wants to merge 1 commit into
nodejs:mainfrom
mcollina:http2-compat-no-end-after-abort

Conversation

@mcollina

Copy link
Copy Markdown
Member

When a client resets a stream (RST_STREAM, with any error code including NO_ERROR) before sending END_STREAM, the compat Http2ServerRequest still pushes EOF from its 'close' handler and emits 'end' for the truncated body. On main, 'end' even arrives after 'error' and 'close'. node:http behaves differently: an aborted IncomingMessage emits 'aborted' and 'error' (ECONNRESET) and is destroyed, never emitting 'end'. Code that only listens for 'end' therefore processes a partial HTTP/2 request body as if it were complete.

onStreamClose() already knows whether the peer ended its side before closing, but destroy() then ends the readable side, so the compat layer can't tell afterwards. This records it on the stream, and the compat request is destroyed instead of ended in that case. Requests whose body was complete before the reset still emit 'end' as before, and request.complete keeps its documented semantics.

This changes observable behavior for compat requests reset mid-body (no more 'end'; stream.finished() and async iteration now report a premature close), so reviewers may want to weigh in on the semver label.

When the peer reset a stream before sending END_STREAM, the compat
`Http2ServerRequest` still pushed EOF from its 'close' handler, so it
emitted 'end' for a truncated body (on main even after 'error' and
'close'). Unlike http1, where an aborted `IncomingMessage` is destroyed
and never emits 'end', applications listening for 'end' then processed
the partial body as if it were complete.

Record in `onStreamClose()` that the peer closed the stream before
END_STREAM, since `destroy()` ends the readable side afterwards, and
destroy the request instead of ending it in that case. Requests whose
body was complete before the reset still end normally.

Signed-off-by: Matteo Collina <hello@matteocollina.com>
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/http2
  • @nodejs/net

@nodejs-github-bot nodejs-github-bot added http2 Issues and PRs related to the http2 subsystem. needs-ci PRs that need a full CI run. labels Oct 27, 2026
@codecov

codecov Bot commented Oct 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.65%. Comparing base (98dbf60) to head (32a0fb2).

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #67061   +/-   ##
=======================================
  Coverage   90.65%   90.65%           
=======================================
  Files         797      797           
  Lines      278268   278284   +16     
  Branches    53517    53518    +1     
=======================================
+ Hits       252261   252283   +22     
  Misses      16386    16386           
+ Partials     9621     9615    -6     
Files with missing lines Coverage Δ
lib/internal/http2/compat.js 97.28% <100.00%> (+0.02%) ⬆️
lib/internal/http2/core.js 94.95% <100.00%> (+<0.01%) ⬆️
lib/internal/http2/util.js 92.94% <100.00%> (+0.01%) ⬆️

... and 27 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Labels

http2 Issues and PRs related to the http2 subsystem. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants