| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Three ways an HTTP/2 request could stop making progress without the caller
ever hearing back:
1. A stream that closed with no response and no error had its queue slot
freed by completeRequestStream() without anyone calling
request.onResponseError(), so the request simply vanished. This is
reachable after a GOAWAY: detachRequestStreamForClose() releases the
request-facing listeners and installs `once('error', noop)`, but leaves
the 'close' listener and kRequestStreamState in place, so the abandoned
stream later completes — and splices out — the request that has since
been requeued onto another session. severRequestStream() now unbinds
such a stream for good, and completeRequestStream() errors a request
whose stream closed before the response was complete.
2. A peer that refuses a request with GOAWAY(lastStreamID = 0) had it
requeued with no attempt budget, so a peer that keeps refusing turned
one request into an unbounded connect/refuse/reconnect loop that never
settled. Refusals now count against MAX_REFUSED_ATTEMPTS.
3. A peer may advertise SETTINGS_MAX_CONCURRENT_STREAMS = 0 to refuse new
streams (RFC 9113 6.5.2). busy() then reports the client as permanently
busy, and queued requests can neither open a stream — so no per-stream
timeout covers them — nor trigger a reconnect, so the SETTINGS frame
that would lift the limit can never arrive. headersTimeout now covers a
request that cannot be sent at all, and the unusable session is dropped
so the next request gets a fresh connection.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A49JamgF2TkZHu5h58ChUM
Signed-off-by: Matteo Collina <hello@matteocollina.com>
Codecov Report❌ Patch coverage is 96.72131% with 4 lines in your changes missing coverage. Please review.
@@ Coverage Diff @@
## main #5603 +/- ##
==========================================
+ Coverage 93.49% 93.50% +0.01%
==========================================
Files 110 110
Lines 38281 38424 +143
==========================================
+ Hits 35789 35929 +140
- Misses 2492 2495 +3 ☔ View full report in Codecov by Harness.
|
Sorry, something went wrong.
RST_STREAM(CANCEL) received before the response is the one reset code Node reports as a bare 'close' on the client stream -- no 'end' (unlike NO_ERROR) and no 'error' (unlike PROTOCOL_ERROR / INTERNAL_ERROR / REFUSED_STREAM) -- and destroying the stream unenrolls its timeout, so no 'timeout' follows either. That is the deterministic path into the case completeRequestStream() did not handle, and it is reachable against ordinary infrastructure: a proxy sends this upstream whenever its own downstream client goes away. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A49JamgF2TkZHu5h58ChUM Signed-off-by: Matteo Collina <hello@matteocollina.com>
Two follow-ups to the rebase onto main. RFC 9113 section 8.7 says a client SHOULD NOT automatically retry a request more than once. The REFUSED_STREAM path already followed that; the GOAWAY replay budget introduced in #5603 allowed three. Lower it to one so both refusal signals behave the same, and rename the budget after the signal it belongs to now that REFUSED_STREAM retries live in this file too: MAX_REFUSED_ATTEMPTS -> MAX_GOAWAY_REPLAY_ATTEMPTS kRefusedAttempts -> kGoAwayReplayAttempts A peer that refuses every connection now opens two connections rather than four before the request fails, so tighten the regression bound accordingly. retryRefusedStream() also open-coded the stream detach that detachRequestStreamForClose() already performs. Reuse the helper: it drops the 'close' listener rather than relying only on the nulled state, and it guards the stream-count decrement, so the abandoned attempt cannot touch the retried request. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A49JamgF2TkZHu5h58ChUM Signed-off-by: Matteo Collina <hello@matteocollina.com>
* fix: retry refused HTTP/2 streams Signed-off-by: Matteo Collina <hello@matteocollina.com> * fix(h2): allow one automatic replay per RFC 9113 8.7 Two follow-ups to the rebase onto main. RFC 9113 section 8.7 says a client SHOULD NOT automatically retry a request more than once. The REFUSED_STREAM path already followed that; the GOAWAY replay budget introduced in #5603 allowed three. Lower it to one so both refusal signals behave the same, and rename the budget after the signal it belongs to now that REFUSED_STREAM retries live in this file too: MAX_REFUSED_ATTEMPTS -> MAX_GOAWAY_REPLAY_ATTEMPTS kRefusedAttempts -> kGoAwayReplayAttempts A peer that refuses every connection now opens two connections rather than four before the request fails, so tighten the regression bound accordingly. retryRefusedStream() also open-coded the stream detach that detachRequestStreamForClose() already performs. Reuse the helper: it drops the 'close' listener rather than relying only on the nulled state, and it guards the stream-count decrement, so the abandoned attempt cannot touch the retried request. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A49JamgF2TkZHu5h58ChUM Signed-off-by: Matteo Collina <hello@matteocollina.com> --------- Signed-off-by: Matteo Collina <hello@matteocollina.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bumps [undici](https://github.com/nodejs/undici) from 8.5.0 to 8.10.0. Release notes *Sourced from [undici's releases](https://github.com/nodejs/undici/releases).* > v8.10.0 > ------- > > What's Changed > -------------- > > * feat: namespace h2 options by [`@metcoder95`](https://github.com/metcoder95) in [nodejs/undici#5498](https://redirect.github.com/nodejs/undici/pull/5498) > * test: update WPT expectations by [`@mcollina`](https://github.com/mcollina) in [nodejs/undici#5587](https://redirect.github.com/nodejs/undici/pull/5587) > * test: add cache/dedupe + dns re-dispatch integration tests by [`@GiHoon1123`](https://github.com/GiHoon1123) in [nodejs/undici#5535](https://redirect.github.com/nodejs/undici/pull/5535) > * fix(websocket): support process.unref by [`@mcollina`](https://github.com/mcollina) in [nodejs/undici#5578](https://redirect.github.com/nodejs/undici/pull/5578) > * fix(h2): ensure every request settles by [`@mcollina`](https://github.com/mcollina) in [nodejs/undici#5603](https://redirect.github.com/nodejs/undici/pull/5603) > * fix(readable): consume a body whose end has already been emitted by [`@marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5617](https://redirect.github.com/nodejs/undici/pull/5617) > * fix(retry): skip the content-length checkpoint for HEAD and for a 206 without content-range by [`@marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5610](https://redirect.github.com/nodejs/undici/pull/5610) > * fix: revert idle socket validation to setTimeout(0) to prevent stall on idle event loop by [`@marceli1404`](https://github.com/marceli1404) in [nodejs/undici#5606](https://redirect.github.com/nodejs/undici/pull/5606) > * fix(env-http-proxy-agent): match bare IPv6 addresses in no\_proxy by [`@marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5623](https://redirect.github.com/nodejs/undici/pull/5623) > * test: handle aggregate balanced pool errors by [`@marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5377](https://redirect.github.com/nodejs/undici/pull/5377) > * fix(readable): keep body bytes that arrive after setEncoding() by [`@mcollina`](https://github.com/mcollina) in [nodejs/undici#5620](https://redirect.github.com/nodejs/undici/pull/5620) > * fix(socks5): evict unused origin pools by [`@Kkartik14`](https://github.com/Kkartik14) in [nodejs/undici#5595](https://redirect.github.com/nodejs/undici/pull/5595) > * fix: skip deduplication for upgrade requests by [`@Ram-blip`](https://github.com/Ram-blip) in [nodejs/undici#5593](https://redirect.github.com/nodejs/undici/pull/5593) > * fix(retry): forward informational responses by [`@mcollina`](https://github.com/mcollina) in [nodejs/undici#5625](https://redirect.github.com/nodejs/undici/pull/5625) > * fix(mock): non-string path matchers under ignoreTrailingSlash, and DataView reply bodies by [`@marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5619](https://redirect.github.com/nodejs/undici/pull/5619) > * fix(interceptors): cache() and deduplicate() silently inert on Client/Pool without opts.origin by [`@marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5628](https://redirect.github.com/nodejs/undici/pull/5628) > * build(deps): bump ossf/scorecard-action from 2.4.3 to 2.4.4 by [`@dependabot`](https://github.com/dependabot)[bot] in [nodejs/undici#5633](https://redirect.github.com/nodejs/undici/pull/5633) > * build(deps): bump github/codeql-action/init from 4.36.2 to 4.37.3 by [`@dependabot`](https://github.com/dependabot)[bot] in [nodejs/undici#5634](https://redirect.github.com/nodejs/undici/pull/5634) > * build(deps): bump actions/setup-node from 6.4.0 to 7.0.0 by [`@dependabot`](https://github.com/dependabot)[bot] in [nodejs/undici#5636](https://redirect.github.com/nodejs/undici/pull/5636) > * fix(mock): emit request body lifecycle hooks by [`@marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5367](https://redirect.github.com/nodejs/undici/pull/5367) > * fix(h2): detach upgrade close handler after GOAWAY by [`@pacocartones`](https://github.com/pacocartones) in [nodejs/undici#5641](https://redirect.github.com/nodejs/undici/pull/5641) > * fix: retry refused HTTP/2 streams by [`@mcollina`](https://github.com/mcollina) in [nodejs/undici#5598](https://redirect.github.com/nodejs/undici/pull/5598) > * fix: preserve DNS origin hostname on sockets by [`@cyphercodes`](https://github.com/cyphercodes) in [nodejs/undici#5577](https://redirect.github.com/nodejs/undici/pull/5577) > > New Contributors > ---------------- > > * [`@marceli1404`](https://github.com/marceli1404) made their first contribution in [nodejs/undici#5606](https://redirect.github.com/nodejs/undici/pull/5606) > * [`@Kkartik14`](https://github.com/Kkartik14) made their first contribution in [nodejs/undici#5595](https://redirect.github.com/nodejs/undici/pull/5595) > * [`@pacocartones`](https://github.com/pacocartones) made their first contribution in [nodejs/undici#5641](https://redirect.github.com/nodejs/undici/pull/5641) > * [`@cyphercodes`](https://github.com/cyphercodes) made their first contribution in [nodejs/undici#5577](https://redirect.github.com/nodejs/undici/pull/5577) > > **Full Changelog**: <nodejs/undici@v8.9.0...v8.10.0> > > v8.9.0 > ------ > > ⚠️ Security fixes > ----------------- > > ### High severity > > * [GHSA-4cwx-7wf7-3272](GHSA-4cwx-7wf7-3272): malformed qualified `private` Cache-Control directives could cause cross-user information disclosure in shared caches or a parse-time crash. The cache parser now treats empty qualified directives conservatively and safely handles mixed qualified and unqualified directives. Fixed by [4fe5bc5f](nodejs/undici@4fe5bc5) with regression coverage in [9f09b49a](nodejs/undici@9f09b49). > > ### Medium severity > > * [GHSA-m8rv-5g2x-5cg5](GHSA-m8rv-5g2x-5cg5): a malicious `type` property on a duck-typed blob-like HTTP/1.1 request body could inject CRLF sequences into the generated `content-type` header. Undici now coerces and validates the value before adding it to the request. Fixed by [7d3cf924](nodejs/undici@7d3cf92). > * [GHSA-jr45-8vmc-qm54](GHSA-jr45-8vmc-qm54): optional whitespace around `=` in qualified `no-cache` and `private` directives could bypass shared-cache restrictions and disclose authenticated data across users. Cache-Control parsing now normalizes these forms and applies conservative cache decisions. Fixed by [c601fff1](nodejs/undici@c601fff). > * [GHSA-8xcm-r25x-g524](GHSA-8xcm-r25x-g524): the retry interceptor could expose a stale `Content-Length` after resuming a partial response, potentially causing downstream response desynchronization, hangs, or corruption. Undici now rejects partial responses whose `Content-Length` is inconsistent with `Content-Range`. Fixed by [e11a68ed](nodejs/undici@e11a68e), with corrected fixtures in [2b3f7493](nodejs/undici@2b3f749). > * [GHSA-v3r7-h72x-cjcm](GHSA-v3r7-h72x-cjcm): unsanitized `domain` and `unparsed` values passed to `setCookie()` could inject cookie attributes. Undici now validates cookie domains, paths, and unparsed attributes more strictly. Fixed by [10d93fc3](nodejs/undici@10d93fc). > > Additional hardening > -------------------- ... (truncated) Commits * [`c8d80e6`](nodejs/undici@c8d80e6) Bumped v8.10.0 ([#5644](https://redirect.github.com/nodejs/undici/issues/5644)) * [`66923b4`](nodejs/undici@66923b4) fix: preserve DNS origin hostname on sockets ([#5577](https://redirect.github.com/nodejs/undici/issues/5577)) * [`3926499`](nodejs/undici@3926499) fix: retry refused HTTP/2 streams ([#5598](https://redirect.github.com/nodejs/undici/issues/5598)) * [`73d6e9e`](nodejs/undici@73d6e9e) fix(h2): detach upgrade close handler after GOAWAY ([#5641](https://redirect.github.com/nodejs/undici/issues/5641)) * [`b111adb`](nodejs/undici@b111adb) fix(mock): emit request body lifecycle hooks ([#5367](https://redirect.github.com/nodejs/undici/issues/5367)) * [`ae4a3e3`](nodejs/undici@ae4a3e3) build(deps): bump actions/setup-node from 6.4.0 to 7.0.0 ([#5636](https://redirect.github.com/nodejs/undici/issues/5636)) * [`ec3fbf1`](nodejs/undici@ec3fbf1) build(deps): bump github/codeql-action/init from 4.36.2 to 4.37.3 ([#5634](https://redirect.github.com/nodejs/undici/issues/5634)) * [`2151720`](nodejs/undici@2151720) build(deps): bump ossf/scorecard-action from 2.4.3 to 2.4.4 ([#5633](https://redirect.github.com/nodejs/undici/issues/5633)) * [`b96a116`](nodejs/undici@b96a116) fix(interceptors): allow interceptors without opts.origin ([#5628](https://redirect.github.com/nodejs/undici/issues/5628)) * [`a18ef2d`](nodejs/undici@a18ef2d) fix(mock): non-string path matchers under ignoreTrailingSlash, and DataView r... * Additional commits viewable in [compare view](nodejs/undici@v8.5.0...v8.10.0) [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- Dependabot commands and options You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/ArcadeData/arcadedb/network/alerts).
…p ci] Bumps [undici](https://github.com/nodejs/undici) from 8.5.0 to 8.10.0. Release notes *Sourced from [undici's releases](https://github.com/nodejs/undici/releases).* > v8.10.0 > ------- > > What's Changed > -------------- > > * feat: namespace h2 options by [`@metcoder95`](https://github.com/metcoder95) in [nodejs/undici#5498](https://redirect.github.com/nodejs/undici/pull/5498) > * test: update WPT expectations by [`@mcollina`](https://github.com/mcollina) in [nodejs/undici#5587](https://redirect.github.com/nodejs/undici/pull/5587) > * test: add cache/dedupe + dns re-dispatch integration tests by [`@GiHoon1123`](https://github.com/GiHoon1123) in [nodejs/undici#5535](https://redirect.github.com/nodejs/undici/pull/5535) > * fix(websocket): support process.unref by [`@mcollina`](https://github.com/mcollina) in [nodejs/undici#5578](https://redirect.github.com/nodejs/undici/pull/5578) > * fix(h2): ensure every request settles by [`@mcollina`](https://github.com/mcollina) in [nodejs/undici#5603](https://redirect.github.com/nodejs/undici/pull/5603) > * fix(readable): consume a body whose end has already been emitted by [`@marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5617](https://redirect.github.com/nodejs/undici/pull/5617) > * fix(retry): skip the content-length checkpoint for HEAD and for a 206 without content-range by [`@marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5610](https://redirect.github.com/nodejs/undici/pull/5610) > * fix: revert idle socket validation to setTimeout(0) to prevent stall on idle event loop by [`@marceli1404`](https://github.com/marceli1404) in [nodejs/undici#5606](https://redirect.github.com/nodejs/undici/pull/5606) > * fix(env-http-proxy-agent): match bare IPv6 addresses in no\_proxy by [`@marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5623](https://redirect.github.com/nodejs/undici/pull/5623) > * test: handle aggregate balanced pool errors by [`@marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5377](https://redirect.github.com/nodejs/undici/pull/5377) > * fix(readable): keep body bytes that arrive after setEncoding() by [`@mcollina`](https://github.com/mcollina) in [nodejs/undici#5620](https://redirect.github.com/nodejs/undici/pull/5620) > * fix(socks5): evict unused origin pools by [`@Kkartik14`](https://github.com/Kkartik14) in [nodejs/undici#5595](https://redirect.github.com/nodejs/undici/pull/5595) > * fix: skip deduplication for upgrade requests by [`@Ram-blip`](https://github.com/Ram-blip) in [nodejs/undici#5593](https://redirect.github.com/nodejs/undici/pull/5593) > * fix(retry): forward informational responses by [`@mcollina`](https://github.com/mcollina) in [nodejs/undici#5625](https://redirect.github.com/nodejs/undici/pull/5625) > * fix(mock): non-string path matchers under ignoreTrailingSlash, and DataView reply bodies by [`@marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5619](https://redirect.github.com/nodejs/undici/pull/5619) > * fix(interceptors): cache() and deduplicate() silently inert on Client/Pool without opts.origin by [`@marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5628](https://redirect.github.com/nodejs/undici/pull/5628) > * build(deps): bump ossf/scorecard-action from 2.4.3 to 2.4.4 by [`@dependabot`](https://github.com/dependabot)[bot] in [nodejs/undici#5633](https://redirect.github.com/nodejs/undici/pull/5633) > * build(deps): bump github/codeql-action/init from 4.36.2 to 4.37.3 by [`@dependabot`](https://github.com/dependabot)[bot] in [nodejs/undici#5634](https://redirect.github.com/nodejs/undici/pull/5634) > * build(deps): bump actions/setup-node from 6.4.0 to 7.0.0 by [`@dependabot`](https://github.com/dependabot)[bot] in [nodejs/undici#5636](https://redirect.github.com/nodejs/undici/pull/5636) > * fix(mock): emit request body lifecycle hooks by [`@marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5367](https://redirect.github.com/nodejs/undici/pull/5367) > * fix(h2): detach upgrade close handler after GOAWAY by [`@pacocartones`](https://github.com/pacocartones) in [nodejs/undici#5641](https://redirect.github.com/nodejs/undici/pull/5641) > * fix: retry refused HTTP/2 streams by [`@mcollina`](https://github.com/mcollina) in [nodejs/undici#5598](https://redirect.github.com/nodejs/undici/pull/5598) > * fix: preserve DNS origin hostname on sockets by [`@cyphercodes`](https://github.com/cyphercodes) in [nodejs/undici#5577](https://redirect.github.com/nodejs/undici/pull/5577) > > New Contributors > ---------------- > > * [`@marceli1404`](https://github.com/marceli1404) made their first contribution in [nodejs/undici#5606](https://redirect.github.com/nodejs/undici/pull/5606) > * [`@Kkartik14`](https://github.com/Kkartik14) made their first contribution in [nodejs/undici#5595](https://redirect.github.com/nodejs/undici/pull/5595) > * [`@pacocartones`](https://github.com/pacocartones) made their first contribution in [nodejs/undici#5641](https://redirect.github.com/nodejs/undici/pull/5641) > * [`@cyphercodes`](https://github.com/cyphercodes) made their first contribution in [nodejs/undici#5577](https://redirect.github.com/nodejs/undici/pull/5577) > > **Full Changelog**: <nodejs/undici@v8.9.0...v8.10.0> > > v8.9.0 > ------ > > ⚠️ Security fixes > ----------------- > > ### High severity > > * [GHSA-4cwx-7wf7-3272](GHSA-4cwx-7wf7-3272): malformed qualified `private` Cache-Control directives could cause cross-user information disclosure in shared caches or a parse-time crash. The cache parser now treats empty qualified directives conservatively and safely handles mixed qualified and unqualified directives. Fixed by [4fe5bc5f](nodejs/undici@4fe5bc5) with regression coverage in [9f09b49a](nodejs/undici@9f09b49). > > ### Medium severity > > * [GHSA-m8rv-5g2x-5cg5](GHSA-m8rv-5g2x-5cg5): a malicious `type` property on a duck-typed blob-like HTTP/1.1 request body could inject CRLF sequences into the generated `content-type` header. Undici now coerces and validates the value before adding it to the request. Fixed by [7d3cf924](nodejs/undici@7d3cf92). > * [GHSA-jr45-8vmc-qm54](GHSA-jr45-8vmc-qm54): optional whitespace around `=` in qualified `no-cache` and `private` directives could bypass shared-cache restrictions and disclose authenticated data across users. Cache-Control parsing now normalizes these forms and applies conservative cache decisions. Fixed by [c601fff1](nodejs/undici@c601fff). > * [GHSA-8xcm-r25x-g524](GHSA-8xcm-r25x-g524): the retry interceptor could expose a stale `Content-Length` after resuming a partial response, potentially causing downstream response desynchronization, hangs, or corruption. Undici now rejects partial responses whose `Content-Length` is inconsistent with `Content-Range`. Fixed by [e11a68ed](nodejs/undici@e11a68e), with corrected fixtures in [2b3f7493](nodejs/undici@2b3f749). > * [GHSA-v3r7-h72x-cjcm](GHSA-v3r7-h72x-cjcm): unsanitized `domain` and `unparsed` values passed to `setCookie()` could inject cookie attributes. Undici now validates cookie domains, paths, and unparsed attributes more strictly. Fixed by [10d93fc3](nodejs/undici@10d93fc). > > Additional hardening > -------------------- ... (truncated) Commits * [`c8d80e6`](nodejs/undici@c8d80e6) Bumped v8.10.0 ([#5644](https://redirect.github.com/nodejs/undici/issues/5644)) * [`66923b4`](nodejs/undici@66923b4) fix: preserve DNS origin hostname on sockets ([#5577](https://redirect.github.com/nodejs/undici/issues/5577)) * [`3926499`](nodejs/undici@3926499) fix: retry refused HTTP/2 streams ([#5598](https://redirect.github.com/nodejs/undici/issues/5598)) * [`73d6e9e`](nodejs/undici@73d6e9e) fix(h2): detach upgrade close handler after GOAWAY ([#5641](https://redirect.github.com/nodejs/undici/issues/5641)) * [`b111adb`](nodejs/undici@b111adb) fix(mock): emit request body lifecycle hooks ([#5367](https://redirect.github.com/nodejs/undici/issues/5367)) * [`ae4a3e3`](nodejs/undici@ae4a3e3) build(deps): bump actions/setup-node from 6.4.0 to 7.0.0 ([#5636](https://redirect.github.com/nodejs/undici/issues/5636)) * [`ec3fbf1`](nodejs/undici@ec3fbf1) build(deps): bump github/codeql-action/init from 4.36.2 to 4.37.3 ([#5634](https://redirect.github.com/nodejs/undici/issues/5634)) * [`2151720`](nodejs/undici@2151720) build(deps): bump ossf/scorecard-action from 2.4.3 to 2.4.4 ([#5633](https://redirect.github.com/nodejs/undici/issues/5633)) * [`b96a116`](nodejs/undici@b96a116) fix(interceptors): allow interceptors without opts.origin ([#5628](https://redirect.github.com/nodejs/undici/issues/5628)) * [`a18ef2d`](nodejs/undici@a18ef2d) fix(mock): non-string path matchers under ignoreTrailingSlash, and DataView r... * Additional commits viewable in [compare view](nodejs/undici@v8.5.0...v8.10.0) [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- Dependabot commands and options You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/ArcadeData/arcadedb/network/alerts).
| Back | FazBrowse Home | New Git URL |
Three ways an HTTP/2 request could stop making progress without the caller ever hearing back. All three are reachable with default options now that allowH2 defaults to true.
1. A request could be dropped silently
completeRequestStream() runs on the stream's 'close':
There was no branch for "the stream closed with no response and no error". In that case the request was spliced out of the queue and nothing ever called request.onResponseError(), so the caller's promise never settled — while the client's own stats looked perfectly healthy.
The window is reachable after a GOAWAY. detachRequestStreamForClose() → releaseRequestStream() removes the request-facing listeners and installs once('error', noop), swallowing any later error, but leaves the 'close' listener and stream[kRequestStreamState] in place. The request is requeued and re-dispatched on a fresh session, and then the old stream closes and takes the new in-flight request's queue slot with it.
severRequestStream() now unbinds an abandoned stream for good, and completeRequestStream() errors a request whose stream closed before the response was complete.
2. A refused request could be retried without bound
A peer that refuses with GOAWAY(lastStreamID = 0) is saying "I processed nothing, retry elsewhere" (RFC 9113 §6.8) — what a draining proxy or a load balancer shedding load sends. onHttp2SessionGoAway() requeued the request and reconnected immediately, with no attempt counter and no backoff, so a peer that keeps refusing turned one request into an unbounded connect → HEADERS → GOAWAY → requeue → connect loop. Measured against a peer that always refuses: ~360 reconnects/s indefinitely, the request never settling, and unrelated origins in the same process losing ~75% of their throughput.
Refusals now count against MAX_REFUSED_ATTEMPTS, after which the request fails.
3. SETTINGS_MAX_CONCURRENT_STREAMS: 0 wedged the client permanently
A peer may advertise 0 to temporarily refuse new streams (RFC 9113 §6.5.2) and raise it again later — graceful drain, overload shedding, an LB draining a backend. busy() then degenerates to running >= 0, i.e. permanently busy, and nothing covers a queued request:
resumeH2() also unrefs the session in this state, so the event loop can drain while a request is outstanding: the process exits with status 0 and the line after the await never runs.
headersTimeout now covers a request that cannot be sent at all — a request that never gets a stream has missed the same deadline as one whose headers never arrive — and the unusable session is dropped so the next request gets a fresh connection.
Tests
Three new files, 7 tests. Each was verified to fail on a pristine main checkout and pass with this change:
test/http2-goaway-refusal-storm.js carries a small frame-level h2 peer because Node's http2 server clamps lastStreamID to the last stream it actually received, so it cannot send this GOAWAY.
Existing suites: test/+(http2|h2)*.js 97 pass, npm run test:unit 1447 pass, retry + redirect interceptors 87 pass.
Not covered here
v7.x ignores headersTimeout entirely in the h2 path (only bodyTimeout arms the stream timer) — followed up separately.
🤖 Generated with Claude Code
https://claude.ai/code/session_01A49JamgF2TkZHu5h58ChUM