Skip to content

Commit

Permalink
fix: request signal not being aborted after request is garbage collected
Browse files Browse the repository at this point in the history
  • Loading branch information
jvaill committed Sep 26, 2024
1 parent 38ca648 commit 6370547
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .changeset/stale-tips-live.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/server-runtime": patch
---

Fix request signal not aborting after request is garbage collected
1 change: 1 addition & 0 deletions contributors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@
- jvnm-dev
- jwaltz
- jwnx
- jvaill
- kalch
- kamtugeza
- kandros
Expand Down
31 changes: 22 additions & 9 deletions packages/remix-server-runtime/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
Expand Down Expand Up @@ -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,
});
Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand Down

0 comments on commit 6370547

Please sign in to comment.