Skip to content

Commit 6370547

Browse files
committed
fix: request signal not being aborted after request is garbage collected
1 parent 38ca648 commit 6370547

File tree

3 files changed

+28
-9
lines changed

3 files changed

+28
-9
lines changed

.changeset/stale-tips-live.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@remix-run/server-runtime": patch
3+
---
4+
5+
Fix request signal not aborting after request is garbage collected

contributors.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@
343343
- jvnm-dev
344344
- jwaltz
345345
- jwnx
346+
- jvaill
346347
- kalch
347348
- kamtugeza
348349
- kandros

packages/remix-server-runtime/data.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,11 @@ export async function callRouteAction({
4343
singleFetch: boolean;
4444
}) {
4545
let result = await action({
46-
request: singleFetch
47-
? stripRoutesParam(stripIndexParam(request))
48-
: stripDataParam(stripIndexParam(request)),
46+
request: makeRequestSignalGcSafe(
47+
singleFetch
48+
? stripRoutesParam(stripIndexParam(request))
49+
: stripDataParam(stripIndexParam(request))
50+
),
4951
context: loadContext,
5052
params,
5153
});
@@ -81,9 +83,11 @@ export async function callRouteLoader({
8183
singleFetch: boolean;
8284
}) {
8385
let result = await loader({
84-
request: singleFetch
85-
? stripRoutesParam(stripIndexParam(request))
86-
: stripDataParam(stripIndexParam(request)),
86+
request: makeRequestSignalGcSafe(
87+
singleFetch
88+
? stripRoutesParam(stripIndexParam(request))
89+
: stripDataParam(stripIndexParam(request))
90+
),
8791
context: loadContext,
8892
params,
8993
});
@@ -113,6 +117,15 @@ export async function callRouteLoader({
113117
return isResponse(result) ? result : json(result);
114118
}
115119

120+
function makeRequestSignalGcSafe(request: Request) {
121+
// `undici` wraps the signal passed to the `Request` constructor. When the
122+
// request object is garbage collected, the signal stops working. This is
123+
// problematic when a loader or action is waiting for a signal to be aborted.
124+
// To fix this, we hold a reference to the request in the signal.
125+
Object.defineProperty(request.signal, "__request", request);
126+
return request;
127+
}
128+
116129
// TODO: Document these search params better
117130
// and stop stripping these in V2. These break
118131
// support for running in a SW and also expose
@@ -136,7 +149,7 @@ function stripIndexParam(request: Request) {
136149
method: request.method,
137150
body: request.body,
138151
headers: request.headers,
139-
signal: request.signal,
152+
signal: makeRequestSignalGcSafe(request).signal,
140153
};
141154

142155
if (init.body) {
@@ -153,7 +166,7 @@ function stripDataParam(request: Request) {
153166
method: request.method,
154167
body: request.body,
155168
headers: request.headers,
156-
signal: request.signal,
169+
signal: makeRequestSignalGcSafe(request).signal,
157170
};
158171

159172
if (init.body) {
@@ -170,7 +183,7 @@ function stripRoutesParam(request: Request) {
170183
method: request.method,
171184
body: request.body,
172185
headers: request.headers,
173-
signal: request.signal,
186+
signal: makeRequestSignalGcSafe(request).signal,
174187
};
175188

176189
if (init.body) {

0 commit comments

Comments
 (0)