From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../redirect-back-to-original-origin.any.js | 38 +++++++ .../tests/fetch/api/redirect/redirect-count.any.js | 51 ++++++++++ .../api/redirect/redirect-empty-location.any.js | 21 ++++ .../fetch/api/redirect/redirect-keepalive.any.js | 35 +++++++ .../api/redirect/redirect-keepalive.https.any.js | 18 ++++ .../redirect-location-escape.tentative.any.js | 46 +++++++++ .../fetch/api/redirect/redirect-location.any.js | 73 ++++++++++++++ .../fetch/api/redirect/redirect-method.any.js | 112 +++++++++++++++++++++ .../tests/fetch/api/redirect/redirect-mode.any.js | 59 +++++++++++ .../fetch/api/redirect/redirect-origin.any.js | 68 +++++++++++++ .../api/redirect/redirect-referrer-override.any.js | 104 +++++++++++++++++++ .../fetch/api/redirect/redirect-referrer.any.js | 66 ++++++++++++ .../fetch/api/redirect/redirect-schemes.any.js | 19 ++++ .../fetch/api/redirect/redirect-to-dataurl.any.js | 28 ++++++ .../fetch/api/redirect/redirect-upload.h2.any.js | 33 ++++++ 15 files changed, 771 insertions(+) create mode 100644 testing/web-platform/tests/fetch/api/redirect/redirect-back-to-original-origin.any.js create mode 100644 testing/web-platform/tests/fetch/api/redirect/redirect-count.any.js create mode 100644 testing/web-platform/tests/fetch/api/redirect/redirect-empty-location.any.js create mode 100644 testing/web-platform/tests/fetch/api/redirect/redirect-keepalive.any.js create mode 100644 testing/web-platform/tests/fetch/api/redirect/redirect-keepalive.https.any.js create mode 100644 testing/web-platform/tests/fetch/api/redirect/redirect-location-escape.tentative.any.js create mode 100644 testing/web-platform/tests/fetch/api/redirect/redirect-location.any.js create mode 100644 testing/web-platform/tests/fetch/api/redirect/redirect-method.any.js create mode 100644 testing/web-platform/tests/fetch/api/redirect/redirect-mode.any.js create mode 100644 testing/web-platform/tests/fetch/api/redirect/redirect-origin.any.js create mode 100644 testing/web-platform/tests/fetch/api/redirect/redirect-referrer-override.any.js create mode 100644 testing/web-platform/tests/fetch/api/redirect/redirect-referrer.any.js create mode 100644 testing/web-platform/tests/fetch/api/redirect/redirect-schemes.any.js create mode 100644 testing/web-platform/tests/fetch/api/redirect/redirect-to-dataurl.any.js create mode 100644 testing/web-platform/tests/fetch/api/redirect/redirect-upload.h2.any.js (limited to 'testing/web-platform/tests/fetch/api/redirect') diff --git a/testing/web-platform/tests/fetch/api/redirect/redirect-back-to-original-origin.any.js b/testing/web-platform/tests/fetch/api/redirect/redirect-back-to-original-origin.any.js new file mode 100644 index 0000000000..74d731f242 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/redirect/redirect-back-to-original-origin.any.js @@ -0,0 +1,38 @@ +// META: global=window,worker +// META: script=/common/get-host-info.sub.js + +const BASE = location.href; +const IS_HTTPS = new URL(BASE).protocol === 'https:'; +const REMOTE_HOST = get_host_info()['REMOTE_HOST']; +const REMOTE_PORT = + IS_HTTPS ? get_host_info()['HTTPS_PORT'] : get_host_info()['HTTP_PORT']; + +const REMOTE_ORIGIN = + new URL(`//${REMOTE_HOST}:${REMOTE_PORT}`, BASE).origin; +const DESTINATION = new URL('../resources/cors-top.txt', BASE); + +function CreateURL(url, BASE, params) { + const u = new URL(url, BASE); + for (const {name, value} of params) { + u.searchParams.append(name, value); + } + return u; +} + +const redirect = + CreateURL('/fetch/api/resources/redirect.py', REMOTE_ORIGIN, + [{name: 'redirect_status', value: 303}, + {name: 'location', value: DESTINATION.href}]); + +promise_test(async (test) => { + const res = await fetch(redirect.href, {mode: 'no-cors'}); + // This is discussed at https://github.com/whatwg/fetch/issues/737. + assert_equals(res.type, 'opaque'); +}, 'original => remote => original with mode: "no-cors"'); + +promise_test(async (test) => { + const res = await fetch(redirect.href, {mode: 'cors'}); + assert_equals(res.type, 'cors'); +}, 'original => remote => original with mode: "cors"'); + +done(); diff --git a/testing/web-platform/tests/fetch/api/redirect/redirect-count.any.js b/testing/web-platform/tests/fetch/api/redirect/redirect-count.any.js new file mode 100644 index 0000000000..420f9c0dfc --- /dev/null +++ b/testing/web-platform/tests/fetch/api/redirect/redirect-count.any.js @@ -0,0 +1,51 @@ +// META: global=window,worker +// META: script=../resources/utils.js +// META: script=/common/utils.js +// META: timeout=long + +/** + * Fetches a target that returns response with HTTP status code `statusCode` to + * redirect `maxCount` times. + */ +function redirectCountTest(maxCount, {statusCode, shouldPass = true} = {}) { + const desc = `Redirect ${statusCode} ${maxCount} times`; + + const fromUrl = `${RESOURCES_DIR}redirect.py`; + const toUrl = fromUrl; + const token1 = token(); + const url = `${fromUrl}?token=${token1}` + + `&max_age=0` + + `&redirect_status=${statusCode}` + + `&max_count=${maxCount}` + + `&location=${encodeURIComponent(toUrl)}`; + + const requestInit = {'redirect': 'follow'}; + + promise_test((test) => { + return fetch(`${RESOURCES_DIR}clean-stash.py?token=${token1}`) + .then((resp) => { + assert_equals( + resp.status, 200, 'Clean stash response\'s status is 200'); + + if (!shouldPass) + return promise_rejects_js(test, TypeError, fetch(url, requestInit)); + + return fetch(url, requestInit) + .then((resp) => { + assert_equals(resp.status, 200, 'Response\'s status is 200'); + return resp.text(); + }) + .then((body) => { + assert_equals( + body, maxCount.toString(), `Redirected ${maxCount} times`); + }); + }); + }, desc); +} + +for (const statusCode of [301, 302, 303, 307, 308]) { + redirectCountTest(20, {statusCode}); + redirectCountTest(21, {statusCode, shouldPass: false}); +} + +done(); diff --git a/testing/web-platform/tests/fetch/api/redirect/redirect-empty-location.any.js b/testing/web-platform/tests/fetch/api/redirect/redirect-empty-location.any.js new file mode 100644 index 0000000000..487f4d42e9 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/redirect/redirect-empty-location.any.js @@ -0,0 +1,21 @@ +// META: global=window,worker +// META: script=../resources/utils.js + +// Tests receiving a redirect response with a Location header with an empty +// value. + +const url = RESOURCES_DIR + 'redirect-empty-location.py'; + +promise_test(t => { + return promise_rejects_js(t, TypeError, fetch(url, {redirect:'follow'})); +}, 'redirect response with empty Location, follow mode'); + +promise_test(t => { + return fetch(url, {redirect:'manual'}) + .then(resp => { + assert_equals(resp.type, 'opaqueredirect'); + assert_equals(resp.status, 0); + }); +}, 'redirect response with empty Location, manual mode'); + +done(); diff --git a/testing/web-platform/tests/fetch/api/redirect/redirect-keepalive.any.js b/testing/web-platform/tests/fetch/api/redirect/redirect-keepalive.any.js new file mode 100644 index 0000000000..c9ac13f3db --- /dev/null +++ b/testing/web-platform/tests/fetch/api/redirect/redirect-keepalive.any.js @@ -0,0 +1,35 @@ +// META: global=window +// META: timeout=long +// META: title=Fetch API: keepalive handling +// META: script=/common/utils.js +// META: script=/common/get-host-info.sub.js +// META: script=../resources/keepalive-helper.js + +'use strict'; + +const { + HTTP_NOTSAMESITE_ORIGIN, + HTTP_REMOTE_ORIGIN, + HTTP_REMOTE_ORIGIN_WITH_DIFFERENT_PORT +} = get_host_info(); + + +keepaliveRedirectInUnloadTest('same-origin redirect'); +keepaliveRedirectInUnloadTest( + 'same-origin redirect + preflight', {withPreflight: true}); +keepaliveRedirectInUnloadTest('cross-origin redirect', { + origin1: HTTP_REMOTE_ORIGIN, + origin2: HTTP_REMOTE_ORIGIN_WITH_DIFFERENT_PORT +}); +keepaliveRedirectInUnloadTest('cross-origin redirect + preflight', { + origin1: HTTP_REMOTE_ORIGIN, + origin2: HTTP_REMOTE_ORIGIN_WITH_DIFFERENT_PORT, + withPreflight: true +}); +keepaliveRedirectInUnloadTest( + 'redirect to file URL', + {url2: 'file://tmp/bar.txt', expectFetchSucceed: false}); +keepaliveRedirectInUnloadTest('redirect to data URL', { + url2: 'data:text/plain;base64,cmVzcG9uc2UncyBib2R5', + expectFetchSucceed: false +}); diff --git a/testing/web-platform/tests/fetch/api/redirect/redirect-keepalive.https.any.js b/testing/web-platform/tests/fetch/api/redirect/redirect-keepalive.https.any.js new file mode 100644 index 0000000000..54e4bc31fa --- /dev/null +++ b/testing/web-platform/tests/fetch/api/redirect/redirect-keepalive.https.any.js @@ -0,0 +1,18 @@ +// META: global=window +// META: title=Fetch API: keepalive handling +// META: script=/common/utils.js +// META: script=/common/get-host-info.sub.js +// META: script=../resources/keepalive-helper.js + +'use strict'; + +const { + HTTP_NOTSAMESITE_ORIGIN, + HTTPS_NOTSAMESITE_ORIGIN, +} = get_host_info(); + +keepaliveRedirectTest(`mixed content redirect`, { + origin1: HTTPS_NOTSAMESITE_ORIGIN, + origin2: HTTP_NOTSAMESITE_ORIGIN, + expectFetchSucceed: false +}); diff --git a/testing/web-platform/tests/fetch/api/redirect/redirect-location-escape.tentative.any.js b/testing/web-platform/tests/fetch/api/redirect/redirect-location-escape.tentative.any.js new file mode 100644 index 0000000000..779ad70579 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/redirect/redirect-location-escape.tentative.any.js @@ -0,0 +1,46 @@ +// META: global=window,worker +// META: script=../resources/utils.js + +// See https://github.com/whatwg/fetch/issues/883 for the behavior covered by +// this test. As of writing, the Fetch spec has not been updated to cover these. + +// redirectLocation tests that a Location header of |locationHeader| is resolved +// to a URL which ends in |expectedUrlSuffix|. |locationHeader| is interpreted +// as a byte sequence via isomorphic encode, as described in [INFRA]. This +// allows the caller to specify byte sequences which are not valid UTF-8. +// However, this means, e.g., U+2603 must be passed in as "\xe2\x98\x83", its +// UTF-8 encoding, not "\u2603". +// +// [INFRA] https://infra.spec.whatwg.org/#isomorphic-encode +function redirectLocation( + desc, redirectUrl, locationHeader, expectedUrlSuffix) { + promise_test(function(test) { + // Note we use escape() instead of encodeURIComponent(), so that characters + // are escaped as bytes in the isomorphic encoding. + var url = redirectUrl + '?simple=1&location=' + escape(locationHeader); + + return fetch(url, {'redirect': 'follow'}).then(function(resp) { + assert_true( + resp.url.endsWith(expectedUrlSuffix), + resp.url + ' ends with ' + expectedUrlSuffix); + }); + }, desc); +} + +var redirUrl = RESOURCES_DIR + 'redirect.py'; +redirectLocation( + 'Redirect to escaped UTF-8', redirUrl, 'top.txt?%E2%98%83%e2%98%83', + 'top.txt?%E2%98%83%e2%98%83'); +redirectLocation( + 'Redirect to unescaped UTF-8', redirUrl, 'top.txt?\xe2\x98\x83', + 'top.txt?%E2%98%83'); +redirectLocation( + 'Redirect to escaped and unescaped UTF-8', redirUrl, + 'top.txt?\xe2\x98\x83%e2%98%83', 'top.txt?%E2%98%83%e2%98%83'); +redirectLocation( + 'Escaping produces double-percent', redirUrl, 'top.txt?%\xe2\x98\x83', + 'top.txt?%%E2%98%83'); +redirectLocation( + 'Redirect to invalid UTF-8', redirUrl, 'top.txt?\xff', 'top.txt?%FF'); + +done(); diff --git a/testing/web-platform/tests/fetch/api/redirect/redirect-location.any.js b/testing/web-platform/tests/fetch/api/redirect/redirect-location.any.js new file mode 100644 index 0000000000..3d483bdcd4 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/redirect/redirect-location.any.js @@ -0,0 +1,73 @@ +// META: global=window,worker +// META: script=../resources/utils.js + +const VALID_URL = 'top.txt'; +const INVALID_URL = 'invalidurl:'; +const DATA_URL = 'data:text/plain;base64,cmVzcG9uc2UncyBib2R5'; + +/** + * A test to fetch a URL that returns response redirecting to `toUrl` with + * `status` as its HTTP status code. `expectStatus` can be set to test the + * status code in fetch's Promise response. + */ +function redirectLocationTest(toUrlDesc, { + toUrl = undefined, + status, + expectStatus = undefined, + mode, + shouldPass = true +} = {}) { + toUrlDesc = toUrl ? `with ${toUrlDesc}` : `without`; + const desc = `Redirect ${status} in "${mode}" mode ${toUrlDesc} location`; + const url = `${RESOURCES_DIR}redirect.py?redirect_status=${status}` + + (toUrl ? `&location=${encodeURIComponent(toUrl)}` : ''); + const requestInit = {'redirect': mode}; + if (!expectStatus) + expectStatus = status; + + promise_test((test) => { + if (mode === 'error' || !shouldPass) + return promise_rejects_js(test, TypeError, fetch(url, requestInit)); + if (mode === 'manual') + return fetch(url, requestInit).then((resp) => { + assert_equals(resp.status, 0, "Response's status is 0"); + assert_equals(resp.type, "opaqueredirect", "Response's type is opaqueredirect"); + assert_equals(resp.statusText, '', `Response's statusText is ""`); + assert_true(resp.headers.entries().next().done, "Headers should be empty"); + }); + + if (mode === 'follow') + return fetch(url, requestInit).then((resp) => { + assert_equals( + resp.status, expectStatus, `Response's status is ${expectStatus}`); + }); + assert_unreached(`${mode} is not a valid redirect mode`); + }, desc); +} + +// FIXME: We may want to mix redirect-mode and cors-mode. +for (const status of [301, 302, 303, 307, 308]) { + redirectLocationTest('without location', {status, mode: 'follow'}); + redirectLocationTest('without location', {status, mode: 'manual'}); + // FIXME: Add tests for "error" redirect-mode without location. + + // When succeeded, `follow` mode should have followed all redirects. + redirectLocationTest( + 'valid', {toUrl: VALID_URL, status, expectStatus: 200, mode: 'follow'}); + redirectLocationTest('valid', {toUrl: VALID_URL, status, mode: 'manual'}); + redirectLocationTest('valid', {toUrl: VALID_URL, status, mode: 'error'}); + + redirectLocationTest( + 'invalid', + {toUrl: INVALID_URL, status, mode: 'follow', shouldPass: false}); + redirectLocationTest('invalid', {toUrl: INVALID_URL, status, mode: 'manual'}); + redirectLocationTest('invalid', {toUrl: INVALID_URL, status, mode: 'error'}); + + redirectLocationTest( + 'data', {toUrl: DATA_URL, status, mode: 'follow', shouldPass: false}); + // FIXME: Should this pass? + redirectLocationTest('data', {toUrl: DATA_URL, status, mode: 'manual'}); + redirectLocationTest('data', {toUrl: DATA_URL, status, mode: 'error'}); +} + +done(); diff --git a/testing/web-platform/tests/fetch/api/redirect/redirect-method.any.js b/testing/web-platform/tests/fetch/api/redirect/redirect-method.any.js new file mode 100644 index 0000000000..9fe086a9db --- /dev/null +++ b/testing/web-platform/tests/fetch/api/redirect/redirect-method.any.js @@ -0,0 +1,112 @@ +// META: global=window,worker +// META: script=../resources/utils.js + +// Creates a promise_test that fetches a URL that returns a redirect response. +// +// |opts| has additional options: +// |opts.body|: the request body as a string or blob (default is empty body) +// |opts.expectedBodyAsString|: the expected response body as a string. The +// server is expected to echo the request body. The default is the empty string +// if the request after redirection isn't POST; otherwise it's |opts.body|. +// |opts.expectedRequestContentType|: the expected Content-Type of redirected +// request. +function redirectMethod(desc, redirectUrl, redirectLocation, redirectStatus, method, expectedMethod, opts) { + let url = redirectUrl; + let urlParameters = "?redirect_status=" + redirectStatus; + urlParameters += "&location=" + encodeURIComponent(redirectLocation); + + let requestHeaders = { + "Content-Encoding": "Identity", + "Content-Language": "en-US", + "Content-Location": "foo", + }; + let requestInit = {"method": method, "redirect": "follow", "headers" : requestHeaders}; + opts = opts || {}; + if (opts.body) { + requestInit.body = opts.body; + } + + promise_test(function(test) { + return fetch(url + urlParameters, requestInit).then(function(resp) { + let expectedRequestContentType = "NO"; + if (opts.expectedRequestContentType) { + expectedRequestContentType = opts.expectedRequestContentType; + } + + assert_equals(resp.status, 200, "Response's status is 200"); + assert_equals(resp.type, "basic", "Response's type basic"); + assert_equals( + resp.headers.get("x-request-method"), + expectedMethod, + "Request method after redirection is " + expectedMethod); + let hasRequestBodyHeader = true; + if (opts.expectedStripRequestBodyHeader) { + hasRequestBodyHeader = !opts.expectedStripRequestBodyHeader; + } + assert_equals( + resp.headers.get("x-request-content-type"), + expectedRequestContentType, + "Request Content-Type after redirection is " + expectedRequestContentType); + [ + "Content-Encoding", + "Content-Language", + "Content-Location" + ].forEach(header => { + let xHeader = "x-request-" + header.toLowerCase(); + let expectedValue = hasRequestBodyHeader ? requestHeaders[header] : "NO"; + assert_equals( + resp.headers.get(xHeader), + expectedValue, + "Request " + header + " after redirection is " + expectedValue); + }); + assert_true(resp.redirected); + return resp.text().then(function(text) { + let expectedBody = ""; + if (expectedMethod == "POST") { + expectedBody = opts.expectedBodyAsString || requestInit.body; + } + let expectedContentLength = expectedBody ? expectedBody.length.toString() : "NO"; + assert_equals(text, expectedBody, "request body"); + assert_equals( + resp.headers.get("x-request-content-length"), + expectedContentLength, + "Request Content-Length after redirection is " + expectedContentLength); + }); + }); + }, desc); +} + +promise_test(function(test) { + assert_false(new Response().redirected); + return fetch(RESOURCES_DIR + "method.py").then(function(resp) { + assert_equals(resp.status, 200, "Response's status is 200"); + assert_false(resp.redirected); + }); +}, "Response.redirected should be false on not-redirected responses"); + +var redirUrl = RESOURCES_DIR + "redirect.py"; +var locationUrl = "method.py"; + +const stringBody = "this is my body"; +const blobBody = new Blob(["it's me the blob!", " ", "and more blob!"]); +const blobBodyAsString = "it's me the blob! and more blob!"; + +redirectMethod("Redirect 301 with GET", redirUrl, locationUrl, 301, "GET", "GET"); +redirectMethod("Redirect 301 with POST", redirUrl, locationUrl, 301, "POST", "GET", { body: stringBody, expectedStripRequestBodyHeader: true }); +redirectMethod("Redirect 301 with HEAD", redirUrl, locationUrl, 301, "HEAD", "HEAD"); + +redirectMethod("Redirect 302 with GET", redirUrl, locationUrl, 302, "GET", "GET"); +redirectMethod("Redirect 302 with POST", redirUrl, locationUrl, 302, "POST", "GET", { body: stringBody, expectedStripRequestBodyHeader: true }); +redirectMethod("Redirect 302 with HEAD", redirUrl, locationUrl, 302, "HEAD", "HEAD"); + +redirectMethod("Redirect 303 with GET", redirUrl, locationUrl, 303, "GET", "GET"); +redirectMethod("Redirect 303 with POST", redirUrl, locationUrl, 303, "POST", "GET", { body: stringBody, expectedStripRequestBodyHeader: true }); +redirectMethod("Redirect 303 with HEAD", redirUrl, locationUrl, 303, "HEAD", "HEAD"); +redirectMethod("Redirect 303 with TESTING", redirUrl, locationUrl, 303, "TESTING", "GET", { expectedStripRequestBodyHeader: true }); + +redirectMethod("Redirect 307 with GET", redirUrl, locationUrl, 307, "GET", "GET"); +redirectMethod("Redirect 307 with POST (string body)", redirUrl, locationUrl, 307, "POST", "POST", { body: stringBody , expectedRequestContentType: "text/plain;charset=UTF-8"}); +redirectMethod("Redirect 307 with POST (blob body)", redirUrl, locationUrl, 307, "POST", "POST", { body: blobBody, expectedBodyAsString: blobBodyAsString }); +redirectMethod("Redirect 307 with HEAD", redirUrl, locationUrl, 307, "HEAD", "HEAD"); + +done(); diff --git a/testing/web-platform/tests/fetch/api/redirect/redirect-mode.any.js b/testing/web-platform/tests/fetch/api/redirect/redirect-mode.any.js new file mode 100644 index 0000000000..9f1ff98c65 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/redirect/redirect-mode.any.js @@ -0,0 +1,59 @@ +// META: script=/common/get-host-info.sub.js + +var redirectLocation = "cors-top.txt"; +const { ORIGIN, REMOTE_ORIGIN } = get_host_info(); + +function testRedirect(origin, redirectStatus, redirectMode, corsMode) { + var url = new URL("../resources/redirect.py", self.location); + if (origin === "cross-origin") { + url.host = get_host_info().REMOTE_HOST; + url.port = get_host_info().HTTP_PORT; + } + + var urlParameters = "?redirect_status=" + redirectStatus; + urlParameters += "&location=" + encodeURIComponent(redirectLocation); + + var requestInit = {redirect: redirectMode, mode: corsMode}; + + promise_test(function(test) { + if (redirectMode === "error" || + (corsMode === "no-cors" && redirectMode !== "follow" && origin !== "same-origin")) + return promise_rejects_js(test, TypeError, fetch(url + urlParameters, requestInit)); + if (redirectMode === "manual") + return fetch(url + urlParameters, requestInit).then(function(resp) { + assert_equals(resp.status, 0, "Response's status is 0"); + assert_equals(resp.type, "opaqueredirect", "Response's type is opaqueredirect"); + assert_equals(resp.statusText, "", "Response's statusText is \"\""); + assert_equals(resp.url, url + urlParameters, "Response URL should be the original one"); + }); + if (redirectMode === "follow") + return fetch(url + urlParameters, requestInit).then(function(resp) { + if (corsMode !== "no-cors" || origin === "same-origin") { + assert_true(new URL(resp.url).pathname.endsWith(redirectLocation), "Response's url should be the redirected one"); + assert_equals(resp.status, 200, "Response's status is 200"); + } else { + assert_equals(resp.type, "opaque", "Response is opaque"); + } + }); + assert_unreached(redirectMode + " is no a valid redirect mode"); + }, origin + " redirect " + redirectStatus + " in " + redirectMode + " redirect and " + corsMode + " mode"); +} + +for (var origin of ["same-origin", "cross-origin"]) { + for (var statusCode of [301, 302, 303, 307, 308]) { + for (var redirect of ["error", "manual", "follow"]) { + for (var mode of ["cors", "no-cors"]) + testRedirect(origin, statusCode, redirect, mode); + } + } +} + +promise_test(async (t) => { + const destination = `${ORIGIN}/common/blank.html`; + // We use /common/redirect.py intentionally, as we want a CORS error. + const url = + `${REMOTE_ORIGIN}/common/redirect.py?location=${destination}`; + await promise_rejects_js(t, TypeError, fetch(url, { redirect: "manual" })); +}, "manual redirect with a CORS error should be rejected"); + +done(); diff --git a/testing/web-platform/tests/fetch/api/redirect/redirect-origin.any.js b/testing/web-platform/tests/fetch/api/redirect/redirect-origin.any.js new file mode 100644 index 0000000000..6001c509b1 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/redirect/redirect-origin.any.js @@ -0,0 +1,68 @@ +// META: script=/common/utils.js +// META: script=../resources/utils.js +// META: script=/common/get-host-info.sub.js + +const { + HTTP_ORIGIN, + HTTP_REMOTE_ORIGIN, +} = get_host_info(); + +/** + * Fetches `fromUrl` with 'cors' and 'follow' modes that returns response to + * redirect to `toUrl`. + */ +function testOriginAfterRedirection( + desc, method, fromUrl, toUrl, statusCode, expectedOrigin) { + desc = `[${method}] Redirect ${statusCode} ${desc}`; + const token1 = token(); + const url = `${fromUrl}?token=${token1}&max_age=0` + + `&redirect_status=${statusCode}` + + `&location=${encodeURIComponent(toUrl)}`; + + const requestInit = {method, 'mode': 'cors', 'redirect': 'follow'}; + + promise_test(function(test) { + return fetch(`${RESOURCES_DIR}clean-stash.py?token=${token1}`) + .then((cleanResponse) => { + assert_equals( + cleanResponse.status, 200, + `Clean stash response's status is 200`); + return fetch(url, requestInit).then((redirectResponse) => { + assert_equals( + redirectResponse.status, 200, + `Inspect header response's status is 200`); + assert_equals( + redirectResponse.headers.get('x-request-origin'), + expectedOrigin, 'Check origin header'); + }); + }); + }, desc); +} + +const FROM_URL = `${RESOURCES_DIR}redirect.py`; +const CORS_FROM_URL = + `${HTTP_REMOTE_ORIGIN}${dirname(location.pathname)}${FROM_URL}`; +const TO_URL = `${HTTP_ORIGIN}${dirname(location.pathname)}${ + RESOURCES_DIR}inspect-headers.py?headers=origin`; +const CORS_TO_URL = `${HTTP_REMOTE_ORIGIN}${dirname(location.pathname)}${ + RESOURCES_DIR}inspect-headers.py?cors&headers=origin`; + +for (const statusCode of [301, 302, 303, 307, 308]) { + for (const method of ['GET', 'POST']) { + testOriginAfterRedirection( + 'Same origin to same origin', method, FROM_URL, TO_URL, statusCode, + null); + testOriginAfterRedirection( + 'Same origin to other origin', method, FROM_URL, CORS_TO_URL, + statusCode, HTTP_ORIGIN); + testOriginAfterRedirection( + 'Other origin to other origin', method, CORS_FROM_URL, CORS_TO_URL, + statusCode, HTTP_ORIGIN); + // TODO(crbug.com/1432059): Fix broken tests. + testOriginAfterRedirection( + 'Other origin to same origin', method, CORS_FROM_URL, `${TO_URL}&cors`, + statusCode, 'null'); + } +} + +done(); diff --git a/testing/web-platform/tests/fetch/api/redirect/redirect-referrer-override.any.js b/testing/web-platform/tests/fetch/api/redirect/redirect-referrer-override.any.js new file mode 100644 index 0000000000..56e55d79e1 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/redirect/redirect-referrer-override.any.js @@ -0,0 +1,104 @@ +// META: timeout=long +// META: script=/common/utils.js +// META: script=../resources/utils.js +// META: script=/common/get-host-info.sub.js + +function getExpectation(expectations, init, initScenario, redirectPolicy, redirectScenario) { + let policies = [ + expectations[initPolicy][initScenario], + expectations[redirectPolicy][redirectScenario] + ]; + + if (policies.includes("omitted")) { + return null; + } else if (policies.includes("origin")) { + return referrerOrigin; + } else { + // "stripped-referrer" + return referrerUrl; + } +} + +function testReferrerAfterRedirection(desc, redirectUrl, redirectLocation, referrerPolicy, redirectReferrerPolicy, expectedReferrer) { + var url = redirectUrl; + var urlParameters = "?location=" + encodeURIComponent(redirectLocation); + var description = desc + ", " + referrerPolicy + " init, " + redirectReferrerPolicy + " redirect header "; + + if (redirectReferrerPolicy) + urlParameters += "&redirect_referrerpolicy=" + redirectReferrerPolicy; + + var requestInit = {"redirect": "follow", "referrerPolicy": referrerPolicy}; + promise_test(function(test) { + return fetch(url + urlParameters, requestInit).then(function(response) { + assert_equals(response.status, 200, "Inspect header response's status is 200"); + assert_equals(response.headers.get("x-request-referer"), expectedReferrer ? expectedReferrer : null, "Check referrer header"); + }); + }, description); +} + +var referrerOrigin = get_host_info().HTTP_ORIGIN + "/"; +var referrerUrl = location.href; + +var redirectUrl = RESOURCES_DIR + "redirect.py"; +var locationUrl = get_host_info().HTTP_ORIGIN + dirname(location.pathname) + RESOURCES_DIR + "inspect-headers.py?headers=referer"; +var crossLocationUrl = get_host_info().HTTP_REMOTE_ORIGIN + dirname(location.pathname) + RESOURCES_DIR + "inspect-headers.py?cors&headers=referer"; + +var expectations = { + "no-referrer": { + "same-origin": "omitted", + "cross-origin": "omitted" + }, + "no-referrer-when-downgrade": { + "same-origin": "stripped-referrer", + "cross-origin": "stripped-referrer" + }, + "origin": { + "same-origin": "origin", + "cross-origin": "origin" + }, + "origin-when-cross-origin": { + "same-origin": "stripped-referrer", + "cross-origin": "origin", + }, + "same-origin": { + "same-origin": "stripped-referrer", + "cross-origin": "omitted" + }, + "strict-origin": { + "same-origin": "origin", + "cross-origin": "origin" + }, + "strict-origin-when-cross-origin": { + "same-origin": "stripped-referrer", + "cross-origin": "origin" + }, + "unsafe-url": { + "same-origin": "stripped-referrer", + "cross-origin": "stripped-referrer" + } +}; + +for (var initPolicy in expectations) { + for (var redirectPolicy in expectations) { + + // Redirect to same-origin URL + testReferrerAfterRedirection( + "Same origin redirection", + redirectUrl, + locationUrl, + initPolicy, + redirectPolicy, + getExpectation(expectations, initPolicy, "same-origin", redirectPolicy, "same-origin")); + + // Redirect to cross-origin URL + testReferrerAfterRedirection( + "Cross origin redirection", + redirectUrl, + crossLocationUrl, + initPolicy, + redirectPolicy, + getExpectation(expectations, initPolicy, "same-origin", redirectPolicy, "cross-origin")); + } +} + +done(); diff --git a/testing/web-platform/tests/fetch/api/redirect/redirect-referrer.any.js b/testing/web-platform/tests/fetch/api/redirect/redirect-referrer.any.js new file mode 100644 index 0000000000..99fda42e69 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/redirect/redirect-referrer.any.js @@ -0,0 +1,66 @@ +// META: timeout=long +// META: script=/common/utils.js +// META: script=../resources/utils.js +// META: script=/common/get-host-info.sub.js + +function testReferrerAfterRedirection(desc, redirectUrl, redirectLocation, referrerPolicy, redirectReferrerPolicy, expectedReferrer) { + var url = redirectUrl; + var urlParameters = "?location=" + encodeURIComponent(redirectLocation); + + if (redirectReferrerPolicy) + urlParameters += "&redirect_referrerpolicy=" + redirectReferrerPolicy; + + var requestInit = {"redirect": "follow", "referrerPolicy": referrerPolicy}; + + promise_test(function(test) { + return fetch(url + urlParameters, requestInit).then(function(response) { + assert_equals(response.status, 200, "Inspect header response's status is 200"); + assert_equals(response.headers.get("x-request-referer"), expectedReferrer ? expectedReferrer : null, "Check referrer header"); + }); + }, desc); +} + +var referrerOrigin = get_host_info().HTTP_ORIGIN + "/"; +var referrerUrl = location.href; + +var redirectUrl = RESOURCES_DIR + "redirect.py"; +var locationUrl = get_host_info().HTTP_ORIGIN + dirname(location.pathname) + RESOURCES_DIR + "inspect-headers.py?headers=referer"; +var crossLocationUrl = get_host_info().HTTP_REMOTE_ORIGIN + dirname(location.pathname) + RESOURCES_DIR + "inspect-headers.py?cors&headers=referer"; + +testReferrerAfterRedirection("Same origin redirection, empty init, unsafe-url redirect header ", redirectUrl, locationUrl, "", "unsafe-url", referrerUrl); +testReferrerAfterRedirection("Same origin redirection, empty init, no-referrer-when-downgrade redirect header ", redirectUrl, locationUrl, "", "no-referrer-when-downgrade", referrerUrl); +testReferrerAfterRedirection("Same origin redirection, empty init, same-origin redirect header ", redirectUrl, locationUrl, "", "same-origin", referrerUrl); +testReferrerAfterRedirection("Same origin redirection, empty init, origin redirect header ", redirectUrl, locationUrl, "", "origin", referrerOrigin); +testReferrerAfterRedirection("Same origin redirection, empty init, origin-when-cross-origin redirect header ", redirectUrl, locationUrl, "", "origin-when-cross-origin", referrerUrl); +testReferrerAfterRedirection("Same origin redirection, empty init, no-referrer redirect header ", redirectUrl, locationUrl, "", "no-referrer", null); +testReferrerAfterRedirection("Same origin redirection, empty init, strict-origin redirect header ", redirectUrl, locationUrl, "", "strict-origin", referrerOrigin); +testReferrerAfterRedirection("Same origin redirection, empty init, strict-origin-when-cross-origin redirect header ", redirectUrl, locationUrl, "", "strict-origin-when-cross-origin", referrerUrl); + +testReferrerAfterRedirection("Same origin redirection, empty redirect header, unsafe-url init ", redirectUrl, locationUrl, "unsafe-url", "", referrerUrl); +testReferrerAfterRedirection("Same origin redirection, empty redirect header, no-referrer-when-downgrade init ", redirectUrl, locationUrl, "no-referrer-when-downgrade", "", referrerUrl); +testReferrerAfterRedirection("Same origin redirection, empty redirect header, same-origin init ", redirectUrl, locationUrl, "same-origin", "", referrerUrl); +testReferrerAfterRedirection("Same origin redirection, empty redirect header, origin init ", redirectUrl, locationUrl, "origin", "", referrerOrigin); +testReferrerAfterRedirection("Same origin redirection, empty redirect header, origin-when-cross-origin init ", redirectUrl, locationUrl, "origin-when-cross-origin", "", referrerUrl); +testReferrerAfterRedirection("Same origin redirection, empty redirect header, no-referrer init ", redirectUrl, locationUrl, "no-referrer", "", null); +testReferrerAfterRedirection("Same origin redirection, empty redirect header, strict-origin init ", redirectUrl, locationUrl, "strict-origin", "", referrerOrigin); +testReferrerAfterRedirection("Same origin redirection, empty redirect header, strict-origin-when-cross-origin init ", redirectUrl, locationUrl, "strict-origin-when-cross-origin", "", referrerUrl); + +testReferrerAfterRedirection("Cross origin redirection, empty init, unsafe-url redirect header ", redirectUrl, crossLocationUrl, "", "unsafe-url", referrerUrl); +testReferrerAfterRedirection("Cross origin redirection, empty init, no-referrer-when-downgrade redirect header ", redirectUrl, crossLocationUrl, "", "no-referrer-when-downgrade", referrerUrl); +testReferrerAfterRedirection("Cross origin redirection, empty init, same-origin redirect header ", redirectUrl, crossLocationUrl, "", "same-origin", null); +testReferrerAfterRedirection("Cross origin redirection, empty init, origin redirect header ", redirectUrl, crossLocationUrl, "", "origin", referrerOrigin); +testReferrerAfterRedirection("Cross origin redirection, empty init, origin-when-cross-origin redirect header ", redirectUrl, crossLocationUrl, "", "origin-when-cross-origin", referrerOrigin); +testReferrerAfterRedirection("Cross origin redirection, empty init, no-referrer redirect header ", redirectUrl, crossLocationUrl, "", "no-referrer", null); +testReferrerAfterRedirection("Cross origin redirection, empty init, strict-origin redirect header ", redirectUrl, crossLocationUrl, "", "strict-origin", referrerOrigin); +testReferrerAfterRedirection("Cross origin redirection, empty init, strict-origin-when-cross-origin redirect header ", redirectUrl, crossLocationUrl, "", "strict-origin-when-cross-origin", referrerOrigin); + +testReferrerAfterRedirection("Cross origin redirection, empty redirect header, unsafe-url init ", redirectUrl, crossLocationUrl, "unsafe-url", "", referrerUrl); +testReferrerAfterRedirection("Cross origin redirection, empty redirect header, no-referrer-when-downgrade init ", redirectUrl, crossLocationUrl, "no-referrer-when-downgrade", "", referrerUrl); +testReferrerAfterRedirection("Cross origin redirection, empty redirect header, same-origin init ", redirectUrl, crossLocationUrl, "same-origin", "", null); +testReferrerAfterRedirection("Cross origin redirection, empty redirect header, origin init ", redirectUrl, crossLocationUrl, "origin", "", referrerOrigin); +testReferrerAfterRedirection("Cross origin redirection, empty redirect header, origin-when-cross-origin init ", redirectUrl, crossLocationUrl, "origin-when-cross-origin", "", referrerOrigin); +testReferrerAfterRedirection("Cross origin redirection, empty redirect header, no-referrer init ", redirectUrl, crossLocationUrl, "no-referrer", "", null); +testReferrerAfterRedirection("Cross origin redirection, empty redirect header, strict-origin init ", redirectUrl, crossLocationUrl, "strict-origin", "", referrerOrigin); +testReferrerAfterRedirection("Cross origin redirection, empty redirect header, strict-origin-when-cross-origin init ", redirectUrl, crossLocationUrl, "strict-origin-when-cross-origin", "", referrerOrigin); + +done(); diff --git a/testing/web-platform/tests/fetch/api/redirect/redirect-schemes.any.js b/testing/web-platform/tests/fetch/api/redirect/redirect-schemes.any.js new file mode 100644 index 0000000000..31ec124fd6 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/redirect/redirect-schemes.any.js @@ -0,0 +1,19 @@ +// META: title=Fetch: handling different schemes in redirects +// META: global=window,worker +// META: script=/common/get-host-info.sub.js + +// All non-HTTP(S) schemes cannot survive redirects +var url = "../resources/redirect.py?location="; +var tests = [ + url + "mailto:a@a.com", + url + "data:,HI", + url + "facetime:a@a.org", + url + "about:blank", + url + "about:unicorn", + url + "blob:djfksfjs" +]; +tests.forEach(function(url) { + promise_test(function(test) { + return promise_rejects_js(test, TypeError, fetch(url)) + }) +}) diff --git a/testing/web-platform/tests/fetch/api/redirect/redirect-to-dataurl.any.js b/testing/web-platform/tests/fetch/api/redirect/redirect-to-dataurl.any.js new file mode 100644 index 0000000000..9d0f147349 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/redirect/redirect-to-dataurl.any.js @@ -0,0 +1,28 @@ +// META: global=window,worker +// META: script=/common/get-host-info.sub.js + +var dataURL = "data:text/plain;base64,cmVzcG9uc2UncyBib2R5"; +var body = "response's body"; +var contentType = "text/plain"; + +function redirectDataURL(desc, redirectUrl, mode) { + var url = redirectUrl + "?cors&location=" + encodeURIComponent(dataURL); + + var requestInit = {"mode": mode}; + + promise_test(function(test) { + return promise_rejects_js(test, TypeError, fetch(url, requestInit)); + }, desc); +} + +var redirUrl = get_host_info().HTTP_ORIGIN + "/fetch/api/resources/redirect.py"; +var corsRedirUrl = get_host_info().HTTP_REMOTE_ORIGIN + "/fetch/api/resources/redirect.py"; + +redirectDataURL("Testing data URL loading after same-origin redirection (cors mode)", redirUrl, "cors"); +redirectDataURL("Testing data URL loading after same-origin redirection (no-cors mode)", redirUrl, "no-cors"); +redirectDataURL("Testing data URL loading after same-origin redirection (same-origin mode)", redirUrl, "same-origin"); + +redirectDataURL("Testing data URL loading after cross-origin redirection (cors mode)", corsRedirUrl, "cors"); +redirectDataURL("Testing data URL loading after cross-origin redirection (no-cors mode)", corsRedirUrl, "no-cors"); + +done(); diff --git a/testing/web-platform/tests/fetch/api/redirect/redirect-upload.h2.any.js b/testing/web-platform/tests/fetch/api/redirect/redirect-upload.h2.any.js new file mode 100644 index 0000000000..521bd3adc2 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/redirect/redirect-upload.h2.any.js @@ -0,0 +1,33 @@ +// META: global=window,worker +// META: script=../resources/utils.js +// META: script=/common/utils.js +// META: script=/common/get-host-info.sub.js + +const redirectUrl = RESOURCES_DIR + "redirect.h2.py"; +const redirectLocation = "top.txt"; + +async function fetchStreamRedirect(statusCode) { + const url = RESOURCES_DIR + "redirect.h2.py" + + `?redirect_status=${statusCode}&location=${redirectLocation}`; + const requestInit = {method: "POST"}; + requestInit["body"] = new ReadableStream({start: controller => { + const encoder = new TextEncoder(); + controller.enqueue(encoder.encode("Test")); + controller.close(); + }}); + requestInit.duplex = "half"; + return fetch(url, requestInit); +} + +promise_test(async () => { + const resp = await fetchStreamRedirect(303); + assert_equals(resp.status, 200); + assert_true(new URL(resp.url).pathname.endsWith(redirectLocation), + "Response's url should be the redirected one"); +}, "Fetch upload streaming should be accepted on 303"); + +for (const statusCode of [301, 302, 307, 308]) { + promise_test(t => { + return promise_rejects_js(t, TypeError, fetchStreamRedirect(statusCode)); + }, `Fetch upload streaming should fail on ${statusCode}`); +} -- cgit v1.2.3