diff --git a/.changeset/stale-tips-live.md b/.changeset/stale-tips-live.md new file mode 100644 index 00000000000..bf6b1dd1bc6 --- /dev/null +++ b/.changeset/stale-tips-live.md @@ -0,0 +1,5 @@ +--- +"@remix-run/server-runtime": patch +--- + +Fix request signal not aborting after request is garbage collected diff --git a/contributors.yml b/contributors.yml index ef460350f43..fe74dec8fb8 100644 --- a/contributors.yml +++ b/contributors.yml @@ -343,6 +343,7 @@ - jvnm-dev - jwaltz - jwnx +- jvaill - kalch - kamtugeza - kandros diff --git a/packages/remix-server-runtime/data.ts b/packages/remix-server-runtime/data.ts index afdda2ac145..9841a6c470a 100644 --- a/packages/remix-server-runtime/data.ts +++ b/packages/remix-server-runtime/data.ts @@ -43,9 +43,11 @@ export async function callRouteAction({ singleFetch: boolean; }) { let result = await action({ - request: singleFetch - ? stripRoutesParam(stripIndexParam(request)) - : stripDataParam(stripIndexParam(request)), + request: makeRequestSignalGcSafe( + singleFetch + ? stripRoutesParam(stripIndexParam(request)) + : stripDataParam(stripIndexParam(request)) + ), context: loadContext, params, }); @@ -81,9 +83,11 @@ export async function callRouteLoader({ singleFetch: boolean; }) { let result = await loader({ - request: singleFetch - ? stripRoutesParam(stripIndexParam(request)) - : stripDataParam(stripIndexParam(request)), + request: makeRequestSignalGcSafe( + singleFetch + ? stripRoutesParam(stripIndexParam(request)) + : stripDataParam(stripIndexParam(request)) + ), context: loadContext, params, }); @@ -113,6 +117,15 @@ export async function callRouteLoader({ return isResponse(result) ? result : json(result); } +function makeRequestSignalGcSafe(request: Request) { + // `undici` wraps the signal passed to the `Request` constructor. When the + // request object is garbage collected, the signal stops working. This is + // problematic when a loader or action is waiting for a signal to be aborted. + // To fix this, we hold a reference to the request in the signal. + Object.defineProperty(request.signal, "__request", request); + return request; +} + // TODO: Document these search params better // and stop stripping these in V2. These break // support for running in a SW and also expose @@ -136,7 +149,7 @@ function stripIndexParam(request: Request) { method: request.method, body: request.body, headers: request.headers, - signal: request.signal, + signal: makeRequestSignalGcSafe(request).signal, }; if (init.body) { @@ -153,7 +166,7 @@ function stripDataParam(request: Request) { method: request.method, body: request.body, headers: request.headers, - signal: request.signal, + signal: makeRequestSignalGcSafe(request).signal, }; if (init.body) { @@ -170,7 +183,7 @@ function stripRoutesParam(request: Request) { method: request.method, body: request.body, headers: request.headers, - signal: request.signal, + signal: makeRequestSignalGcSafe(request).signal, }; if (init.body) {