Skip to content

Commit b58b6d1

Browse files
authored
enhance(server): use native AbortController and AbortSignal (#2031)
1 parent 1953c16 commit b58b6d1

File tree

8 files changed

+89
-165
lines changed

8 files changed

+89
-165
lines changed

.changeset/metal-moles-care.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@whatwg-node/server': minor
3+
'@whatwg-node/node-fetch': patch
4+
---
5+
6+
- Use native AbortSignal and AbortController for Request.signal
7+
- Remove custom AbortSignal implementation (ServerAdapterAbortSignal)

packages/node-fetch/src/Request.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export class PonyfillRequest<TJSON = any> extends PonyfillBody<TJSON> implements
7373
this.redirect = requestInit?.redirect || 'follow';
7474
this.referrer = requestInit?.referrer || 'about:client';
7575
this.referrerPolicy = requestInit?.referrerPolicy || 'no-referrer';
76-
this._signal = requestInit?.signal;
76+
this.signal = requestInit?.signal || new AbortController().signal;
7777
this.headersSerializer = requestInit?.headersSerializer;
7878
this.duplex = requestInit?.duplex || 'half';
7979

@@ -137,16 +137,7 @@ export class PonyfillRequest<TJSON = any> extends PonyfillBody<TJSON> implements
137137

138138
agent: HTTPAgent | HTTPSAgent | false | undefined;
139139

140-
private _signal: AbortSignal | undefined | null;
141-
142-
get signal() {
143-
// Create a new signal only if needed
144-
// Because the creation of signal is expensive
145-
if (!this._signal) {
146-
this._signal = new AbortController().signal;
147-
}
148-
return this._signal!;
149-
}
140+
signal: AbortSignal;
150141

151142
clone(): PonyfillRequest<TJSON> {
152143
return this;

packages/node-fetch/src/WritableStream.ts

Lines changed: 6 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -44,55 +44,15 @@ export class PonyfillWritableStream<W = any> implements WritableStream<W> {
4444
},
4545
});
4646
this.writable = writable;
47-
let onabort: EventListener | null;
48-
let reason: any;
47+
const abortCtrl = new AbortController();
4948
const controller: WritableStreamDefaultController = {
50-
signal: {
51-
any(signals) {
52-
return AbortSignal.any([...signals]);
53-
},
54-
get reason() {
55-
return reason;
56-
},
57-
get aborted() {
58-
return writable.destroyed;
59-
},
60-
addEventListener: (_event: string, eventListener: EventListener) => {
61-
writable.once('error', eventListener);
62-
writable.once('close', eventListener);
63-
},
64-
removeEventListener: (_event: string, eventListener: EventListener) => {
65-
writable.off('error', eventListener);
66-
writable.off('close', eventListener);
67-
},
68-
dispatchEvent: (_event: Event) => {
69-
return false;
70-
},
71-
get onabort() {
72-
return onabort;
73-
},
74-
set onabort(value) {
75-
if (onabort) {
76-
this.removeEventListener('abort', onabort);
77-
}
78-
onabort = value;
79-
if (onabort) {
80-
this.addEventListener('abort', onabort);
81-
}
82-
},
83-
throwIfAborted() {
84-
if (writable.destroyed) {
85-
throw reason;
86-
}
87-
},
88-
},
89-
error: e => {
90-
this.writable.destroy(e);
49+
signal: abortCtrl.signal,
50+
error(e) {
51+
writable.destroy(e);
9152
},
9253
};
93-
this.writable.once('error', err => {
94-
reason = err;
95-
});
54+
writable.once('error', err => abortCtrl.abort(err));
55+
writable.once('close', () => abortCtrl.abort());
9656
} else {
9757
this.writable = new Writable();
9858
}

packages/node-fetch/src/fetchCurl.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,7 @@ export function fetchCurl<TResponseJSON = any, TRequestJSON = any>(
3030
curlHandle.enable(CurlFeature.StreamResponse);
3131

3232
curlHandle.setStreamProgressCallback(function () {
33-
return fetchRequest['_signal']?.aborted
34-
? process.env.DEBUG
35-
? CurlProgressFunc.Continue
36-
: 1
37-
: 0;
33+
return fetchRequest.signal.aborted ? (process.env.DEBUG ? CurlProgressFunc.Continue : 1) : 0;
3834
});
3935

4036
if (fetchRequest['bodyType'] === 'String') {
@@ -92,17 +88,17 @@ export function fetchCurl<TResponseJSON = any, TRequestJSON = any>(
9288
}
9389
}
9490
}
95-
if (fetchRequest['_signal']) {
96-
fetchRequest['_signal'].addEventListener('abort', onAbort, { once: true });
91+
if (fetchRequest.signal) {
92+
fetchRequest.signal.addEventListener('abort', onAbort, { once: true });
9793
}
9894
curlHandle.once('end', function endListener() {
9995
try {
10096
curlHandle.close();
10197
} catch (e) {
10298
deferredPromise.reject(e);
10399
}
104-
if (fetchRequest['_signal']) {
105-
fetchRequest['_signal'].removeEventListener('abort', onAbort);
100+
if (fetchRequest.signal) {
101+
fetchRequest.signal.removeEventListener('abort', onAbort);
106102
}
107103
});
108104
curlHandle.once('error', function errorListener(error: any) {
@@ -127,7 +123,7 @@ export function fetchCurl<TResponseJSON = any, TRequestJSON = any>(
127123

128124
pipeline(stream, outputStream, {
129125
end: true,
130-
signal: fetchRequest['_signal'] ?? undefined,
126+
signal: fetchRequest.signal,
131127
})
132128
.then(() => {
133129
if (!stream.destroyed) {

packages/node-fetch/src/fetchNodeHttp.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,14 @@ export function fetchNodeHttp<TResponseJSON = any, TRequestJSON = any>(
4747
nodeRequest = requestFn(fetchRequest.parsedUrl, {
4848
method: fetchRequest.method,
4949
headers: nodeHeaders,
50-
signal: fetchRequest['_signal'] ?? undefined,
50+
signal: fetchRequest.signal,
5151
agent: fetchRequest.agent,
5252
});
5353
} else {
5454
nodeRequest = requestFn(fetchRequest.url, {
5555
method: fetchRequest.method,
5656
headers: nodeHeaders,
57-
signal: fetchRequest['_signal'] ?? undefined,
57+
signal: fetchRequest.signal,
5858
agent: fetchRequest.agent,
5959
});
6060
}
@@ -107,7 +107,7 @@ export function fetchNodeHttp<TResponseJSON = any, TRequestJSON = any>(
107107
}
108108
}
109109
pipeline(nodeResponse, outputStream, {
110-
signal: fetchRequest['_signal'] ?? undefined,
110+
signal: fetchRequest.signal,
111111
end: true,
112112
})
113113
.then(() => {

packages/server/src/createServerAdapter.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import {
3232
NodeResponse,
3333
normalizeNodeRequest,
3434
sendNodeResponse,
35-
ServerAdapterRequestAbortSignal,
3635
} from './utils.js';
3736
import {
3837
fakePromise,
@@ -319,7 +318,7 @@ function createServerAdapter<
319318
? completeAssign(defaultServerContext, ...ctx)
320319
: defaultServerContext;
321320

322-
const signal = new ServerAdapterRequestAbortSignal();
321+
const controller = new AbortController();
323322
const originalResEnd = res.end.bind(res);
324323
let resEnded = false;
325324
res.end = function (data: any) {
@@ -328,16 +327,16 @@ function createServerAdapter<
328327
};
329328
const originalOnAborted = res.onAborted.bind(res);
330329
originalOnAborted(function () {
331-
signal.sendAbort();
330+
controller.abort();
332331
});
333332
res.onAborted = function (cb: () => void) {
334-
signal.addEventListener('abort', cb);
333+
controller.signal.addEventListener('abort', cb, { once: true });
335334
};
336335
const request = getRequestFromUWSRequest({
337336
req,
338337
res,
339338
fetchAPI,
340-
signal,
339+
controller,
341340
});
342341
let response$: Response | Promise<Response> | undefined;
343342
try {
@@ -349,8 +348,8 @@ function createServerAdapter<
349348
return response$
350349
.catch((e: any) => handleErrorFromRequestHandler(e, fetchAPI.Response))
351350
.then(response => {
352-
if (!signal.aborted && !resEnded) {
353-
return sendResponseToUwsOpts(res, response, signal, fetchAPI);
351+
if (!controller.signal.aborted && !resEnded) {
352+
return sendResponseToUwsOpts(res, response, controller, fetchAPI);
354353
}
355354
})
356355
.catch(err => {
@@ -360,8 +359,8 @@ function createServerAdapter<
360359
});
361360
}
362361
try {
363-
if (!signal.aborted && !resEnded) {
364-
return sendResponseToUwsOpts(res, response$, signal, fetchAPI);
362+
if (!controller.signal.aborted && !resEnded) {
363+
return sendResponseToUwsOpts(res, response$, controller, fetchAPI);
365364
}
366365
} catch (err: any) {
367366
console.error(
@@ -406,13 +405,17 @@ function createServerAdapter<
406405
if (isRequestInit(initOrCtx)) {
407406
const request = new fetchAPI.Request(input, initOrCtx);
408407
const res$ = handleRequestWithWaitUntil(request, ...restOfCtx);
409-
return handleAbortSignalAndPromiseResponse(res$, (initOrCtx as RequestInit)?.signal);
408+
const signal = (initOrCtx as RequestInit).signal;
409+
if (signal) {
410+
return handleAbortSignalAndPromiseResponse(res$, signal);
411+
}
412+
return res$;
410413
}
411414
const request = new fetchAPI.Request(input);
412415
return handleRequestWithWaitUntil(request, ...maybeCtx);
413416
}
414417
const res$ = handleRequestWithWaitUntil(input, ...maybeCtx);
415-
return handleAbortSignalAndPromiseResponse(res$, (input as any)._signal);
418+
return handleAbortSignalAndPromiseResponse(res$, input.signal);
416419
};
417420

418421
const genericRequestHandler = (

0 commit comments

Comments
 (0)