Skip to content

Commit 6ddb214

Browse files
author
Andreu Botella
committed
feat(fetch): Support abort reasons in fetch
The spec PR for this (whatwg/fetch#1343) hasn't been finalized yet because some questions regarding service worker integration are not settled yet, but everything else seems settled, and it seems best to not wait for another Deno version to support abort reasons in the fetch API.
1 parent 4d176b7 commit 6ddb214

2 files changed

Lines changed: 70 additions & 16 deletions

File tree

cli/tests/unit/fetch_test.ts

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1272,7 +1272,60 @@ Deno.test(
12721272
} catch (error) {
12731273
assert(error instanceof DOMException);
12741274
assertEquals(error.name, "AbortError");
1275-
assertEquals(error.message, "Ongoing fetch was aborted.");
1275+
assertEquals(error.message, "The signal has been aborted");
1276+
}
1277+
},
1278+
);
1279+
1280+
Deno.test(
1281+
{ permissions: { net: true } },
1282+
async function fetchAbortWhileUploadStreamingWithReason(): Promise<void> {
1283+
const abortController = new AbortController();
1284+
const abortReason = new Error();
1285+
try {
1286+
await fetch(
1287+
"http://localhost:5552/echo_server",
1288+
{
1289+
method: "POST",
1290+
body: new ReadableStream({
1291+
pull(controller) {
1292+
abortController.abort(abortReason);
1293+
controller.enqueue(new Uint8Array([1, 2, 3, 4]));
1294+
},
1295+
}),
1296+
signal: abortController.signal,
1297+
},
1298+
);
1299+
fail("Fetch didn't reject.");
1300+
} catch (error) {
1301+
assertEquals(error, abortReason);
1302+
}
1303+
},
1304+
);
1305+
1306+
Deno.test(
1307+
{ permissions: { net: true } },
1308+
async function fetchAbortWhileUploadStreamingWithPrimitiveReason(): Promise<
1309+
void
1310+
> {
1311+
const abortController = new AbortController();
1312+
try {
1313+
await fetch(
1314+
"http://localhost:5552/echo_server",
1315+
{
1316+
method: "POST",
1317+
body: new ReadableStream({
1318+
pull(controller) {
1319+
abortController.abort("Abort reason");
1320+
controller.enqueue(new Uint8Array([1, 2, 3, 4]));
1321+
},
1322+
}),
1323+
signal: abortController.signal,
1324+
},
1325+
);
1326+
fail("Fetch didn't reject.");
1327+
} catch (error) {
1328+
assertEquals(error, "Abort reason");
12761329
}
12771330
},
12781331
);

ext/fetch/26_fetch.js

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,7 @@
8888
function createResponseBodyStream(responseBodyRid, terminator) {
8989
function onAbort() {
9090
if (readable) {
91-
errorReadableStream(
92-
readable,
93-
new DOMException("Ongoing fetch was aborted.", "AbortError"),
94-
);
91+
errorReadableStream(readable, terminator.reason);
9592
}
9693
core.tryClose(responseBodyRid);
9794
}
@@ -121,9 +118,7 @@
121118
} catch (err) {
122119
RESOURCE_REGISTRY.unregister(readable);
123120
if (terminator.aborted) {
124-
controller.error(
125-
new DOMException("Ongoing fetch was aborted.", "AbortError"),
126-
);
121+
controller.error(terminator.reason);
127122
} else {
128123
// There was an error while reading a chunk of the body, so we
129124
// error.
@@ -155,9 +150,7 @@
155150
}
156151

157152
const body = new InnerBody(req.blobUrlEntry.stream());
158-
terminator[abortSignal.add](() =>
159-
body.error(new DOMException("Ongoing fetch was aborted.", "AbortError"))
160-
);
153+
terminator[abortSignal.add](() => body.error(terminator.reason));
161154

162155
return {
163156
headerList: [
@@ -328,6 +321,7 @@
328321
/**
329322
* @param {InnerRequest} request
330323
* @param {InnerResponse} response
324+
* @param {AbortSignal} terminator
331325
* @returns {Promise<InnerResponse>}
332326
*/
333327
function httpRedirectFetch(request, response, terminator) {
@@ -405,7 +399,7 @@
405399
const request = toInnerRequest(requestObject);
406400
// 4.
407401
if (requestObject.signal.aborted) {
408-
reject(abortFetch(request, null));
402+
reject(abortFetch(request, null, requestObject.signal.reason));
409403
return;
410404
}
411405

@@ -416,7 +410,9 @@
416410
// 10.
417411
function onabort() {
418412
locallyAborted = true;
419-
reject(abortFetch(request, responseObject));
413+
reject(
414+
abortFetch(request, responseObject, requestObject.signal.reason),
415+
);
420416
}
421417
requestObject.signal[abortSignal.add](onabort);
422418

@@ -433,7 +429,13 @@
433429
if (locallyAborted) return;
434430
// 12.2.
435431
if (response.aborted) {
436-
reject(request, responseObject);
432+
reject(
433+
abortFetch(
434+
request,
435+
responseObject,
436+
requestObject.signal.reason,
437+
),
438+
);
437439
requestObject.signal[abortSignal.remove](onabort);
438440
return;
439441
}
@@ -459,8 +461,7 @@
459461
});
460462
}
461463

462-
function abortFetch(request, responseObject) {
463-
const error = new DOMException("Ongoing fetch was aborted.", "AbortError");
464+
function abortFetch(request, responseObject, error) {
464465
if (request.body !== null) {
465466
if (WeakMapPrototypeHas(requestBodyReaders, request)) {
466467
WeakMapPrototypeGet(requestBodyReaders, request).cancel(error);

0 commit comments

Comments
 (0)