From 84290e6de6e87c0c4c80da58cfa7365a88aac7c4 Mon Sep 17 00:00:00 2001 From: Tom Lienard Date: Wed, 28 Dec 2022 10:26:01 +0100 Subject: [PATCH] fix(js-runtime): `Response.error()` & `Response.redirect()` WPT conformance (#429) * fix(js-runtime): `Response.error()` & `Response.redirect()` WPT conformance * chore: add changeset * fix: current-results log --- .changeset/loud-clocks-tell.md | 5 +++ packages/js-runtime/src/index.ts | 4 ++ .../js-runtime/src/runtime/http/Headers.ts | 13 +++++++ .../js-runtime/src/runtime/http/Response.ts | 38 ++++++++++++------- packages/js-runtime/src/runtime/http/URL.ts | 1 - packages/wpt-runner/current-results.md | 28 +++++++------- 6 files changed, 61 insertions(+), 28 deletions(-) create mode 100644 .changeset/loud-clocks-tell.md diff --git a/.changeset/loud-clocks-tell.md b/.changeset/loud-clocks-tell.md new file mode 100644 index 000000000..dbdab5330 --- /dev/null +++ b/.changeset/loud-clocks-tell.md @@ -0,0 +1,5 @@ +--- +'@lagon/js-runtime': patch +--- + +Fix implementation of `Response.error()` & `Response.redirect()` diff --git a/packages/js-runtime/src/index.ts b/packages/js-runtime/src/index.ts index 3f080480c..1134880e4 100644 --- a/packages/js-runtime/src/index.ts +++ b/packages/js-runtime/src/index.ts @@ -74,6 +74,10 @@ declare global { interface Blob { readonly buffer: Uint8Array; } + + interface Headers { + immutable: boolean; + } } export async function masterHandler(request: { diff --git a/packages/js-runtime/src/runtime/http/Headers.ts b/packages/js-runtime/src/runtime/http/Headers.ts index 741001bba..d6b981d1f 100644 --- a/packages/js-runtime/src/runtime/http/Headers.ts +++ b/packages/js-runtime/src/runtime/http/Headers.ts @@ -1,6 +1,7 @@ (globalThis => { globalThis.Headers = class { private readonly h: Map = new Map(); + immutable = false; constructor(init?: HeadersInit) { if (init === null) { @@ -49,11 +50,19 @@ } append(name: string, value: string) { + if (this.immutable) { + throw new TypeError('Headers are immutable'); + } + name = name.toLowerCase(); this.addValue(name, value); } delete(name: string) { + if (this.immutable) { + throw new TypeError('Headers are immutable'); + } + name = name.toLowerCase(); this.h.delete(name); } @@ -81,6 +90,10 @@ } set(name: string, value: string) { + if (this.immutable) { + throw new TypeError('Headers are immutable'); + } + name = name.toLowerCase(); value = String(value); this.h.set(name, [value]); diff --git a/packages/js-runtime/src/runtime/http/Response.ts b/packages/js-runtime/src/runtime/http/Response.ts index cc1888136..cc58711da 100644 --- a/packages/js-runtime/src/runtime/http/Response.ts +++ b/packages/js-runtime/src/runtime/http/Response.ts @@ -3,6 +3,8 @@ import { RequestResponseBody } from './body'; (globalThis => { // https://fetch.spec.whatwg.org/#null-body-status const NULL_BODY_STATUS = [101, 103, 204, 205, 304]; + // https://fetch.spec.whatwg.org/#redirect-status + const REDIRECT_STATUS = [301, 302, 303, 307, 308]; globalThis.Response = class extends RequestResponseBody { ok: boolean; @@ -25,8 +27,8 @@ import { RequestResponseBody } from './body'; this.ok = true; } - this.status = init?.status || 200; - this.statusText = init?.statusText || ''; + this.status = init?.status ?? 200; + this.statusText = init?.statusText ?? ''; // TODO: investigate // @ts-expect-error ResponseInit doesn't have url this.url = init?.url || ''; @@ -43,19 +45,29 @@ import { RequestResponseBody } from './body'; } static error(): Response { - return { - ...new Response(), - type: 'error', - }; + const response = new Response(null, { + status: 0, + }); + // @ts-expect-error we modify a read-only property + response.type = 'error'; + response.headers.immutable = true; + + return response; } - static redirect(url: string | URL, status?: number): Response { - const response = { - ...new Response(null, { - status, - }), - url: url.toString(), - }; + static redirect(url: string | URL, status = 302): Response { + if (!REDIRECT_STATUS.includes(status)) { + throw new RangeError('Invalid status code'); + } + + const response = new Response(null, { + status, + headers: { + Location: new URL(url).toString(), + }, + }); + // @ts-expect-error we modify a read-only property + response.type = 'default'; return response; } diff --git a/packages/js-runtime/src/runtime/http/URL.ts b/packages/js-runtime/src/runtime/http/URL.ts index f675b20c9..2c4ed04f3 100644 --- a/packages/js-runtime/src/runtime/http/URL.ts +++ b/packages/js-runtime/src/runtime/http/URL.ts @@ -82,7 +82,6 @@ } get href(): string { - // TODO: username and password const credentials = this.username + (this.password ? ':' + this.password : ''); let href = diff --git a/packages/wpt-runner/current-results.md b/packages/wpt-runner/current-results.md index 742962256..efa63c6a1 100644 --- a/packages/wpt-runner/current-results.md +++ b/packages/wpt-runner/current-results.md @@ -473,8 +473,8 @@ TEST DONE 0 Can override Content-Type for Response with string body TEST DONE 0 Can override Content-Type for Response with ReadableStream body TEST DONE 1 Default Content-Type for Response with FormData body Running ../../tools/wpt/fetch/api/response/response-static-error.any.js -TEST DONE 1 Check response returned by static method error() -TEST DONE 1 the 'guard' of the Headers instance should be immutable +TEST DONE 0 Check response returned by static method error() +TEST DONE 0 the 'guard' of the Headers instance should be immutable Running ../../tools/wpt/fetch/api/response/response-static-json.any.js TEST DONE 0 Throws TypeError when calling static json() with a status of 204 TEST DONE 0 Throws TypeError when calling static json() with a status of 205 @@ -490,17 +490,17 @@ TEST DONE 0 Check response returned by static json() with init {"headers":{"x-fo TEST DONE 0 Check static json() encodes JSON objects correctly TEST DONE 0 Check static json() propagates JSON serializer errors Running ../../tools/wpt/fetch/api/response/response-static-redirect.any.js -TEST DONE 1 Check default redirect response -TEST DONE 1 Check response returned by static method redirect(), status = 301 -TEST DONE 1 Check response returned by static method redirect(), status = 302 -TEST DONE 1 Check response returned by static method redirect(), status = 303 -TEST DONE 1 Check response returned by static method redirect(), status = 307 -TEST DONE 1 Check response returned by static method redirect(), status = 308 +TEST DONE 0 Check default redirect response +TEST DONE 0 Check response returned by static method redirect(), status = 301 +TEST DONE 0 Check response returned by static method redirect(), status = 302 +TEST DONE 0 Check response returned by static method redirect(), status = 303 +TEST DONE 0 Check response returned by static method redirect(), status = 307 +TEST DONE 0 Check response returned by static method redirect(), status = 308 TEST DONE 1 Check error returned when giving invalid url to redirect() -TEST DONE 1 Check error returned when giving invalid status to redirect(), status = 200 -TEST DONE 1 Check error returned when giving invalid status to redirect(), status = 309 -TEST DONE 1 Check error returned when giving invalid status to redirect(), status = 400 -TEST DONE 1 Check error returned when giving invalid status to redirect(), status = 500 +TEST DONE 0 Check error returned when giving invalid status to redirect(), status = 200 +TEST DONE 0 Check error returned when giving invalid status to redirect(), status = 309 +TEST DONE 0 Check error returned when giving invalid status to redirect(), status = 400 +TEST DONE 0 Check error returned when giving invalid status to redirect(), status = 500 Running ../../tools/wpt/fetch/api/response/response-stream-bad-chunk.any.js TEST DONE 1 ReadableStream with non-Uint8Array chunk passed to Response.arrayBuffer() causes TypeError TEST DONE 1 ReadableStream with non-Uint8Array chunk passed to Response.blob() causes TypeError @@ -1497,5 +1497,5 @@ TEST DONE 0 Blob.text() different charset param with non-ascii input TEST DONE 1 Blob.text() invalid utf-8 input TEST DONE 1 Blob.text() concurrent reads -1402 tests, 349 passed, 1047 failed - -> 24% conformance +1402 tests, 361 passed, 1035 failed + -> 25% conformance