diff --git a/.changeset/clean-kings-like.md b/.changeset/clean-kings-like.md new file mode 100644 index 0000000..89cb926 --- /dev/null +++ b/.changeset/clean-kings-like.md @@ -0,0 +1,5 @@ +--- +'fetch-nodeshim': patch +--- + +Limit state in which `incoming.socket` is unrefed and instead `.ref()` it when the body is being read, and `.unref()` it again when reading stops. diff --git a/src/fetch.ts b/src/fetch.ts index 6fefc12..96c5277 100644 --- a/src/fetch.ts +++ b/src/fetch.ts @@ -1,4 +1,5 @@ import { Stream, Readable, pipeline } from 'node:stream'; +import { Socket } from 'node:net'; import * as https from 'node:https'; import * as http from 'node:http'; import * as url from 'node:url'; @@ -112,6 +113,18 @@ function createResponse( return response; } +function attachRefLifetime(body: Readable, socket: Socket): void { + const { _read } = body; + body.on('close', () => { + socket.unref(); + }); + body._read = function _readRef(...args: Parameters) { + body._read = _read; + socket.ref(); + return _read.apply(this, args); + }; +} + async function _fetch( input: string | URL | Request, requestInit?: RequestInit @@ -257,6 +270,12 @@ async function _fetch( outgoing.on('error', destroy); } + // Re-ref the socket when the body starts being consumed to prevent + // early process exit, then unref when done to allow normal exit. + if (body != null) { + attachRefLifetime(body, incoming.socket); + } + resolve( createResponse(body, init, { type: 'default',