diff --git a/op_crates/fetch/26_fetch.js b/op_crates/fetch/26_fetch.js index 0bcd8f85ca..e713b48edd 100644 --- a/op_crates/fetch/26_fetch.js +++ b/op_crates/fetch/26_fetch.js @@ -686,6 +686,7 @@ // fastBody and dontValidateUrl allow users to opt out of certain behaviors const fastBody = Symbol("Body#fast"); const dontValidateUrl = Symbol("dontValidateUrl"); + const lazyHeaders = Symbol("lazyHeaders"); class Body { #contentType = ""; @@ -960,7 +961,7 @@ #method = "GET"; /** @type {string} */ #url = ""; - /** @type {Headers} */ + /** @type {Headers | string[][]} */ #headers; /** @type {"include" | "omit" | "same-origin" | undefined} */ #credentials = "omit"; @@ -999,16 +1000,32 @@ } let headers; + let contentType = ""; // prefer headers from init if (init.headers) { - headers = new Headers(init.headers); + if (init[lazyHeaders] && Array.isArray(init.headers)) { + // Trust the headers are valid, and only put them into the `Headers` + // strucutre when the user accesses the property. We also assume that + // all passed headers are lower-case (as is the case when they come + // from hyper in Rust), and that headers are of type + // `[string, string][]`. + headers = init.headers; + for (const tuple of headers) { + if (tuple[0] === "content-type") { + contentType = tuple[1]; + } + } + } else { + headers = new Headers(init.headers); + contentType = headers.get("content-type") || ""; + } } else if (input instanceof Request) { headers = input.headers; + contentType = headers.get("content-type") || ""; } else { headers = new Headers(); } - const contentType = headers.get("content-type") || ""; super(b, { contentType }); this.#headers = headers; @@ -1016,9 +1033,9 @@ if (input.bodyUsed) { throw TypeError(BodyUsedError); } + // headers are already set above. no reason to do it again this.#method = input.method; this.#url = input.url; - this.#headers = new Headers(input.headers); this.#credentials = input.credentials; } else { // Constructing a URL just for validation is known to be expensive. @@ -1085,6 +1102,9 @@ } get headers() { + if (!(this.#headers instanceof Headers)) { + this.#headers = new Headers(this.#headers); + } return this.#headers; } @@ -1515,5 +1535,6 @@ createHttpClient, fastBody, dontValidateUrl, + lazyHeaders, }; })(this); diff --git a/runtime/js/40_http.js b/runtime/js/40_http.js index 4a2dcdf4a3..18ead84c33 100644 --- a/runtime/js/40_http.js +++ b/runtime/js/40_http.js @@ -2,7 +2,7 @@ "use strict"; ((window) => { - const { Request, dontValidateUrl, fastBody, Response } = + const { Request, dontValidateUrl, lazyHeaders, fastBody, Response } = window.__bootstrap.fetch; const { Headers } = window.__bootstrap.headers; const errors = window.__bootstrap.errors.errors; @@ -61,8 +61,9 @@ const request = new Request(url, { body, method, - headers: new Headers(headersList), + headers: headersList, [dontValidateUrl]: true, + [lazyHeaders]: true, }); const respondWith = createRespondWith(responseSenderRid, this.#rid);