Skip to content

fix(h2): detach upgrade close handler after GOAWAY - #5641

Merged
mcollina merged 1 commit into
nodejs:mainfrom
pacocartones:codex/fix-h2-upgrade-goaway
Aug 3, 2026
Merged

fix(h2): detach upgrade close handler after GOAWAY#5641
mcollina merged 1 commit into
nodejs:mainfrom
pacocartones:codex/fix-h2-upgrade-goaway

Conversation

@pacocartones

@pacocartones pacocartones commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

This relates to...

Fixes #5638.

Rationale

When GOAWAY marks an upgrade stream as unaccepted, severRequestStream()
already releases its HTTP/2 session. Upgrade streams also retain
onUpgradeStreamClose, which later attempts the same release after the stream
closes. The second release dereferences a cleared session and escapes as an
uncaught exception.

Detaching that upgrade-specific close handler at sever time makes the cleanup
ownership unambiguous. The request remains on the existing GOAWAY retry/error
path and settles normally.

Changes

  • Detach onUpgradeStreamClose along with the regular request close handler.
  • Add a raw h2c regression test: GOAWAY with lastStreamID = 0 while a CONNECT
    stream is opening must settle rather than crash the process.

Validation

  • HTTP/2 core suite: 98 passed, 1 skipped.
  • ESLint passed for the changed implementation and test.

Implementation and validation used Codex assistance; I reviewed the final diff and results.

Verification

Every command below was run and its output captured verbatim. The record is reproducible — the exact commands are included so you can re-run them yourself.

red — without detaching the upgrade close handler (must fail without the fix)

$ node --test --test-timeout=35000 --test-force-exit test/issue-5638.js
[…]
ℹ duration_ms 305.762
✖ failing tests:
test at test\issue-5638.js:21:1
✖ h2 CONNECT settles when GOAWAY names an older stream (29.0696ms)
  TypeError: Cannot read properties of null (reading 'Symbol(open streams)')
      at closeStreamSession (~\AppData\Local\Temp\undici-5638-repro\lib\dispatcher\client-h2.js:767:11)
      at ClientHttp2Stream.onUpgradeStreamClose (~\AppData\Local\Temp\undici-5638-repro\lib\dispatcher\client-h2.js:781:3)
      at Object.onceWrapper (node:events:622:28)
      at ClientHttp2Stream.emit (node:events:508:28)
      at emitCloseNT (node:internal/streams/destroy:148:10)
      at emitErrorCloseNT (node:internal/streams/destroy:130:3)
      at process.processTicksAndRejections (node:internal/process/task_queues:90:21)
[exit code 1]

green — fix intact (must pass) — 3 runs, identical exit code

$ node --test --test-timeout=35000 --test-force-exit test/issue-5638.js
✔ h2 CONNECT settles when GOAWAY names an older stream (35.6737ms)
ℹ tests 1
ℹ suites 0
ℹ pass 1
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 346.9513
[exit code 0]
Environment and output hashes
so: Windows 11 (AMD64)
python: 3.13.14
node --version: v24.14.1
git rev-parse HEAD: 964ce931029fdcd48fca24adbc15c42f58b82162
git status --short:
sha256(red — without detaching the upgrade close handler run 1) = 8fcf06da733ec7cd…  exit 1  0.43s
sha256(green — fix intact run 1) = 6d0099dbf983e900…  exit 0  0.47s
sha256(green — fix intact run 2) = 8848a52d1b4728cc…  exit 0  0.48s
sha256(green — fix intact run 3) = 8f41e847e930672b…  exit 0  0.43s
acta: 0fba181ecbcf4801

Status

  • I have read and agreed to the Developer's Certificate of Origin.
  • Tested.
  • Documented by regression coverage.

Signed-off-by: pacocartones <manusanchezhl@gmail.com>
@mcollina

mcollina commented Aug 3, 2026

Copy link
Copy Markdown
Member

why is it a draft?

@pacocartones
pacocartones marked this pull request as ready for review August 3, 2026 10:42
@pacocartones

Copy link
Copy Markdown
Contributor Author

It’s now ready for review — thanks!

@codecov-commenter

codecov-commenter commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.40%. Comparing base (b111adb) to head (964ce93).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5641      +/-   ##
==========================================
- Coverage   93.40%   93.40%   -0.01%     
==========================================
  Files         110      110              
  Lines       38645    38648       +3     
==========================================
+ Hits        36098    36099       +1     
- Misses       2547     2549       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 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.

@mcollina mcollina left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@mcollina
mcollina merged commit 73d6e9e into nodejs:main Aug 3, 2026
34 of 37 checks passed
@github-actions github-actions Bot mentioned this pull request Aug 3, 2026
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.

h2: CONNECT crashes the process when the peer sends GOAWAY naming an older stream

3 participants