From 1e3a8264c83e988e79589189364d9c9365076978 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 8 Nov 2026 14:23:11 +0000 Subject: [PATCH 1/4] chore(deps): update dependency undici to v8.12.4 --- pnpm-lock.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 92476cc6..b8fd39b4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -202,7 +202,7 @@ importers: version: 4.41.0 undici: specifier: ^8.10.0 - version: 8.12.2 + version: 8.12.4 ylru: specifier: ^2.0.0 version: 2.0.0 @@ -1702,8 +1702,8 @@ packages: undici-types@8.9.0: resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} - undici@8.12.2: - resolution: {integrity: sha512-XtuDWVdqTLr3NAxm2ly9/hU6kUsJOgGh1RASzMBbsw6aWCOaQzHdW+QQDfdERjfJ2V5vL5pRPa4RetO9UfivDg==} + undici@8.12.4: + resolution: {integrity: sha512-8Mx+uNKKER2bA4AdtedkqRbwUCIJFr2nJckXRe5yrDqzePqwLP7VNcctN8/XdmDSbF7mS+jJKIszlL+vbam1VA==} engines: {node: '>=22.19.0'} util-deprecate@1.0.2: @@ -2911,7 +2911,7 @@ snapshots: undici-types@8.9.0: {} - undici@8.12.2: {} + undici@8.12.4: {} util-deprecate@1.0.2: {} From 9b5eee1e2d7a3399d0c76780a598b7ece3556eb0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Nov 2026 15:08:00 +0000 Subject: [PATCH 2/4] test: ignore server-side TLS socket reset in h2 timeout test Co-authored-by: fengmk2 <156269+fengmk2@users.noreply.github.com> --- test/options.timeout.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/options.timeout.test.ts b/test/options.timeout.test.ts index 618def6a..65e7d4e3 100644 --- a/test/options.timeout.test.ts +++ b/test/options.timeout.test.ts @@ -92,6 +92,12 @@ describe('options.timeout.test.ts', () => { } }); + server.on('secureConnection', (socket) => { + socket.on('error', () => { + // the client may reset the connection after the timeout + }); + }); + server.on('stream', () => { // wait for timeout }); From 8fb8f8ced3133ad7e14b61cc0fc5475383c04ca2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Nov 2026 06:36:58 +0000 Subject: [PATCH 3/4] test: swallow client socket ECONNRESET in h2 timeout test Co-authored-by: fengmk2 <156269+fengmk2@users.noreply.github.com> --- test/options.timeout.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/options.timeout.test.ts b/test/options.timeout.test.ts index 65e7d4e3..9eae84b4 100644 --- a/test/options.timeout.test.ts +++ b/test/options.timeout.test.ts @@ -1,4 +1,5 @@ import { strict as assert } from 'node:assert'; +import { subscribe, unsubscribe } from 'node:diagnostics_channel'; import { once } from 'node:events'; import { createSecureServer } from 'node:http2'; import type { AddressInfo } from 'node:net'; @@ -92,6 +93,13 @@ describe('options.timeout.test.ts', () => { } }); + // undici@8 replaces an unresponsive h2 connection after a stream timeout, which can + // surface ECONNRESET on the client socket on macOS/Windows after its listeners are gone + const onClientConnected = (message: unknown) => { + (message as { socket: NodeJS.EventEmitter }).socket.on('error', () => {}); + }; + subscribe('undici:client:connected', onClientConnected); + server.on('secureConnection', (socket) => { socket.on('error', () => { // the client may reset the connection after the timeout @@ -129,6 +137,7 @@ describe('options.timeout.test.ts', () => { }, ); } finally { + unsubscribe('undici:client:connected', onClientConnected); await httpClient.getDispatcher().close(); await new Promise((resolve) => server.close(() => resolve())); } From 1b5fe2467bf4d4e4aa2f8f53b9e68595ddd151ea Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 9 Nov 2026 14:54:21 +0800 Subject: [PATCH 4/4] fix: handle HTTP/2 timeout stream resets and correct action refs --- .github/workflows/codspeed.yml | 2 +- .github/workflows/nodejs.yml | 4 ++-- .github/workflows/pkg.pr.new.yml | 2 +- .github/workflows/release.yml | 2 +- test/options.timeout.test.ts | 22 ++++++---------------- 5 files changed, 11 insertions(+), 21 deletions(-) diff --git a/.github/workflows/codspeed.yml b/.github/workflows/codspeed.yml index 7d7056e4..538a1b29 100644 --- a/.github/workflows/codspeed.yml +++ b/.github/workflows/codspeed.yml @@ -21,7 +21,7 @@ jobs: persist-credentials: false - name: Setup Vite+ - uses: voidzero-dev/setup-vp@cab1ab9eaff52d4857875f4f2c016e36af8f2c76 # main + uses: voidzero-dev/setup-vp@cab1ab9eaff52d4857875f4f2c016e36af8f2c76 # v1.22.0 with: node-version: '26' cache: true diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 5586810c..db5f87dd 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -29,7 +29,7 @@ jobs: annotations: true - name: Setup Vite+ - uses: voidzero-dev/setup-vp@cab1ab9eaff52d4857875f4f2c016e36af8f2c76 # main + uses: voidzero-dev/setup-vp@cab1ab9eaff52d4857875f4f2c016e36af8f2c76 # v1.22.0 with: node-version: '26.13.0' cache: true @@ -77,7 +77,7 @@ jobs: persist-credentials: false - name: Setup Vite+ - uses: voidzero-dev/setup-vp@cab1ab9eaff52d4857875f4f2c016e36af8f2c76 # main + uses: voidzero-dev/setup-vp@cab1ab9eaff52d4857875f4f2c016e36af8f2c76 # v1.22.0 with: node-version: ${{ matrix.node }} cache: true diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml index b477a279..a1a02c94 100644 --- a/.github/workflows/pkg.pr.new.yml +++ b/.github/workflows/pkg.pr.new.yml @@ -18,7 +18,7 @@ jobs: persist-credentials: false - name: Setup Vite+ - uses: voidzero-dev/setup-vp@cab1ab9eaff52d4857875f4f2c016e36af8f2c76 # main + uses: voidzero-dev/setup-vp@cab1ab9eaff52d4857875f4f2c016e36af8f2c76 # v1.22.0 with: node-version: 26 cache: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 16bbd062..dda96d32 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -107,7 +107,7 @@ jobs: persist-credentials: false - name: Setup Vite+ - uses: voidzero-dev/setup-vp@cab1ab9eaff52d4857875f4f2c016e36af8f2c76 # main + uses: voidzero-dev/setup-vp@cab1ab9eaff52d4857875f4f2c016e36af8f2c76 # v1.22.0 with: # Node 24 bundles npm >= 11.5.1, required for OIDC trusted publishing. node-version: '26' diff --git a/test/options.timeout.test.ts b/test/options.timeout.test.ts index 9eae84b4..a434991b 100644 --- a/test/options.timeout.test.ts +++ b/test/options.timeout.test.ts @@ -1,5 +1,4 @@ import { strict as assert } from 'node:assert'; -import { subscribe, unsubscribe } from 'node:diagnostics_channel'; import { once } from 'node:events'; import { createSecureServer } from 'node:http2'; import type { AddressInfo } from 'node:net'; @@ -93,23 +92,15 @@ describe('options.timeout.test.ts', () => { } }); - // undici@8 replaces an unresponsive h2 connection after a stream timeout, which can - // surface ECONNRESET on the client socket on macOS/Windows after its listeners are gone - const onClientConnected = (message: unknown) => { - (message as { socket: NodeJS.EventEmitter }).socket.on('error', () => {}); - }; - subscribe('undici:client:connected', onClientConnected); - - server.on('secureConnection', (socket) => { - socket.on('error', () => { - // the client may reset the connection after the timeout + server.on('stream', (stream) => { + // Leave the request unanswered and handle the reset when the client times out. + stream.on('error', (error: NodeJS.ErrnoException) => { + if (error.code !== 'ECONNRESET') { + throw error; + } }); }); - server.on('stream', () => { - // wait for timeout - }); - server.listen(0); await once(server, 'listening'); @@ -137,7 +128,6 @@ describe('options.timeout.test.ts', () => { }, ); } finally { - unsubscribe('undici:client:connected', onClientConnected); await httpClient.getDispatcher().close(); await new Promise((resolve) => server.close(() => resolve())); }