summaryrefslogtreecommitdiff
path: root/runtime/js/40_http.js
diff options
context:
space:
mode:
authorLuca Casonato <lucacasonato@yahoo.com>2021-04-20 14:47:22 +0200
committerGitHub <noreply@github.com>2021-04-20 14:47:22 +0200
commit9e6cd91014ac4a0d34556b0d09cbe25e4e0930c6 (patch)
tree4523790510a17676c987039feb03f208a258dc16 /runtime/js/40_http.js
parent115197ffb06aad2a3045e8478980ab911b5a5eeb (diff)
chore: align fetch to spec (#10203)
This commit aligns the `fetch` API and the `Request` / `Response` classes belonging to it to the spec. This commit enables all the relevant `fetch` WPT tests. Spec compliance is now at around 90%. Performance is essentially identical now (within 1% of 1.9.0).
Diffstat (limited to 'runtime/js/40_http.js')
-rw-r--r--runtime/js/40_http.js108
1 files changed, 59 insertions, 49 deletions
diff --git a/runtime/js/40_http.js b/runtime/js/40_http.js
index 45361ee34..67faf19cb 100644
--- a/runtime/js/40_http.js
+++ b/runtime/js/40_http.js
@@ -2,9 +2,9 @@
"use strict";
((window) => {
- const { Request, dontValidateUrl, lazyHeaders, fastBody, Response } =
+ const { InnerBody } = window.__bootstrap.fetchBody;
+ const { Response, fromInnerRequest, toInnerResponse, newInnerRequest } =
window.__bootstrap.fetch;
- const { Headers } = window.__bootstrap.headers;
const errors = window.__bootstrap.errors.errors;
const core = window.Deno.core;
const { ReadableStream } = window.__bootstrap.streams;
@@ -53,18 +53,18 @@
] = nextRequest;
/** @type {ReadableStream<Uint8Array> | undefined} */
- let body = undefined;
+ let body = null;
if (typeof requestBodyRid === "number") {
body = createRequestBodyStream(requestBodyRid);
}
- const request = new Request(url, {
- body,
+ const innerRequest = newInnerRequest(
method,
- headers: headersList,
- [dontValidateUrl]: true,
- [lazyHeaders]: true,
- });
+ url,
+ headersList,
+ body !== null ? new InnerBody(body) : null,
+ );
+ const request = fromInnerRequest(innerRequest, "immutable");
const respondWith = createRespondWith(responseSenderRid, this.#rid);
@@ -96,16 +96,6 @@
);
}
- /** IMPORTANT: Equivalent to `Array.from(headers).flat()` but more performant.
- * Please preserve. */
- function flattenHeaders(headers) {
- const array = [];
- for (const pair of headers) {
- array.push(pair[0], pair[1]);
- }
- return array;
- }
-
function createRespondWith(responseSenderRid) {
return async function respondWith(resp) {
if (resp instanceof Promise) {
@@ -117,46 +107,66 @@
"First argument to respondWith must be a Response or a promise resolving to a Response.",
);
}
- // If response body is Uint8Array it will be sent synchronously
- // in a single op, in other case a "response body" resource will be
- // created and we'll be streaming it.
- const body = resp[fastBody]();
- let zeroCopyBuf;
- if (body instanceof ArrayBuffer) {
- zeroCopyBuf = new Uint8Array(body);
- } else if (!body) {
- zeroCopyBuf = new Uint8Array(0);
+
+ const innerResp = toInnerResponse(resp);
+
+ // If response body length is known, it will be sent synchronously in a
+ // single op, in other case a "response body" resource will be created and
+ // we'll be streaming it.
+ /** @type {ReadableStream<Uint8Array> | Uint8Array | null} */
+ let respBody = null;
+ if (innerResp.body !== null) {
+ if (innerResp.body.unusable()) throw new TypeError("Body is unusable.");
+ if (innerResp.body.streamOrStatic instanceof ReadableStream) {
+ if (innerResp.body.length === null) {
+ respBody = innerResp.body.stream;
+ } else {
+ const reader = innerResp.body.stream.getReader();
+ const r1 = await reader.read();
+ if (r1.done) throw new TypeError("Unreachable");
+ respBody = r1.value;
+ const r2 = await reader.read();
+ if (!r2.done) throw new TypeError("Unreachable");
+ }
+ } else {
+ innerResp.body.streamOrStatic.consumed = true;
+ respBody = innerResp.body.streamOrStatic.body;
+ }
} else {
- zeroCopyBuf = null;
+ respBody = new Uint8Array(0);
}
const responseBodyRid = await Deno.core.opAsync("op_http_response", [
responseSenderRid,
- resp.status ?? 200,
- flattenHeaders(resp.headers),
- ], zeroCopyBuf);
+ innerResp.status ?? 200,
+ innerResp.headerList,
+ ], respBody instanceof Uint8Array ? respBody : null);
// If `respond` returns a responseBodyRid, we should stream the body
// to that resource.
- if (typeof responseBodyRid === "number") {
- if (!body || !(body instanceof ReadableStream)) {
- throw new Error(
- "internal error: recieved responseBodyRid, but response has no body or is not a stream",
- );
+ if (responseBodyRid !== null) {
+ if (respBody === null || !(respBody instanceof ReadableStream)) {
+ throw new TypeError("Unreachable");
}
- for await (const chunk of body) {
- const data = new Uint8Array(
- chunk.buffer,
- chunk.byteOffset,
- chunk.byteLength,
- );
- await Deno.core.opAsync(
- "op_http_response_write",
- responseBodyRid,
- data,
- );
+ const reader = respBody.getReader();
+ while (true) {
+ const { value, done } = await reader.read();
+ if (done) break;
+ if (!(value instanceof Uint8Array)) {
+ await reader.cancel("value not a Uint8Array");
+ break;
+ }
+ try {
+ await Deno.core.opAsync(
+ "op_http_response_write",
+ responseBodyRid,
+ value,
+ );
+ } catch (err) {
+ await reader.cancel(err);
+ break;
+ }
}
-
// Once all chunks are sent, and the request body is closed, we can close
// the response body.
await Deno.core.opAsync("op_http_response_close", responseBodyRid);