diff options
Diffstat (limited to 'ext/fetch/26_fetch.js')
-rw-r--r-- | ext/fetch/26_fetch.js | 1033 |
1 files changed, 516 insertions, 517 deletions
diff --git a/ext/fetch/26_fetch.js b/ext/fetch/26_fetch.js index ddb023a37..9c136f242 100644 --- a/ext/fetch/26_fetch.js +++ b/ext/fetch/26_fetch.js @@ -9,578 +9,577 @@ /// <reference path="./internal.d.ts" /> /// <reference path="./lib.deno_fetch.d.ts" /> /// <reference lib="esnext" /> -"use strict"; - -((window) => { - const core = window.Deno.core; - const ops = core.ops; - const webidl = window.__bootstrap.webidl; - const { byteLowerCase } = window.__bootstrap.infra; - const { BlobPrototype } = window.__bootstrap.file; - const { errorReadableStream, ReadableStreamPrototype, readableStreamForRid } = - window.__bootstrap.streams; - const { InnerBody, extractBody } = window.__bootstrap.fetchBody; - const { - toInnerRequest, - toInnerResponse, - fromInnerResponse, - redirectStatus, - nullBodyStatus, - networkError, - abortedNetworkError, - processUrlList, - } = window.__bootstrap.fetch; - const abortSignal = window.__bootstrap.abortSignal; - const { - ArrayPrototypePush, - ArrayPrototypeSplice, - ArrayPrototypeFilter, - ArrayPrototypeIncludes, - ObjectPrototypeIsPrototypeOf, - Promise, - PromisePrototypeThen, - PromisePrototypeCatch, - SafeArrayIterator, - String, - StringPrototypeStartsWith, - StringPrototypeToLowerCase, - TypeError, - Uint8Array, - Uint8ArrayPrototype, - WeakMap, - WeakMapPrototypeDelete, - WeakMapPrototypeGet, - WeakMapPrototypeHas, - WeakMapPrototypeSet, - } = window.__bootstrap.primordials; - - const REQUEST_BODY_HEADER_NAMES = [ - "content-encoding", - "content-language", - "content-location", - "content-type", - ]; - - const requestBodyReaders = new WeakMap(); - - /** - * @param {{ method: string, url: string, headers: [string, string][], clientRid: number | null, hasBody: boolean }} args - * @param {Uint8Array | null} body - * @returns {{ requestRid: number, requestBodyRid: number | null }} - */ - function opFetch(method, url, headers, clientRid, hasBody, bodyLength, body) { - return ops.op_fetch( - method, - url, - headers, - clientRid, - hasBody, - bodyLength, - body, - ); - } - /** - * @param {number} rid - * @returns {Promise<{ status: number, statusText: string, headers: [string, string][], url: string, responseRid: number }>} - */ - function opFetchSend(rid) { - return core.opAsync("op_fetch_send", rid); +const core = globalThis.Deno.core; +const ops = core.ops; +import * as webidl from "internal:ext/webidl/00_webidl.js"; +import { byteLowerCase } from "internal:ext/web/00_infra.js"; +import { BlobPrototype } from "internal:ext/web/09_file.js"; +import { + errorReadableStream, + readableStreamForRid, + ReadableStreamPrototype, +} from "internal:ext/web/06_streams.js"; +import { extractBody, InnerBody } from "internal:ext/fetch/22_body.js"; +import { + processUrlList, + toInnerRequest, +} from "internal:ext/fetch/23_request.js"; +import { + abortedNetworkError, + fromInnerResponse, + networkError, + nullBodyStatus, + redirectStatus, + toInnerResponse, +} from "internal:ext/fetch/23_response.js"; +import * as abortSignal from "internal:ext/web/03_abort_signal.js"; +const primordials = globalThis.__bootstrap.primordials; +const { + ArrayPrototypePush, + ArrayPrototypeSplice, + ArrayPrototypeFilter, + ArrayPrototypeIncludes, + ObjectPrototypeIsPrototypeOf, + Promise, + PromisePrototypeThen, + PromisePrototypeCatch, + SafeArrayIterator, + String, + StringPrototypeStartsWith, + StringPrototypeToLowerCase, + TypeError, + Uint8Array, + Uint8ArrayPrototype, + WeakMap, + WeakMapPrototypeDelete, + WeakMapPrototypeGet, + WeakMapPrototypeHas, + WeakMapPrototypeSet, +} = primordials; + +const REQUEST_BODY_HEADER_NAMES = [ + "content-encoding", + "content-language", + "content-location", + "content-type", +]; + +const requestBodyReaders = new WeakMap(); + +/** + * @param {{ method: string, url: string, headers: [string, string][], clientRid: number | null, hasBody: boolean }} args + * @param {Uint8Array | null} body + * @returns {{ requestRid: number, requestBodyRid: number | null }} + */ +function opFetch(method, url, headers, clientRid, hasBody, bodyLength, body) { + return ops.op_fetch( + method, + url, + headers, + clientRid, + hasBody, + bodyLength, + body, + ); +} + +/** + * @param {number} rid + * @returns {Promise<{ status: number, statusText: string, headers: [string, string][], url: string, responseRid: number }>} + */ +function opFetchSend(rid) { + return core.opAsync("op_fetch_send", rid); +} + +/** + * @param {number} responseBodyRid + * @param {AbortSignal} [terminator] + * @returns {ReadableStream<Uint8Array>} + */ +function createResponseBodyStream(responseBodyRid, terminator) { + const readable = readableStreamForRid(responseBodyRid); + + function onAbort() { + errorReadableStream(readable, terminator.reason); + core.tryClose(responseBodyRid); } - /** - * @param {number} responseBodyRid - * @param {AbortSignal} [terminator] - * @returns {ReadableStream<Uint8Array>} - */ - function createResponseBodyStream(responseBodyRid, terminator) { - const readable = readableStreamForRid(responseBodyRid); - - function onAbort() { - errorReadableStream(readable, terminator.reason); - core.tryClose(responseBodyRid); + // TODO(lucacasonato): clean up registration + terminator[abortSignal.add](onAbort); + + return readable; +} + +/** + * @param {InnerRequest} req + * @param {boolean} recursive + * @param {AbortSignal} terminator + * @returns {Promise<InnerResponse>} + */ +async function mainFetch(req, recursive, terminator) { + if (req.blobUrlEntry !== null) { + if (req.method !== "GET") { + throw new TypeError("Blob URL fetch only supports GET method."); } - // TODO(lucacasonato): clean up registration - terminator[abortSignal.add](onAbort); + const body = new InnerBody(req.blobUrlEntry.stream()); + terminator[abortSignal.add](() => body.error(terminator.reason)); + processUrlList(req.urlList, req.urlListProcessed); - return readable; + return { + headerList: [ + ["content-length", String(req.blobUrlEntry.size)], + ["content-type", req.blobUrlEntry.type], + ], + status: 200, + statusMessage: "OK", + body, + type: "basic", + url() { + if (this.urlList.length == 0) return null; + return this.urlList[this.urlList.length - 1]; + }, + urlList: recursive + ? [] + : [...new SafeArrayIterator(req.urlListProcessed)], + }; } - /** - * @param {InnerRequest} req - * @param {boolean} recursive - * @param {AbortSignal} terminator - * @returns {Promise<InnerResponse>} - */ - async function mainFetch(req, recursive, terminator) { - if (req.blobUrlEntry !== null) { - if (req.method !== "GET") { - throw new TypeError("Blob URL fetch only supports GET method."); - } - - const body = new InnerBody(req.blobUrlEntry.stream()); - terminator[abortSignal.add](() => body.error(terminator.reason)); - processUrlList(req.urlList, req.urlListProcessed); - - return { - headerList: [ - ["content-length", String(req.blobUrlEntry.size)], - ["content-type", req.blobUrlEntry.type], - ], - status: 200, - statusMessage: "OK", - body, - type: "basic", - url() { - if (this.urlList.length == 0) return null; - return this.urlList[this.urlList.length - 1]; - }, - urlList: recursive - ? [] - : [...new SafeArrayIterator(req.urlListProcessed)], - }; - } + /** @type {ReadableStream<Uint8Array> | Uint8Array | null} */ + let reqBody = null; - /** @type {ReadableStream<Uint8Array> | Uint8Array | null} */ - let reqBody = null; - - if (req.body !== null) { + if (req.body !== null) { + if ( + ObjectPrototypeIsPrototypeOf( + ReadableStreamPrototype, + req.body.streamOrStatic, + ) + ) { if ( - ObjectPrototypeIsPrototypeOf( - ReadableStreamPrototype, - req.body.streamOrStatic, - ) + req.body.length === null || + ObjectPrototypeIsPrototypeOf(BlobPrototype, req.body.source) ) { - if ( - req.body.length === null || - ObjectPrototypeIsPrototypeOf(BlobPrototype, req.body.source) - ) { - reqBody = req.body.stream; + reqBody = req.body.stream; + } else { + const reader = req.body.stream.getReader(); + WeakMapPrototypeSet(requestBodyReaders, req, reader); + const r1 = await reader.read(); + if (r1.done) { + reqBody = new Uint8Array(0); } else { - const reader = req.body.stream.getReader(); - WeakMapPrototypeSet(requestBodyReaders, req, reader); - const r1 = await reader.read(); - if (r1.done) { - reqBody = new Uint8Array(0); - } else { - reqBody = r1.value; - const r2 = await reader.read(); - if (!r2.done) throw new TypeError("Unreachable"); - } - WeakMapPrototypeDelete(requestBodyReaders, req); + reqBody = r1.value; + const r2 = await reader.read(); + if (!r2.done) throw new TypeError("Unreachable"); } - } else { - req.body.streamOrStatic.consumed = true; - reqBody = req.body.streamOrStatic.body; - // TODO(@AaronO): plumb support for StringOrBuffer all the way - reqBody = typeof reqBody === "string" ? core.encode(reqBody) : reqBody; + WeakMapPrototypeDelete(requestBodyReaders, req); } + } else { + req.body.streamOrStatic.consumed = true; + reqBody = req.body.streamOrStatic.body; + // TODO(@AaronO): plumb support for StringOrBuffer all the way + reqBody = typeof reqBody === "string" ? core.encode(reqBody) : reqBody; } + } - const { requestRid, requestBodyRid, cancelHandleRid } = opFetch( - req.method, - req.currentUrl(), - req.headerList, - req.clientRid, - reqBody !== null, - req.body?.length, - ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, reqBody) - ? reqBody - : null, - ); - - function onAbort() { - if (cancelHandleRid !== null) { - core.tryClose(cancelHandleRid); - } - if (requestBodyRid !== null) { - core.tryClose(requestBodyRid); - } + const { requestRid, requestBodyRid, cancelHandleRid } = opFetch( + req.method, + req.currentUrl(), + req.headerList, + req.clientRid, + reqBody !== null, + req.body?.length, + ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, reqBody) ? reqBody : null, + ); + + function onAbort() { + if (cancelHandleRid !== null) { + core.tryClose(cancelHandleRid); } - terminator[abortSignal.add](onAbort); - - let requestSendError; - let requestSendErrorSet = false; if (requestBodyRid !== null) { - if ( - reqBody === null || - !ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, reqBody) - ) { - throw new TypeError("Unreachable"); + core.tryClose(requestBodyRid); + } + } + terminator[abortSignal.add](onAbort); + + let requestSendError; + let requestSendErrorSet = false; + if (requestBodyRid !== null) { + if ( + reqBody === null || + !ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, reqBody) + ) { + throw new TypeError("Unreachable"); + } + const reader = reqBody.getReader(); + WeakMapPrototypeSet(requestBodyReaders, req, reader); + (async () => { + let done = false; + while (!done) { + let val; + try { + const res = await reader.read(); + done = res.done; + val = res.value; + } catch (err) { + if (terminator.aborted) break; + // TODO(lucacasonato): propagate error into response body stream + requestSendError = err; + requestSendErrorSet = true; + break; + } + if (done) break; + if (!ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, val)) { + const error = new TypeError( + "Item in request body ReadableStream is not a Uint8Array", + ); + await reader.cancel(error); + // TODO(lucacasonato): propagate error into response body stream + requestSendError = error; + requestSendErrorSet = true; + break; + } + try { + await core.writeAll(requestBodyRid, val); + } catch (err) { + if (terminator.aborted) break; + await reader.cancel(err); + // TODO(lucacasonato): propagate error into response body stream + requestSendError = err; + requestSendErrorSet = true; + break; + } } - const reader = reqBody.getReader(); - WeakMapPrototypeSet(requestBodyReaders, req, reader); - (async () => { - let done = false; - while (!done) { - let val; - try { - const res = await reader.read(); - done = res.done; - val = res.value; - } catch (err) { - if (terminator.aborted) break; - // TODO(lucacasonato): propagate error into response body stream + if (done && !terminator.aborted) { + try { + await core.shutdown(requestBodyRid); + } catch (err) { + if (!terminator.aborted) { requestSendError = err; requestSendErrorSet = true; - break; - } - if (done) break; - if (!ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, val)) { - const error = new TypeError( - "Item in request body ReadableStream is not a Uint8Array", - ); - await reader.cancel(error); - // TODO(lucacasonato): propagate error into response body stream - requestSendError = error; - requestSendErrorSet = true; - break; - } - try { - await core.writeAll(requestBodyRid, val); - } catch (err) { - if (terminator.aborted) break; - await reader.cancel(err); - // TODO(lucacasonato): propagate error into response body stream - requestSendError = err; - requestSendErrorSet = true; - break; } } - if (done && !terminator.aborted) { - try { - await core.shutdown(requestBodyRid); - } catch (err) { - if (!terminator.aborted) { - requestSendError = err; - requestSendErrorSet = true; - } - } - } - WeakMapPrototypeDelete(requestBodyReaders, req); - core.tryClose(requestBodyRid); - })(); - } - let resp; - try { - resp = await opFetchSend(requestRid); - } catch (err) { - if (terminator.aborted) return; - if (requestSendErrorSet) { - // if the request body stream errored, we want to propagate that error - // instead of the original error from opFetchSend - throw new TypeError("Failed to fetch: request body stream errored", { - cause: requestSendError, - }); - } - throw err; - } finally { - if (cancelHandleRid !== null) { - core.tryClose(cancelHandleRid); } + WeakMapPrototypeDelete(requestBodyReaders, req); + core.tryClose(requestBodyRid); + })(); + } + let resp; + try { + resp = await opFetchSend(requestRid); + } catch (err) { + if (terminator.aborted) return; + if (requestSendErrorSet) { + // if the request body stream errored, we want to propagate that error + // instead of the original error from opFetchSend + throw new TypeError("Failed to fetch: request body stream errored", { + cause: requestSendError, + }); } - if (terminator.aborted) return abortedNetworkError(); - - processUrlList(req.urlList, req.urlListProcessed); - - /** @type {InnerResponse} */ - const response = { - headerList: resp.headers, - status: resp.status, - body: null, - statusMessage: resp.statusText, - type: "basic", - url() { - if (this.urlList.length == 0) return null; - return this.urlList[this.urlList.length - 1]; - }, - urlList: req.urlListProcessed, - }; - if (redirectStatus(resp.status)) { - switch (req.redirectMode) { - case "error": - core.close(resp.responseRid); - return networkError( - "Encountered redirect while redirect mode is set to 'error'", - ); - case "follow": - core.close(resp.responseRid); - return httpRedirectFetch(req, response, terminator); - case "manual": - break; - } + throw err; + } finally { + if (cancelHandleRid !== null) { + core.tryClose(cancelHandleRid); + } + } + if (terminator.aborted) return abortedNetworkError(); + + processUrlList(req.urlList, req.urlListProcessed); + + /** @type {InnerResponse} */ + const response = { + headerList: resp.headers, + status: resp.status, + body: null, + statusMessage: resp.statusText, + type: "basic", + url() { + if (this.urlList.length == 0) return null; + return this.urlList[this.urlList.length - 1]; + }, + urlList: req.urlListProcessed, + }; + if (redirectStatus(resp.status)) { + switch (req.redirectMode) { + case "error": + core.close(resp.responseRid); + return networkError( + "Encountered redirect while redirect mode is set to 'error'", + ); + case "follow": + core.close(resp.responseRid); + return httpRedirectFetch(req, response, terminator); + case "manual": + break; } + } - if (nullBodyStatus(response.status)) { + if (nullBodyStatus(response.status)) { + core.close(resp.responseRid); + } else { + if (req.method === "HEAD" || req.method === "CONNECT") { + response.body = null; core.close(resp.responseRid); } else { - if (req.method === "HEAD" || req.method === "CONNECT") { - response.body = null; - core.close(resp.responseRid); - } else { - response.body = new InnerBody( - createResponseBodyStream(resp.responseRid, terminator), - ); - } + response.body = new InnerBody( + createResponseBodyStream(resp.responseRid, terminator), + ); } + } - if (recursive) return response; + if (recursive) return response; - if (response.urlList.length === 0) { - processUrlList(req.urlList, req.urlListProcessed); - response.urlList = [...new SafeArrayIterator(req.urlListProcessed)]; - } + if (response.urlList.length === 0) { + processUrlList(req.urlList, req.urlListProcessed); + response.urlList = [...new SafeArrayIterator(req.urlListProcessed)]; + } + return response; +} + +/** + * @param {InnerRequest} request + * @param {InnerResponse} response + * @param {AbortSignal} terminator + * @returns {Promise<InnerResponse>} + */ +function httpRedirectFetch(request, response, terminator) { + const locationHeaders = ArrayPrototypeFilter( + response.headerList, + (entry) => byteLowerCase(entry[0]) === "location", + ); + if (locationHeaders.length === 0) { return response; } - - /** - * @param {InnerRequest} request - * @param {InnerResponse} response - * @param {AbortSignal} terminator - * @returns {Promise<InnerResponse>} - */ - function httpRedirectFetch(request, response, terminator) { - const locationHeaders = ArrayPrototypeFilter( - response.headerList, - (entry) => byteLowerCase(entry[0]) === "location", - ); - if (locationHeaders.length === 0) { - return response; - } - const locationURL = new URL( - locationHeaders[0][1], - response.url() ?? undefined, + const locationURL = new URL( + locationHeaders[0][1], + response.url() ?? undefined, + ); + if (locationURL.hash === "") { + locationURL.hash = request.currentUrl().hash; + } + if (locationURL.protocol !== "https:" && locationURL.protocol !== "http:") { + return networkError("Can not redirect to a non HTTP(s) url"); + } + if (request.redirectCount === 20) { + return networkError("Maximum number of redirects (20) reached"); + } + request.redirectCount++; + if ( + response.status !== 303 && + request.body !== null && + request.body.source === null + ) { + return networkError( + "Can not redeliver a streaming request body after a redirect", ); - if (locationURL.hash === "") { - locationURL.hash = request.currentUrl().hash; - } - if (locationURL.protocol !== "https:" && locationURL.protocol !== "http:") { - return networkError("Can not redirect to a non HTTP(s) url"); - } - if (request.redirectCount === 20) { - return networkError("Maximum number of redirects (20) reached"); - } - request.redirectCount++; - if ( - response.status !== 303 && - request.body !== null && - request.body.source === null - ) { - return networkError( - "Can not redeliver a streaming request body after a redirect", - ); - } - if ( - ((response.status === 301 || response.status === 302) && - request.method === "POST") || - (response.status === 303 && - request.method !== "GET" && - request.method !== "HEAD") - ) { - request.method = "GET"; - request.body = null; - for (let i = 0; i < request.headerList.length; i++) { - if ( - ArrayPrototypeIncludes( - REQUEST_BODY_HEADER_NAMES, - byteLowerCase(request.headerList[i][0]), - ) - ) { - ArrayPrototypeSplice(request.headerList, i, 1); - i--; - } + } + if ( + ((response.status === 301 || response.status === 302) && + request.method === "POST") || + (response.status === 303 && + request.method !== "GET" && + request.method !== "HEAD") + ) { + request.method = "GET"; + request.body = null; + for (let i = 0; i < request.headerList.length; i++) { + if ( + ArrayPrototypeIncludes( + REQUEST_BODY_HEADER_NAMES, + byteLowerCase(request.headerList[i][0]), + ) + ) { + ArrayPrototypeSplice(request.headerList, i, 1); + i--; } } - if (request.body !== null) { - const res = extractBody(request.body.source); - request.body = res.body; - } - ArrayPrototypePush(request.urlList, () => locationURL.href); - return mainFetch(request, true, terminator); } + if (request.body !== null) { + const res = extractBody(request.body.source); + request.body = res.body; + } + ArrayPrototypePush(request.urlList, () => locationURL.href); + return mainFetch(request, true, terminator); +} + +/** + * @param {RequestInfo} input + * @param {RequestInit} init + */ +function fetch(input, init = {}) { + // There is an async dispatch later that causes a stack trace disconnect. + // We reconnect it by assigning the result of that dispatch to `opPromise`, + // awaiting `opPromise` in an inner function also named `fetch()` and + // returning the result from that. + let opPromise = undefined; + // 1. + const result = new Promise((resolve, reject) => { + const prefix = "Failed to call 'fetch'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + // 2. + const requestObject = new Request(input, init); + // 3. + const request = toInnerRequest(requestObject); + // 4. + if (requestObject.signal.aborted) { + reject(abortFetch(request, null, requestObject.signal.reason)); + return; + } - /** - * @param {RequestInfo} input - * @param {RequestInit} init - */ - function fetch(input, init = {}) { - // There is an async dispatch later that causes a stack trace disconnect. - // We reconnect it by assigning the result of that dispatch to `opPromise`, - // awaiting `opPromise` in an inner function also named `fetch()` and - // returning the result from that. - let opPromise = undefined; - // 1. - const result = new Promise((resolve, reject) => { - const prefix = "Failed to call 'fetch'"; - webidl.requiredArguments(arguments.length, 1, { prefix }); - // 2. - const requestObject = new Request(input, init); - // 3. - const request = toInnerRequest(requestObject); - // 4. - if (requestObject.signal.aborted) { - reject(abortFetch(request, null, requestObject.signal.reason)); - return; - } - - // 7. - let responseObject = null; - // 9. - let locallyAborted = false; - // 10. - function onabort() { - locallyAborted = true; - reject( - abortFetch(request, responseObject, requestObject.signal.reason), - ); - } - requestObject.signal[abortSignal.add](onabort); + // 7. + let responseObject = null; + // 9. + let locallyAborted = false; + // 10. + function onabort() { + locallyAborted = true; + reject( + abortFetch(request, responseObject, requestObject.signal.reason), + ); + } + requestObject.signal[abortSignal.add](onabort); - if (!requestObject.headers.has("Accept")) { - ArrayPrototypePush(request.headerList, ["Accept", "*/*"]); - } + if (!requestObject.headers.has("Accept")) { + ArrayPrototypePush(request.headerList, ["Accept", "*/*"]); + } - if (!requestObject.headers.has("Accept-Language")) { - ArrayPrototypePush(request.headerList, ["Accept-Language", "*"]); - } + if (!requestObject.headers.has("Accept-Language")) { + ArrayPrototypePush(request.headerList, ["Accept-Language", "*"]); + } - // 12. - opPromise = PromisePrototypeCatch( - PromisePrototypeThen( - mainFetch(request, false, requestObject.signal), - (response) => { - // 12.1. - if (locallyAborted) return; - // 12.2. - if (response.aborted) { - reject( - abortFetch( - request, - responseObject, - requestObject.signal.reason, - ), - ); - requestObject.signal[abortSignal.remove](onabort); - return; - } - // 12.3. - if (response.type === "error") { - const err = new TypeError( - "Fetch failed: " + (response.error ?? "unknown error"), - ); - reject(err); - requestObject.signal[abortSignal.remove](onabort); - return; - } - responseObject = fromInnerResponse(response, "immutable"); - resolve(responseObject); + // 12. + opPromise = PromisePrototypeCatch( + PromisePrototypeThen( + mainFetch(request, false, requestObject.signal), + (response) => { + // 12.1. + if (locallyAborted) return; + // 12.2. + if (response.aborted) { + reject( + abortFetch( + request, + responseObject, + requestObject.signal.reason, + ), + ); requestObject.signal[abortSignal.remove](onabort); - }, - ), - (err) => { - reject(err); + return; + } + // 12.3. + if (response.type === "error") { + const err = new TypeError( + "Fetch failed: " + (response.error ?? "unknown error"), + ); + reject(err); + requestObject.signal[abortSignal.remove](onabort); + return; + } + responseObject = fromInnerResponse(response, "immutable"); + resolve(responseObject); requestObject.signal[abortSignal.remove](onabort); }, - ); - }); - if (opPromise) { - PromisePrototypeCatch(result, () => {}); - return (async function fetch() { - await opPromise; - return result; - })(); - } - return result; + ), + (err) => { + reject(err); + requestObject.signal[abortSignal.remove](onabort); + }, + ); + }); + if (opPromise) { + PromisePrototypeCatch(result, () => {}); + return (async function fetch() { + await opPromise; + return result; + })(); } + return result; +} - function abortFetch(request, responseObject, error) { - if (request.body !== null) { - if (WeakMapPrototypeHas(requestBodyReaders, request)) { - WeakMapPrototypeGet(requestBodyReaders, request).cancel(error); - } else { - request.body.cancel(error); - } - } - if (responseObject !== null) { - const response = toInnerResponse(responseObject); - if (response.body !== null) response.body.error(error); +function abortFetch(request, responseObject, error) { + if (request.body !== null) { + if (WeakMapPrototypeHas(requestBodyReaders, request)) { + WeakMapPrototypeGet(requestBodyReaders, request).cancel(error); + } else { + request.body.cancel(error); } - return error; } + if (responseObject !== null) { + const response = toInnerResponse(responseObject); + if (response.body !== null) response.body.error(error); + } + return error; +} + +/** + * Handle the Response argument to the WebAssembly streaming APIs, after + * resolving if it was passed as a promise. This function should be registered + * through `Deno.core.setWasmStreamingCallback`. + * + * @param {any} source The source parameter that the WebAssembly streaming API + * was called with. If it was called with a Promise, `source` is the resolved + * value of that promise. + * @param {number} rid An rid that represents the wasm streaming resource. + */ +function handleWasmStreaming(source, rid) { + // This implements part of + // https://webassembly.github.io/spec/web-api/#compile-a-potential-webassembly-response + try { + const res = webidl.converters["Response"](source, { + prefix: "Failed to call 'WebAssembly.compileStreaming'", + context: "Argument 1", + }); - /** - * Handle the Response argument to the WebAssembly streaming APIs, after - * resolving if it was passed as a promise. This function should be registered - * through `Deno.core.setWasmStreamingCallback`. - * - * @param {any} source The source parameter that the WebAssembly streaming API - * was called with. If it was called with a Promise, `source` is the resolved - * value of that promise. - * @param {number} rid An rid that represents the wasm streaming resource. - */ - function handleWasmStreaming(source, rid) { - // This implements part of - // https://webassembly.github.io/spec/web-api/#compile-a-potential-webassembly-response - try { - const res = webidl.converters["Response"](source, { - prefix: "Failed to call 'WebAssembly.compileStreaming'", - context: "Argument 1", - }); - - // 2.3. - // The spec is ambiguous here, see - // https://github.com/WebAssembly/spec/issues/1138. The WPT tests expect - // the raw value of the Content-Type attribute lowercased. We ignore this - // for file:// because file fetches don't have a Content-Type. - if (!StringPrototypeStartsWith(res.url, "file://")) { - const contentType = res.headers.get("Content-Type"); - if ( - typeof contentType !== "string" || - StringPrototypeToLowerCase(contentType) !== "application/wasm" - ) { - throw new TypeError("Invalid WebAssembly content type."); - } + // 2.3. + // The spec is ambiguous here, see + // https://github.com/WebAssembly/spec/issues/1138. The WPT tests expect + // the raw value of the Content-Type attribute lowercased. We ignore this + // for file:// because file fetches don't have a Content-Type. + if (!StringPrototypeStartsWith(res.url, "file://")) { + const contentType = res.headers.get("Content-Type"); + if ( + typeof contentType !== "string" || + StringPrototypeToLowerCase(contentType) !== "application/wasm" + ) { + throw new TypeError("Invalid WebAssembly content type."); } + } - // 2.5. - if (!res.ok) { - throw new TypeError(`HTTP status code ${res.status}`); - } + // 2.5. + if (!res.ok) { + throw new TypeError(`HTTP status code ${res.status}`); + } - // Pass the resolved URL to v8. - ops.op_wasm_streaming_set_url(rid, res.url); - - if (res.body !== null) { - // 2.6. - // Rather than consuming the body as an ArrayBuffer, this passes each - // chunk to the feed as soon as it's available. - PromisePrototypeThen( - (async () => { - const reader = res.body.getReader(); - while (true) { - const { value: chunk, done } = await reader.read(); - if (done) break; - ops.op_wasm_streaming_feed(rid, chunk); - } - })(), - // 2.7 - () => core.close(rid), - // 2.8 - (err) => core.abortWasmStreaming(rid, err), - ); - } else { + // Pass the resolved URL to v8. + ops.op_wasm_streaming_set_url(rid, res.url); + + if (res.body !== null) { + // 2.6. + // Rather than consuming the body as an ArrayBuffer, this passes each + // chunk to the feed as soon as it's available. + PromisePrototypeThen( + (async () => { + const reader = res.body.getReader(); + while (true) { + const { value: chunk, done } = await reader.read(); + if (done) break; + ops.op_wasm_streaming_feed(rid, chunk); + } + })(), // 2.7 - core.close(rid); - } - } catch (err) { - // 2.8 - core.abortWasmStreaming(rid, err); + () => core.close(rid), + // 2.8 + (err) => core.abortWasmStreaming(rid, err), + ); + } else { + // 2.7 + core.close(rid); } + } catch (err) { + // 2.8 + core.abortWasmStreaming(rid, err); } +} - window.__bootstrap.fetch ??= {}; - window.__bootstrap.fetch.fetch = fetch; - window.__bootstrap.fetch.handleWasmStreaming = handleWasmStreaming; -})(this); +export { fetch, handleWasmStreaming }; |