mirror of
https://github.com/denoland/deno.git
synced 2025-09-26 20:29:11 +00:00
fix(ext/http): ensure signal is created iff requested (#23601)
This correctly creates the `AbortSignal` regardless of when we request it. If the signal is requested after the request has completed, the signal is created in the aborted state. Using GC counts, we can see a reduction in object creation: This PR: 440 deno 1.42.4: 1650 deno 1.43.0+b02ffec: 874
This commit is contained in:
parent
da52058a94
commit
56fec538e1
2 changed files with 52 additions and 4 deletions
|
@ -9,7 +9,7 @@
|
||||||
/// <reference path="./lib.deno_fetch.d.ts" />
|
/// <reference path="./lib.deno_fetch.d.ts" />
|
||||||
/// <reference lib="esnext" />
|
/// <reference lib="esnext" />
|
||||||
|
|
||||||
import { core, primordials } from "ext:core/mod.js";
|
import { core, internals, primordials } from "ext:core/mod.js";
|
||||||
const {
|
const {
|
||||||
ArrayPrototypeMap,
|
ArrayPrototypeMap,
|
||||||
ArrayPrototypeSlice,
|
ArrayPrototypeSlice,
|
||||||
|
@ -269,10 +269,20 @@ class Request {
|
||||||
/** @type {AbortSignal} */
|
/** @type {AbortSignal} */
|
||||||
get [_signal]() {
|
get [_signal]() {
|
||||||
const signal = this[_signalCache];
|
const signal = this[_signalCache];
|
||||||
if (signal !== undefined) {
|
// This signal not been created yet, and the request is still in progress
|
||||||
|
if (signal === undefined) {
|
||||||
|
const signal = newSignal();
|
||||||
|
this[_signalCache] = signal;
|
||||||
return signal;
|
return signal;
|
||||||
}
|
}
|
||||||
return (this[_signalCache] = newSignal());
|
// This signal has not been created yet, but the request has already completed
|
||||||
|
if (signal === false) {
|
||||||
|
const signal = newSignal();
|
||||||
|
this[_signalCache] = signal;
|
||||||
|
signal[signalAbort](signalAbortError);
|
||||||
|
return signal;
|
||||||
|
}
|
||||||
|
return signal;
|
||||||
}
|
}
|
||||||
get [_mimeType]() {
|
get [_mimeType]() {
|
||||||
const values = getDecodeSplitHeader(
|
const values = getDecodeSplitHeader(
|
||||||
|
@ -593,11 +603,20 @@ const signalAbortError = new DOMException(
|
||||||
ObjectFreeze(signalAbortError);
|
ObjectFreeze(signalAbortError);
|
||||||
|
|
||||||
function abortRequest(request) {
|
function abortRequest(request) {
|
||||||
if (request[_signal]) {
|
if (request[_signalCache] !== undefined) {
|
||||||
request[_signal][signalAbort](signalAbortError);
|
request[_signal][signalAbort](signalAbortError);
|
||||||
|
} else {
|
||||||
|
request[_signalCache] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCachedAbortSignal(request) {
|
||||||
|
return request[_signalCache];
|
||||||
|
}
|
||||||
|
|
||||||
|
// For testing
|
||||||
|
internals.getCachedAbortSignal = getCachedAbortSignal;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
abortRequest,
|
abortRequest,
|
||||||
fromInnerRequest,
|
fromInnerRequest,
|
||||||
|
|
|
@ -23,6 +23,7 @@ const {
|
||||||
addTrailers,
|
addTrailers,
|
||||||
serveHttpOnListener,
|
serveHttpOnListener,
|
||||||
serveHttpOnConnection,
|
serveHttpOnConnection,
|
||||||
|
getCachedAbortSignal,
|
||||||
// @ts-expect-error TypeScript (as of 3.7) does not support indexing namespaces by symbol
|
// @ts-expect-error TypeScript (as of 3.7) does not support indexing namespaces by symbol
|
||||||
} = Deno[Deno.internal];
|
} = Deno[Deno.internal];
|
||||||
|
|
||||||
|
@ -2838,6 +2839,34 @@ for (const delay of ["delay", "nodelay"]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test for the internal implementation detail of cached request signals. Ensure that the request's
|
||||||
|
// signal is aborted if we try to access it after the request has been completed.
|
||||||
|
Deno.test(
|
||||||
|
{ permissions: { net: true } },
|
||||||
|
async function httpServerSignalCancelled() {
|
||||||
|
let stashedRequest;
|
||||||
|
const { finished, abort } = await makeServer((req) => {
|
||||||
|
// The cache signal is `undefined` because it has not been requested
|
||||||
|
assertEquals(getCachedAbortSignal(req), undefined);
|
||||||
|
stashedRequest = req;
|
||||||
|
return new Response("ok");
|
||||||
|
});
|
||||||
|
await (await fetch(`http://localhost:${servePort}`)).text();
|
||||||
|
abort();
|
||||||
|
await finished;
|
||||||
|
|
||||||
|
// `false` is a semaphore for a signal that should be aborted on creation
|
||||||
|
assertEquals(getCachedAbortSignal(stashedRequest!), false);
|
||||||
|
// Requesting the signal causes it to be materialized
|
||||||
|
assert(stashedRequest!.signal.aborted);
|
||||||
|
// The cached signal is now a full `AbortSignal`
|
||||||
|
assertEquals(
|
||||||
|
getCachedAbortSignal(stashedRequest!).constructor,
|
||||||
|
AbortSignal,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
Deno.test(
|
Deno.test(
|
||||||
{ permissions: { net: true } },
|
{ permissions: { net: true } },
|
||||||
async function httpServerCancelFetch() {
|
async function httpServerCancelFetch() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue