mirror of
https://github.com/denoland/deno.git
synced 2025-09-27 04:39:10 +00:00
parent
55e0483626
commit
39223f709b
4 changed files with 170 additions and 43 deletions
|
@ -10,6 +10,7 @@
|
||||||
/// <reference lib="esnext" />
|
/// <reference lib="esnext" />
|
||||||
|
|
||||||
import * as webidl from "ext:deno_webidl/00_webidl.js";
|
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 { createFilteredInspectProxy } from "ext:deno_console/01_console.js";
|
||||||
import {
|
import {
|
||||||
byteUpperCase,
|
byteUpperCase,
|
||||||
|
@ -356,21 +357,19 @@ class Request {
|
||||||
request.clientRid = init.client?.rid ?? null;
|
request.clientRid = init.client?.rid ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 27.
|
// 28.
|
||||||
this[_request] = request;
|
this[_request] = request;
|
||||||
|
|
||||||
// 28.
|
|
||||||
this[_signal] = abortSignal.newSignal();
|
|
||||||
|
|
||||||
// 29.
|
// 29.
|
||||||
if (signal !== null) {
|
const signals = signal !== null ? [signal] : [];
|
||||||
abortSignal.follow(this[_signal], signal);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 30.
|
// 30.
|
||||||
|
this[_signal] = abortSignal.createDependentAbortSignal(signals, prefix);
|
||||||
|
|
||||||
|
// 31.
|
||||||
this[_headers] = headersFromHeaderList(request.headerList, "request");
|
this[_headers] = headersFromHeaderList(request.headerList, "request");
|
||||||
|
|
||||||
// 32.
|
// 33.
|
||||||
if (init.headers || ObjectKeys(init).length > 0) {
|
if (init.headers || ObjectKeys(init).length > 0) {
|
||||||
const headerList = headerListFromHeaders(this[_headers]);
|
const headerList = headerListFromHeaders(this[_headers]);
|
||||||
const headers = init.headers ?? ArrayPrototypeSlice(
|
const headers = init.headers ?? ArrayPrototypeSlice(
|
||||||
|
@ -384,13 +383,13 @@ class Request {
|
||||||
fillHeaders(this[_headers], headers);
|
fillHeaders(this[_headers], headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 33.
|
// 34.
|
||||||
let inputBody = null;
|
let inputBody = null;
|
||||||
if (ObjectPrototypeIsPrototypeOf(RequestPrototype, input)) {
|
if (ObjectPrototypeIsPrototypeOf(RequestPrototype, input)) {
|
||||||
inputBody = input[_body];
|
inputBody = input[_body];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 34.
|
// 35.
|
||||||
if (
|
if (
|
||||||
(request.method === "GET" || request.method === "HEAD") &&
|
(request.method === "GET" || request.method === "HEAD") &&
|
||||||
((init.body !== undefined && init.body !== null) ||
|
((init.body !== undefined && init.body !== null) ||
|
||||||
|
@ -399,10 +398,10 @@ class Request {
|
||||||
throw new TypeError("Request with GET/HEAD method cannot have body.");
|
throw new TypeError("Request with GET/HEAD method cannot have body.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 35.
|
// 36.
|
||||||
let initBody = null;
|
let initBody = null;
|
||||||
|
|
||||||
// 36.
|
// 37.
|
||||||
if (init.body !== undefined && init.body !== null) {
|
if (init.body !== undefined && init.body !== null) {
|
||||||
const res = extractBody(init.body);
|
const res = extractBody(init.body);
|
||||||
initBody = res.body;
|
initBody = res.body;
|
||||||
|
@ -411,13 +410,13 @@ class Request {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 37.
|
// 38.
|
||||||
const inputOrInitBody = initBody ?? inputBody;
|
const inputOrInitBody = initBody ?? inputBody;
|
||||||
|
|
||||||
// 39.
|
// 40.
|
||||||
let finalBody = inputOrInitBody;
|
let finalBody = inputOrInitBody;
|
||||||
|
|
||||||
// 40.
|
// 41.
|
||||||
if (initBody === null && inputBody !== null) {
|
if (initBody === null && inputBody !== null) {
|
||||||
if (input[_body] && input[_body].unusable()) {
|
if (input[_body] && input[_body].unusable()) {
|
||||||
throw new TypeError("Input request's body is unusable.");
|
throw new TypeError("Input request's body is unusable.");
|
||||||
|
@ -425,7 +424,7 @@ class Request {
|
||||||
finalBody = inputBody.createProxy();
|
finalBody = inputBody.createProxy();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 41.
|
// 42.
|
||||||
request.body = finalBody;
|
request.body = finalBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,20 +463,22 @@ class Request {
|
||||||
}
|
}
|
||||||
|
|
||||||
clone() {
|
clone() {
|
||||||
|
const prefix = "Failed to call 'Request.clone'";
|
||||||
webidl.assertBranded(this, RequestPrototype);
|
webidl.assertBranded(this, RequestPrototype);
|
||||||
if (this[_body] && this[_body].unusable()) {
|
if (this[_body] && this[_body].unusable()) {
|
||||||
throw new TypeError("Body is unusable.");
|
throw new TypeError("Body is unusable.");
|
||||||
}
|
}
|
||||||
const newReq = cloneInnerRequest(this[_request]);
|
const clonedReq = cloneInnerRequest(this[_request]);
|
||||||
const newSignal = abortSignal.newSignal();
|
|
||||||
|
|
||||||
if (this[_signal]) {
|
assert(this[_signal] !== null);
|
||||||
abortSignal.follow(newSignal, this[_signal]);
|
const clonedSignal = abortSignal.createDependentAbortSignal(
|
||||||
}
|
[this[_signal]],
|
||||||
|
prefix,
|
||||||
|
);
|
||||||
|
|
||||||
return fromInnerRequest(
|
return fromInnerRequest(
|
||||||
newReq,
|
clonedReq,
|
||||||
newSignal,
|
clonedSignal,
|
||||||
guardFromHeaders(this[_headers]),
|
guardFromHeaders(this[_headers]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
/// <reference path="../../core/internal.d.ts" />
|
/// <reference path="../../core/internal.d.ts" />
|
||||||
|
|
||||||
import * as webidl from "ext:deno_webidl/00_webidl.js";
|
import * as webidl from "ext:deno_webidl/00_webidl.js";
|
||||||
|
import { assert } from "ext:deno_web/00_infra.js";
|
||||||
import {
|
import {
|
||||||
defineEventHandler,
|
defineEventHandler,
|
||||||
Event,
|
Event,
|
||||||
|
@ -13,27 +14,76 @@ import {
|
||||||
} from "ext:deno_web/02_event.js";
|
} from "ext:deno_web/02_event.js";
|
||||||
const primordials = globalThis.__bootstrap.primordials;
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
const {
|
const {
|
||||||
|
ArrayPrototypeEvery,
|
||||||
|
ArrayPrototypePush,
|
||||||
SafeArrayIterator,
|
SafeArrayIterator,
|
||||||
SafeSet,
|
SafeSet,
|
||||||
SafeSetIterator,
|
SafeSetIterator,
|
||||||
|
SafeWeakRef,
|
||||||
|
SafeWeakSet,
|
||||||
SetPrototypeAdd,
|
SetPrototypeAdd,
|
||||||
SetPrototypeDelete,
|
SetPrototypeDelete,
|
||||||
Symbol,
|
Symbol,
|
||||||
TypeError,
|
TypeError,
|
||||||
|
WeakRefPrototypeDeref,
|
||||||
|
WeakSetPrototypeAdd,
|
||||||
|
WeakSetPrototypeHas,
|
||||||
} = primordials;
|
} = primordials;
|
||||||
import { refTimer, setTimeout, unrefTimer } from "ext:deno_web/02_timers.js";
|
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 add = Symbol("[[add]]");
|
||||||
const signalAbort = Symbol("[[signalAbort]]");
|
const signalAbort = Symbol("[[signalAbort]]");
|
||||||
const remove = Symbol("[[remove]]");
|
const remove = Symbol("[[remove]]");
|
||||||
const abortReason = Symbol("[[abortReason]]");
|
const abortReason = Symbol("[[abortReason]]");
|
||||||
const abortAlgos = Symbol("[[abortAlgos]]");
|
const abortAlgos = Symbol("[[abortAlgos]]");
|
||||||
|
const dependent = Symbol("[[dependent]]");
|
||||||
|
const sourceSignals = Symbol("[[sourceSignals]]");
|
||||||
|
const dependentSignals = Symbol("[[dependentSignals]]");
|
||||||
const signal = Symbol("[[signal]]");
|
const signal = Symbol("[[signal]]");
|
||||||
const timerId = Symbol("[[timerId]]");
|
const timerId = Symbol("[[timerId]]");
|
||||||
|
|
||||||
const illegalConstructorKey = Symbol("illegalConstructorKey");
|
const illegalConstructorKey = Symbol("illegalConstructorKey");
|
||||||
|
|
||||||
class AbortSignal extends EventTarget {
|
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) {
|
static abort(reason = undefined) {
|
||||||
if (reason !== undefined) {
|
if (reason !== undefined) {
|
||||||
reason = webidl.converters.any(reason);
|
reason = webidl.converters.any(reason);
|
||||||
|
@ -73,9 +123,7 @@ class AbortSignal extends EventTarget {
|
||||||
if (this.aborted) {
|
if (this.aborted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this[abortAlgos] === null) {
|
this[abortAlgos] ??= new SafeSet();
|
||||||
this[abortAlgos] = new SafeSet();
|
|
||||||
}
|
|
||||||
SetPrototypeAdd(this[abortAlgos], algorithm);
|
SetPrototypeAdd(this[abortAlgos], algorithm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,12 +139,20 @@ class AbortSignal extends EventTarget {
|
||||||
|
|
||||||
const event = new Event("abort");
|
const event = new Event("abort");
|
||||||
setIsTrusted(event, true);
|
setIsTrusted(event, true);
|
||||||
this.dispatchEvent(event);
|
super.dispatchEvent(event);
|
||||||
if (algos !== null) {
|
if (algos !== null) {
|
||||||
for (const algorithm of new SafeSetIterator(algos)) {
|
for (const algorithm of new SafeSetIterator(algos)) {
|
||||||
algorithm();
|
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) {
|
[remove](algorithm) {
|
||||||
|
@ -104,12 +160,15 @@ class AbortSignal extends EventTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(key = null) {
|
constructor(key = null) {
|
||||||
if (key != illegalConstructorKey) {
|
if (key !== illegalConstructorKey) {
|
||||||
throw new TypeError("Illegal constructor.");
|
throw new TypeError("Illegal constructor.");
|
||||||
}
|
}
|
||||||
super();
|
super();
|
||||||
this[abortReason] = undefined;
|
this[abortReason] = undefined;
|
||||||
this[abortAlgos] = null;
|
this[abortAlgos] = null;
|
||||||
|
this[dependent] = false;
|
||||||
|
this[sourceSignals] = null;
|
||||||
|
this[dependentSignals] = null;
|
||||||
this[timerId] = null;
|
this[timerId] = null;
|
||||||
this[webidl.brand] = webidl.brand;
|
this[webidl.brand] = webidl.brand;
|
||||||
}
|
}
|
||||||
|
@ -138,15 +197,45 @@ class AbortSignal extends EventTarget {
|
||||||
// ops which would block the event loop.
|
// ops which would block the event loop.
|
||||||
addEventListener(...args) {
|
addEventListener(...args) {
|
||||||
super.addEventListener(...new SafeArrayIterator(args));
|
super.addEventListener(...new SafeArrayIterator(args));
|
||||||
if (this[timerId] !== null && listenerCount(this, "abort") > 0) {
|
if (listenerCount(this, "abort") > 0) {
|
||||||
|
if (this[timerId] !== null) {
|
||||||
refTimer(this[timerId]);
|
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) {
|
removeEventListener(...args) {
|
||||||
super.removeEventListener(...new SafeArrayIterator(args));
|
super.removeEventListener(...new SafeArrayIterator(args));
|
||||||
if (this[timerId] !== null && listenerCount(this, "abort") === 0) {
|
if (listenerCount(this, "abort") === 0) {
|
||||||
|
if (this[timerId] !== null) {
|
||||||
unrefTimer(this[timerId]);
|
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);
|
webidl.configureInterface(AbortController);
|
||||||
const AbortControllerPrototype = AbortController.prototype;
|
const AbortControllerPrototype = AbortController.prototype;
|
||||||
|
|
||||||
webidl.converters["AbortSignal"] = webidl.createInterfaceConverter(
|
webidl.converters.AbortSignal = webidl.createInterfaceConverter(
|
||||||
"AbortSignal",
|
"AbortSignal",
|
||||||
AbortSignal.prototype,
|
AbortSignal.prototype,
|
||||||
);
|
);
|
||||||
|
webidl.converters["sequence<AbortSignal>"] = webidl.createSequenceConverter(
|
||||||
|
webidl.converters.AbortSignal,
|
||||||
|
);
|
||||||
|
|
||||||
function newSignal() {
|
function newSignal() {
|
||||||
return new AbortSignal(illegalConstructorKey);
|
return new AbortSignal(illegalConstructorKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
function follow(followingSignal, parentSignal) {
|
function createDependentAbortSignal(signals, prefix) {
|
||||||
if (followingSignal.aborted) {
|
signals = webidl.converters["sequence<AbortSignal>"](
|
||||||
return;
|
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);
|
|
||||||
|
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 {
|
} else {
|
||||||
parentSignal[add](() => followingSignal[signalAbort](parentSignal.reason));
|
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 {
|
export {
|
||||||
|
@ -201,7 +325,7 @@ export {
|
||||||
AbortSignal,
|
AbortSignal,
|
||||||
AbortSignalPrototype,
|
AbortSignalPrototype,
|
||||||
add,
|
add,
|
||||||
follow,
|
createDependentAbortSignal,
|
||||||
newSignal,
|
newSignal,
|
||||||
remove,
|
remove,
|
||||||
signalAbort,
|
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;
|
readonly prototype: AbortSignal;
|
||||||
new (): never;
|
new (): never;
|
||||||
abort(reason?: any): AbortSignal;
|
abort(reason?: any): AbortSignal;
|
||||||
|
any(signals: AbortSignal[]): AbortSignal;
|
||||||
timeout(milliseconds: number): AbortSignal;
|
timeout(milliseconds: number): AbortSignal;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2302,7 +2302,9 @@
|
||||||
"AbortSignal.any.html": true,
|
"AbortSignal.any.html": true,
|
||||||
"AbortSignal.any.worker.html": true,
|
"AbortSignal.any.worker.html": true,
|
||||||
"event.any.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": {
|
"events": {
|
||||||
"AddEventListenerOptions-once.any.html": true,
|
"AddEventListenerOptions-once.any.html": true,
|
||||||
|
@ -2364,7 +2366,6 @@
|
||||||
"EventTarget interface: operation addEventListener(DOMString, EventListener?, optional (AddEventListenerOptions or boolean))",
|
"EventTarget interface: operation addEventListener(DOMString, EventListener?, optional (AddEventListenerOptions or boolean))",
|
||||||
"EventTarget interface: operation removeEventListener(DOMString, EventListener?, optional (EventListenerOptions or boolean))",
|
"EventTarget interface: operation removeEventListener(DOMString, EventListener?, optional (EventListenerOptions or boolean))",
|
||||||
"AbortController interface: operation abort(optional any)",
|
"AbortController interface: operation abort(optional any)",
|
||||||
"AbortSignal interface: operation any(sequence<AbortSignal>)",
|
|
||||||
"AbortSignal interface: attribute onabort",
|
"AbortSignal interface: attribute onabort",
|
||||||
"NodeList interface: existence and properties of interface object",
|
"NodeList interface: existence and properties of interface object",
|
||||||
"NodeList interface object length",
|
"NodeList interface object length",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue