mirror of
https://github.com/denoland/deno.git
synced 2024-11-25 15:29:32 -05:00
parent
bfe9c9e1ba
commit
c72f5fced0
4 changed files with 170 additions and 43 deletions
|
@ -10,6 +10,7 @@
|
|||
/// <reference lib="esnext" />
|
||||
|
||||
import * as webidl from "ext:deno_webidl/00_webidl.js";
|
||||
import { assert } from "ext:deno_web/00_infra.js";
|
||||
import { createFilteredInspectProxy } from "ext:deno_console/01_console.js";
|
||||
import {
|
||||
byteUpperCase,
|
||||
|
@ -356,21 +357,19 @@ class Request {
|
|||
request.clientRid = init.client?.rid ?? null;
|
||||
}
|
||||
|
||||
// 27.
|
||||
// 28.
|
||||
this[_request] = request;
|
||||
|
||||
// 28.
|
||||
this[_signal] = abortSignal.newSignal();
|
||||
|
||||
// 29.
|
||||
if (signal !== null) {
|
||||
abortSignal.follow(this[_signal], signal);
|
||||
}
|
||||
const signals = signal !== null ? [signal] : [];
|
||||
|
||||
// 30.
|
||||
this[_signal] = abortSignal.createDependentAbortSignal(signals, prefix);
|
||||
|
||||
// 31.
|
||||
this[_headers] = headersFromHeaderList(request.headerList, "request");
|
||||
|
||||
// 32.
|
||||
// 33.
|
||||
if (init.headers || ObjectKeys(init).length > 0) {
|
||||
const headerList = headerListFromHeaders(this[_headers]);
|
||||
const headers = init.headers ?? ArrayPrototypeSlice(
|
||||
|
@ -384,13 +383,13 @@ class Request {
|
|||
fillHeaders(this[_headers], headers);
|
||||
}
|
||||
|
||||
// 33.
|
||||
// 34.
|
||||
let inputBody = null;
|
||||
if (ObjectPrototypeIsPrototypeOf(RequestPrototype, input)) {
|
||||
inputBody = input[_body];
|
||||
}
|
||||
|
||||
// 34.
|
||||
// 35.
|
||||
if (
|
||||
(request.method === "GET" || request.method === "HEAD") &&
|
||||
((init.body !== undefined && init.body !== null) ||
|
||||
|
@ -399,10 +398,10 @@ class Request {
|
|||
throw new TypeError("Request with GET/HEAD method cannot have body.");
|
||||
}
|
||||
|
||||
// 35.
|
||||
// 36.
|
||||
let initBody = null;
|
||||
|
||||
// 36.
|
||||
// 37.
|
||||
if (init.body !== undefined && init.body !== null) {
|
||||
const res = extractBody(init.body);
|
||||
initBody = res.body;
|
||||
|
@ -411,13 +410,13 @@ class Request {
|
|||
}
|
||||
}
|
||||
|
||||
// 37.
|
||||
// 38.
|
||||
const inputOrInitBody = initBody ?? inputBody;
|
||||
|
||||
// 39.
|
||||
// 40.
|
||||
let finalBody = inputOrInitBody;
|
||||
|
||||
// 40.
|
||||
// 41.
|
||||
if (initBody === null && inputBody !== null) {
|
||||
if (input[_body] && input[_body].unusable()) {
|
||||
throw new TypeError("Input request's body is unusable.");
|
||||
|
@ -425,7 +424,7 @@ class Request {
|
|||
finalBody = inputBody.createProxy();
|
||||
}
|
||||
|
||||
// 41.
|
||||
// 42.
|
||||
request.body = finalBody;
|
||||
}
|
||||
|
||||
|
@ -464,20 +463,22 @@ class Request {
|
|||
}
|
||||
|
||||
clone() {
|
||||
const prefix = "Failed to call 'Request.clone'";
|
||||
webidl.assertBranded(this, RequestPrototype);
|
||||
if (this[_body] && this[_body].unusable()) {
|
||||
throw new TypeError("Body is unusable.");
|
||||
}
|
||||
const newReq = cloneInnerRequest(this[_request]);
|
||||
const newSignal = abortSignal.newSignal();
|
||||
const clonedReq = cloneInnerRequest(this[_request]);
|
||||
|
||||
if (this[_signal]) {
|
||||
abortSignal.follow(newSignal, this[_signal]);
|
||||
}
|
||||
assert(this[_signal] !== null);
|
||||
const clonedSignal = abortSignal.createDependentAbortSignal(
|
||||
[this[_signal]],
|
||||
prefix,
|
||||
);
|
||||
|
||||
return fromInnerRequest(
|
||||
newReq,
|
||||
newSignal,
|
||||
clonedReq,
|
||||
clonedSignal,
|
||||
guardFromHeaders(this[_headers]),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
/// <reference path="../../core/internal.d.ts" />
|
||||
|
||||
import * as webidl from "ext:deno_webidl/00_webidl.js";
|
||||
import { assert } from "ext:deno_web/00_infra.js";
|
||||
import {
|
||||
defineEventHandler,
|
||||
Event,
|
||||
|
@ -13,27 +14,76 @@ import {
|
|||
} from "ext:deno_web/02_event.js";
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
ArrayPrototypeEvery,
|
||||
ArrayPrototypePush,
|
||||
SafeArrayIterator,
|
||||
SafeSet,
|
||||
SafeSetIterator,
|
||||
SafeWeakRef,
|
||||
SafeWeakSet,
|
||||
SetPrototypeAdd,
|
||||
SetPrototypeDelete,
|
||||
Symbol,
|
||||
TypeError,
|
||||
WeakRefPrototypeDeref,
|
||||
WeakSetPrototypeAdd,
|
||||
WeakSetPrototypeHas,
|
||||
} = primordials;
|
||||
import { refTimer, setTimeout, unrefTimer } from "ext:deno_web/02_timers.js";
|
||||
|
||||
// Since WeakSet is not a iterable, WeakRefSet class is provided to store and
|
||||
// iterate objects.
|
||||
// To create an AsyncIterable using GeneratorFunction in the internal code,
|
||||
// there are many primordial considerations, so we simply implement the
|
||||
// toArray method.
|
||||
class WeakRefSet {
|
||||
#weakSet = new SafeWeakSet();
|
||||
#refs = [];
|
||||
|
||||
add(value) {
|
||||
if (WeakSetPrototypeHas(this.#weakSet, value)) {
|
||||
return;
|
||||
}
|
||||
WeakSetPrototypeAdd(this.#weakSet, value);
|
||||
ArrayPrototypePush(this.#refs, new SafeWeakRef(value));
|
||||
}
|
||||
|
||||
has(value) {
|
||||
return WeakSetPrototypeHas(this.#weakSet, value);
|
||||
}
|
||||
|
||||
toArray() {
|
||||
const ret = [];
|
||||
for (let i = 0; i < this.#refs.length; ++i) {
|
||||
const value = WeakRefPrototypeDeref(this.#refs[i]);
|
||||
if (value !== undefined) {
|
||||
ArrayPrototypePush(ret, value);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
const add = Symbol("[[add]]");
|
||||
const signalAbort = Symbol("[[signalAbort]]");
|
||||
const remove = Symbol("[[remove]]");
|
||||
const abortReason = Symbol("[[abortReason]]");
|
||||
const abortAlgos = Symbol("[[abortAlgos]]");
|
||||
const dependent = Symbol("[[dependent]]");
|
||||
const sourceSignals = Symbol("[[sourceSignals]]");
|
||||
const dependentSignals = Symbol("[[dependentSignals]]");
|
||||
const signal = Symbol("[[signal]]");
|
||||
const timerId = Symbol("[[timerId]]");
|
||||
|
||||
const illegalConstructorKey = Symbol("illegalConstructorKey");
|
||||
|
||||
class AbortSignal extends EventTarget {
|
||||
static any(signals) {
|
||||
const prefix = "Failed to call 'AbortSignal.any'";
|
||||
webidl.requiredArguments(arguments.length, 1, prefix);
|
||||
return createDependentAbortSignal(signals, prefix);
|
||||
}
|
||||
|
||||
static abort(reason = undefined) {
|
||||
if (reason !== undefined) {
|
||||
reason = webidl.converters.any(reason);
|
||||
|
@ -73,9 +123,7 @@ class AbortSignal extends EventTarget {
|
|||
if (this.aborted) {
|
||||
return;
|
||||
}
|
||||
if (this[abortAlgos] === null) {
|
||||
this[abortAlgos] = new SafeSet();
|
||||
}
|
||||
this[abortAlgos] ??= new SafeSet();
|
||||
SetPrototypeAdd(this[abortAlgos], algorithm);
|
||||
}
|
||||
|
||||
|
@ -91,12 +139,20 @@ class AbortSignal extends EventTarget {
|
|||
|
||||
const event = new Event("abort");
|
||||
setIsTrusted(event, true);
|
||||
this.dispatchEvent(event);
|
||||
super.dispatchEvent(event);
|
||||
if (algos !== null) {
|
||||
for (const algorithm of new SafeSetIterator(algos)) {
|
||||
algorithm();
|
||||
}
|
||||
}
|
||||
|
||||
if (this[dependentSignals] !== null) {
|
||||
const dependentSignalArray = this[dependentSignals].toArray();
|
||||
for (let i = 0; i < dependentSignalArray.length; ++i) {
|
||||
const dependentSignal = dependentSignalArray[i];
|
||||
dependentSignal[signalAbort](reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[remove](algorithm) {
|
||||
|
@ -104,12 +160,15 @@ class AbortSignal extends EventTarget {
|
|||
}
|
||||
|
||||
constructor(key = null) {
|
||||
if (key != illegalConstructorKey) {
|
||||
if (key !== illegalConstructorKey) {
|
||||
throw new TypeError("Illegal constructor.");
|
||||
}
|
||||
super();
|
||||
this[abortReason] = undefined;
|
||||
this[abortAlgos] = null;
|
||||
this[dependent] = false;
|
||||
this[sourceSignals] = null;
|
||||
this[dependentSignals] = null;
|
||||
this[timerId] = null;
|
||||
this[webidl.brand] = webidl.brand;
|
||||
}
|
||||
|
@ -138,15 +197,45 @@ class AbortSignal extends EventTarget {
|
|||
// ops which would block the event loop.
|
||||
addEventListener(...args) {
|
||||
super.addEventListener(...new SafeArrayIterator(args));
|
||||
if (this[timerId] !== null && listenerCount(this, "abort") > 0) {
|
||||
refTimer(this[timerId]);
|
||||
if (listenerCount(this, "abort") > 0) {
|
||||
if (this[timerId] !== null) {
|
||||
refTimer(this[timerId]);
|
||||
} else if (this[sourceSignals] !== null) {
|
||||
const sourceSignalArray = this[sourceSignals].toArray();
|
||||
for (let i = 0; i < sourceSignalArray.length; ++i) {
|
||||
const sourceSignal = sourceSignalArray[i];
|
||||
if (sourceSignal[timerId] !== null) {
|
||||
refTimer(sourceSignal[timerId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeEventListener(...args) {
|
||||
super.removeEventListener(...new SafeArrayIterator(args));
|
||||
if (this[timerId] !== null && listenerCount(this, "abort") === 0) {
|
||||
unrefTimer(this[timerId]);
|
||||
if (listenerCount(this, "abort") === 0) {
|
||||
if (this[timerId] !== null) {
|
||||
unrefTimer(this[timerId]);
|
||||
} else if (this[sourceSignals] !== null) {
|
||||
const sourceSignalArray = this[sourceSignals].toArray();
|
||||
for (let i = 0; i < sourceSignalArray.length; ++i) {
|
||||
const sourceSignal = sourceSignalArray[i];
|
||||
if (sourceSignal[timerId] !== null) {
|
||||
// Check that all dependent signals of the timer signal do not have listeners
|
||||
if (
|
||||
ArrayPrototypeEvery(
|
||||
sourceSignal[dependentSignals].toArray(),
|
||||
(dependentSignal) =>
|
||||
dependentSignal === this ||
|
||||
listenerCount(dependentSignal, "abort") === 0,
|
||||
)
|
||||
) {
|
||||
unrefTimer(sourceSignal[timerId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -176,24 +265,59 @@ class AbortController {
|
|||
webidl.configureInterface(AbortController);
|
||||
const AbortControllerPrototype = AbortController.prototype;
|
||||
|
||||
webidl.converters["AbortSignal"] = webidl.createInterfaceConverter(
|
||||
webidl.converters.AbortSignal = webidl.createInterfaceConverter(
|
||||
"AbortSignal",
|
||||
AbortSignal.prototype,
|
||||
);
|
||||
webidl.converters["sequence<AbortSignal>"] = webidl.createSequenceConverter(
|
||||
webidl.converters.AbortSignal,
|
||||
);
|
||||
|
||||
function newSignal() {
|
||||
return new AbortSignal(illegalConstructorKey);
|
||||
}
|
||||
|
||||
function follow(followingSignal, parentSignal) {
|
||||
if (followingSignal.aborted) {
|
||||
return;
|
||||
function createDependentAbortSignal(signals, prefix) {
|
||||
signals = webidl.converters["sequence<AbortSignal>"](
|
||||
signals,
|
||||
prefix,
|
||||
"Argument 1",
|
||||
);
|
||||
|
||||
const resultSignal = new AbortSignal(illegalConstructorKey);
|
||||
for (let i = 0; i < signals.length; ++i) {
|
||||
const signal = signals[i];
|
||||
if (signal[abortReason] !== undefined) {
|
||||
resultSignal[abortReason] = signal[abortReason];
|
||||
return resultSignal;
|
||||
}
|
||||
}
|
||||
if (parentSignal.aborted) {
|
||||
followingSignal[signalAbort](parentSignal.reason);
|
||||
} else {
|
||||
parentSignal[add](() => followingSignal[signalAbort](parentSignal.reason));
|
||||
|
||||
resultSignal[dependent] = true;
|
||||
resultSignal[sourceSignals] = new WeakRefSet();
|
||||
for (let i = 0; i < signals.length; ++i) {
|
||||
const signal = signals[i];
|
||||
if (!signal[dependent]) {
|
||||
signal[dependentSignals] ??= new WeakRefSet();
|
||||
resultSignal[sourceSignals].add(signal);
|
||||
signal[dependentSignals].add(resultSignal);
|
||||
} else {
|
||||
const sourceSignalArray = signal[sourceSignals].toArray();
|
||||
for (let j = 0; j < sourceSignalArray.length; ++j) {
|
||||
const sourceSignal = sourceSignalArray[j];
|
||||
assert(sourceSignal[abortReason] === undefined);
|
||||
assert(!sourceSignal[dependent]);
|
||||
|
||||
if (resultSignal[sourceSignals].has(sourceSignal)) {
|
||||
continue;
|
||||
}
|
||||
resultSignal[sourceSignals].add(sourceSignal);
|
||||
sourceSignal[dependentSignals].add(resultSignal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resultSignal;
|
||||
}
|
||||
|
||||
export {
|
||||
|
@ -201,7 +325,7 @@ export {
|
|||
AbortSignal,
|
||||
AbortSignalPrototype,
|
||||
add,
|
||||
follow,
|
||||
createDependentAbortSignal,
|
||||
newSignal,
|
||||
remove,
|
||||
signalAbort,
|
||||
|
|
1
ext/web/lib.deno_web.d.ts
vendored
1
ext/web/lib.deno_web.d.ts
vendored
|
@ -442,6 +442,7 @@ declare var AbortSignal: {
|
|||
readonly prototype: AbortSignal;
|
||||
new (): never;
|
||||
abort(reason?: any): AbortSignal;
|
||||
any(signals: AbortSignal[]): AbortSignal;
|
||||
timeout(milliseconds: number): AbortSignal;
|
||||
};
|
||||
|
||||
|
|
|
@ -2302,7 +2302,9 @@
|
|||
"AbortSignal.any.html": true,
|
||||
"AbortSignal.any.worker.html": true,
|
||||
"event.any.html": true,
|
||||
"event.any.worker.html": true
|
||||
"event.any.worker.html": true,
|
||||
"abort-signal-any.any.html": true,
|
||||
"abort-signal-any.any.worker.html": true
|
||||
},
|
||||
"events": {
|
||||
"AddEventListenerOptions-once.any.html": true,
|
||||
|
@ -2364,7 +2366,6 @@
|
|||
"EventTarget interface: operation addEventListener(DOMString, EventListener?, optional (AddEventListenerOptions or boolean))",
|
||||
"EventTarget interface: operation removeEventListener(DOMString, EventListener?, optional (EventListenerOptions or boolean))",
|
||||
"AbortController interface: operation abort(optional any)",
|
||||
"AbortSignal interface: operation any(sequence<AbortSignal>)",
|
||||
"AbortSignal interface: attribute onabort",
|
||||
"NodeList interface: existence and properties of interface object",
|
||||
"NodeList interface object length",
|
||||
|
|
Loading…
Reference in a new issue