diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/cors | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/cors')
44 files changed, 2589 insertions, 0 deletions
diff --git a/testing/web-platform/tests/cors/304.htm b/testing/web-platform/tests/cors/304.htm new file mode 100644 index 0000000000..2aee01417a --- /dev/null +++ b/testing/web-platform/tests/cors/304.htm @@ -0,0 +1,98 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CORS - 304 Responses</title> +<meta name="timeout" content="long"> +<meta name=author title="Mark Nottingham" href="mailto:mnot@mnot.net"> + +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=support.js?pipe=sub></script> + +<h1>CORS - 304 Responses</h1> +<div id=log></div> +<script> + + +/* + * 304 Responses + */ + +// A header used to correlate requests and responses +var state_header = "content-language" + +/* Make a request; call ready(client) when done */ +function req(url, id, t, ready) { + var client = new XMLHttpRequest() + client.open('GET', url, true) + client.setRequestHeader(state_header, id) + client.send() + client.onreadystatechange = function() { + if (client.readyState == client.DONE) { + t.step(function() { + assert_not_equals(client.status, 299, "req " + id + " server says: " + client.responseText) + }) + ready(client) + } + } + return client +} + +/* + * Make two requests to test cache behaviour. + * The second is made after the first is done and a delay, to make sure it gets into cache. + */ +function two_reqs(id1, id2, should_have_same_body, t, done) { + var rand = Date.now() + var url = CROSSDOMAIN + 'resources/304.py?id=' + id1 + '&r=%s' + rand + + var client1 = req(url, id1, t, function(client1) { + t.step(function() { + assert_equals(client1.response, "Success", "didn't get successful 1st response;") + assert_equals(client1.getResponseHeader(state_header), id1, "1st response didn't come from server;") + }) + + t.step_timeout(function() { + req(url, id2, t, function(client2) { + t.step(function() { + if (should_have_same_body) { + assert_equals(client1.response, client2.response, "response bodies were different;") +// var res_id2 = client2.getResponseHeader(state_header) +// assert_not_equals(res_id2, id1, "2nd response doesn't appear to have updated cached headers;") +// assert_not_equals(res_id2, null, "2nd response didn't expose request identifier;") +// assert_equals(res_id2, id2, "2nd response is associated with a different request (!);") + } + done(client1, client2) + }) + t.done() + }) + }, 5000) + }) +} + +async_test(function(t) { + two_reqs('1', '2', true, t, function(client1, client2) { + assert_equals(client1.getResponseHeader("A"), null, "'A' header exposed without permission;") + }) +}, "A 304 response with no CORS headers inherits from the stored response") + +async_test(function(t) { + two_reqs('3', '4', true, t, function(client1, client2) { + assert_equals(client2.getResponseHeader("A"), "4", "304 didn't expose 'A' header, even though allowed;") + assert_equals(client2.getResponseHeader("B"), "4", "304 didn't expose 'B' header even though allowed;") + }) +}, "A 304 can expand Access-Control-Expose-Headers") + +async_test(function(t) { + two_reqs('5', '6', true, t, function(client1, client2) { + assert_equals(client2.getResponseHeader("B"), null, "2nd 304 exposed 'B' header;") + }) +}, "A 304 can contract Access-Control-Expose-Headers") + +async_test(function(t) { + two_reqs('7', '8', false, t, function(client1, client2) { + assert_not_equals(client1.response, client2.response, "Access granted even though 304 updated it to disallow;") + }) +}, "A 304 can change Access-Control-Allow-Origin") + + +</script> diff --git a/testing/web-platform/tests/cors/META.yml b/testing/web-platform/tests/cors/META.yml new file mode 100644 index 0000000000..08dc73704f --- /dev/null +++ b/testing/web-platform/tests/cors/META.yml @@ -0,0 +1,7 @@ +spec: https://fetch.spec.whatwg.org/#http-cors-protocol +suggested_reviewers: + - zqzhang + - odinho + - hillbrad + - jdm + - annevk diff --git a/testing/web-platform/tests/cors/README.md b/testing/web-platform/tests/cors/README.md new file mode 100644 index 0000000000..aa5ff90d48 --- /dev/null +++ b/testing/web-platform/tests/cors/README.md @@ -0,0 +1,8 @@ +Tests for the [Fetch Standard](https://fetch.spec.whatwg.org/). + +These tests are located here as originally the CORS protocol was defined on its own. + +More CORS tests can be found in + +* /fetch +* /xhr diff --git a/testing/web-platform/tests/cors/access-control-expose-headers-parsing.window.js b/testing/web-platform/tests/cors/access-control-expose-headers-parsing.window.js new file mode 100644 index 0000000000..a139ff6f1e --- /dev/null +++ b/testing/web-platform/tests/cors/access-control-expose-headers-parsing.window.js @@ -0,0 +1,15 @@ +promise_test(() => fetch("resources/access-control-expose-headers.json").then(res => res.json()).then(runTests), "Loading JSON…"); + +function runTests(allTestData) { + allTestData.forEach(testData => { + const encodedInput = encodeURIComponent(testData.input); + promise_test(() => { + const relativeURL = "resources/expose-headers.py?expose=" + encodedInput, + url = new URL(relativeURL, location.href).href.replace("://", "://élève."); + return fetch(url).then(res => { + assert_equals(res.headers.get("content-language"), "mkay"); + assert_equals(res.headers.get("bb-8"), (testData.exposed ? "hey" : null)); + }); + }, "Parsing: " + encodedInput); + }) +} diff --git a/testing/web-platform/tests/cors/basic.htm b/testing/web-platform/tests/cors/basic.htm new file mode 100644 index 0000000000..85d443190d --- /dev/null +++ b/testing/web-platform/tests/cors/basic.htm @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Basic CORS</title> +<link rel=help href=https://fetch.spec.whatwg.org/> +<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com"> + +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=/common/utils.js></script> +<script src=support.js?pipe=sub></script> +<div id=log></div> + +<script> +function cors(desc, scheme, subdomain = "", port = location.port) { + const sameorigin = !scheme; + const base = + sameorigin ? "" : `${scheme}://${subdomain}${location.hostname}:${port}${dirname(location.pathname)}`; + + async_test((t) => { + const client = new XMLHttpRequest(); + client.open("GET", `${base}resources/cors-makeheader.py?get_value=hest_er_best&origin=none&${token()}`); + client.send(); + + client.onload = t.step_func_done(() => { + assert_true(sameorigin, "Cross origin request must be rejected."); + assert_true(client.response.includes("hest_er_best"), "Got response"); + }); + client.onerror = t.step_func_done(() => { + assert_false(sameorigin, "Same origin request must be accepted."); + }); + }, `${desc}, origin: none`); + + async_test((t) => { + const client = new XMLHttpRequest(); + client.open("GET", `${base}resources/cors-makeheader.py?get_value=hest_er_best&${token()}`); + client.send(); + + client.onload = t.step_func_done(() => { + assert_true(client.response.includes("hest_er_best"), "Got response"); + }); + client.onerror = t.unreached_func("Should be accepted"); + }, `${desc}, origin: echo`); +} + +cors("Same domain basic usage"); +cors("Cross domain basic usage", "http", "www1"); +cors("Same domain different port", "http", undefined, PORT); + +cors("Cross domain different port", "http", "www1", PORT); + +cors("Cross domain different protocol", "https", "www1", PORTS); + +cors("Same domain different protocol different port", "https", undefined, PORTS); + +</script> diff --git a/testing/web-platform/tests/cors/client-hint-request-headers-2.tentative.htm b/testing/web-platform/tests/cors/client-hint-request-headers-2.tentative.htm new file mode 100644 index 0000000000..f7ec3dc6d1 --- /dev/null +++ b/testing/web-platform/tests/cors/client-hint-request-headers-2.tentative.htm @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CORS and Client Hints, potentially</title> + +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=support.js?pipe=sub></script> + +<h1>Request headers</h1> +<div id=log></div> +<script> + +test(function() { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print,', false) + client.setRequestHeader('x-print', 'unicorn') + client.setRequestHeader('content-type', 'text/plain') + client.setRequestHeader('accept', 'test') + client.setRequestHeader('accept-language', 'nn') + client.setRequestHeader('content-language', 'nn') + client.setRequestHeader('save-data', 'on') + client.setRequestHeader('device-memory', '1.0') + client.setRequestHeader('dpr', '2.0') + client.setRequestHeader('width', '35') + client.setRequestHeader('viewport-width', '42') + client.setRequestHeader('rtt', '1') + client.setRequestHeader('downlink', '1.0') + client.setRequestHeader('ect', '2g') + client.send(null) + + const res = JSON.parse(client.response) + assert_equals(res['x-print'], 'unicorn') + assert_equals(res['content-type'], 'text/plain') + assert_equals(res['accept'], 'test') + assert_equals(res['accept-language'], 'nn') + assert_equals(res['content-language'], 'nn') + assert_equals(res['save-data'], 'on') + assert_equals(res['device-memory'], '1.0') + assert_equals(res['dpr'], '2.0') + assert_equals(res['width'], '35') + assert_equals(res['viewport-width'], '42') + assert_equals(res['rtt'], '1') + assert_equals(res['downlink'], '1.0') + assert_equals(res['ect'], '2g') +}, 'Client hint headers are simple headers') + +</script> diff --git a/testing/web-platform/tests/cors/client-hint-request-headers.htm b/testing/web-platform/tests/cors/client-hint-request-headers.htm new file mode 100644 index 0000000000..6cf298433e --- /dev/null +++ b/testing/web-platform/tests/cors/client-hint-request-headers.htm @@ -0,0 +1,82 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CORS and Client Hints</title> + +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=support.js?pipe=sub></script> + +<h1>Request headers</h1> +<div id=log></div> +<script> + +test(function() { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false) + client.setRequestHeader('x-print', 'unicorn') + client.setRequestHeader('y-print', 'unicorn') + assert_throws_dom("NetworkError", function() { client.send(null) }) +}, 'Unspecified request headers are disallowed') + +test(function() { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false) + client.setRequestHeader('device-memory', '') + assert_throws_dom("NetworkError", function() { client.send(null) }) +}, 'Unextractable device-memory client hint header is disallowed') + +test(function() { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false) + client.setRequestHeader('dpr', '') + assert_throws_dom("NetworkError", function() { client.send(null) }) +}, 'Unextractable DPR client hint header is disallowed') + +test(function() { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false) + client.setRequestHeader('width', '') + assert_throws_dom("NetworkError", function() { client.send(null) }) +}, 'Unextractable width client hint header is disallowed') + +test(function() { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false) + client.setRequestHeader('viewport-width', '') + assert_throws_dom("NetworkError", function() { client.send(null) }) +}, 'Unextractable viewport-width client hint header is disallowed') + +test(function() { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false) + client.setRequestHeader('rtt', '') + assert_throws_dom("NetworkError", function() { client.send(null) }) + client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false) + client.setRequestHeader('rtt', '-1') + assert_throws_dom("NetworkError", function() { client.send(null) }) +}, 'Test invalid rtt value is disallowed') + +test(function() { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false) + client.setRequestHeader('downlink', '') + assert_throws_dom("NetworkError", function() { client.send(null) }) + client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false) + client.setRequestHeader('downlink', '-1.0') + assert_throws_dom("NetworkError", function() { client.send(null) }) +}, 'Test invalid downlink value is disallowed') + +test(function() { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false) + client.setRequestHeader('ect', '') + assert_throws_dom("NetworkError", function() { client.send(null) }) + client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false) + client.setRequestHeader('ect', '6g') + assert_throws_dom("NetworkError", function() { client.send(null) }) +}, 'Test invalid ect value is disallowed') + +</script> diff --git a/testing/web-platform/tests/cors/cors-safelisted-request-header.any.js b/testing/web-platform/tests/cors/cors-safelisted-request-header.any.js new file mode 100644 index 0000000000..4783c86296 --- /dev/null +++ b/testing/web-platform/tests/cors/cors-safelisted-request-header.any.js @@ -0,0 +1,63 @@ +// META: script=support.js?pipe=sub +// META: script=/common/utils.js + +// This is based on simple-requests.htm, with modifications to make the code more modern and test +// more esoteric cases of header value parsing. + +function safelist(headers, expectPreflight = false) { + promise_test(async t => { + const uuid = token(), + url = CROSSDOMAIN + "resources/preflight.py?token=" + uuid, + checkURL = "resources/preflight.py?check&token=" + uuid, + request = () => fetch(url, { method: "POST", headers, body: "data" }); + if (expectPreflight) { + await promise_rejects_js(t, TypeError, request()); + } else { + const response = await request(); + assert_equals(response.headers.get("content-type"), "text/plain"); + assert_equals(await response.text(), "NO"); + } + const checkResponse = await fetch(checkURL, { method: "POST", body: "data" }); + assert_equals(await checkResponse.text(), (expectPreflight ? "1" : "0")); + }, (expectPreflight ? "Preflight" : "No preflight") + " for " + JSON.stringify(headers)); +} + +[ + ["text /plain", true], + ["text\t/\tplain", true], + ["text/plain;"], + ["text/plain;garbage"], + ["text/plain;garbage\u0001\u0002", true], + ["text/plain,", true], + [",text/plain", true], + ["text/plain,text/plain", true], + ["text/plain,x/x", true], + ["text/plain\u000B", true], + ["text/plain\u000C", true], + ["application/www-form-urlencoded", true], + ["application/x-www-form-urlencoded;\u007F", true], + ["multipart/form-data"], + ["multipart/form-data;\"", true] +].forEach(([mimeType, preflight = false]) => { + safelist({"content-type": mimeType}, preflight); +}); + +[ + ["100-200", true], + ["MB=100-200", true], + ["bytes=100-200"], + ["bytes=100-200hello", true], + ["bytes=abc-def", true], + ["bytes=100-200,300-400", true], + ["bytes=-200", true], + ["bytes=200-"], + ["bytes=200-100", true], + [`bytes=1${'0'.repeat(60)}-2${'0'.repeat(60)}`, true], + ["bytes 100-200", true], + ["bytes = 100-200", true], + ["bytes =100-200", true], + [",bytes=100-200", true], + ["bytes=,100-200", true], +].forEach(([value, preflight = false]) => { + safelist({"range": value}, preflight); +}); diff --git a/testing/web-platform/tests/cors/credentials-flag.htm b/testing/web-platform/tests/cors/credentials-flag.htm new file mode 100644 index 0000000000..45a7143685 --- /dev/null +++ b/testing/web-platform/tests/cors/credentials-flag.htm @@ -0,0 +1,134 @@ +<!DOCTYPE html> +<title>CORS - Access-Control-Allow-Credentials</title> +<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com"> + +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=support.js?pipe=sub></script> + +<h1>CORS - Access-Control-Allow-Credentials</h1> +<div id=log></div> +<script> + +var url = CROSSDOMAIN + 'resources/cors-cookie.py?ident=' + + +/* + * widthCredentials + */ +// XXX Do some https tests here as well + +test(function () { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN, false) + client.withCredentials = true; +}, 'Setting withCredentials on a sync XHR object should not throw') + +async_test(function () { + var id = new Date().getTime() + '_1', + client = new XMLHttpRequest() + client.open("GET", url + id, true) + client.onload = this.step_func(function() { + assert_equals(client.response, "NO_COOKIE") + client.open("GET", url + id, true) + client.onload = this.step_func(function() { + assert_equals(client.response, "NO_COOKIE") + this.done() + }) + client.send(null) + }) + client.send(null) + +}, "Don't send cookie by default"); + +async_test(function () { + var id = new Date().getTime() + '_2', + client = new XMLHttpRequest() + + client.open("GET", url + id, true) + client.withCredentials = true + client.onload = this.step_func(function() { + assert_equals(client.response, "NO_COOKIE", "No cookie in initial request"); + + /* We have cookie, but the browser shouldn't send */ + client.open("GET", url + id, true) + client.withCredentials = false + client.onload = this.step_func(function() { + assert_equals(client.response, "NO_COOKIE", "No cookie after withCredentials=false sync request") + + /* Reads and deletes the cookie */ + client.open("GET", url + id, true) + client.withCredentials = true + client.onload = this.step_func(function() { + assert_equals(client.response, "COOKIE", "Cookie sent in withCredentials=true sync request") + this.done() + }) + client.send(null) + }) + client.send(null) + }) + client.send(null) +}, "Don't send cookie part 2"); + +async_test(function () { + var id = new Date().getTime() + '_3', + client = new XMLHttpRequest() + + /* Shouldn't set the response cookie */ + client.open("GET", url + id, true) + client.withCredentials = false + client.onload = this.step_func(function() { + assert_equals(client.response, "NO_COOKIE", "first"); + + /* Sets the cookie */ + client.open("GET", url + id, true) + client.withCredentials = true + client.onload = this.step_func(function() { + assert_equals(client.response, "NO_COOKIE", "second") + + /* Reads and deletes the cookie */ + client.open("GET", url + id, true) + client.withCredentials = true + client.onload = this.step_func(function() { + assert_equals(client.response, "COOKIE", "third") + this.done() + }) + client.send(null) + }) + client.send(null) + }) + client.send(null) +}, "Don't obey Set-Cookie when withCredentials=false"); + +function test_response_header(allow) { + var resp_test = async_test('Access-Control-Allow-Credentials: ' + allow + ' should be disallowed (async)') + resp_test.step(function() { + var client = new XMLHttpRequest() + client.open('GET', + CROSSDOMAIN + 'resources/cors-makeheader.py?credentials=' + allow, + true) + client.withCredentials = true; + client.onload = resp_test.step_func(function() { + assert_unreached("onload") + }) + client.onerror = resp_test.step_func(function () { + assert_equals(client.readyState, client.DONE, 'readyState') + resp_test.done() + }) + client.send() + }) +} + +test_response_header('TRUE') +test_response_header('True') +test_response_header('"true"') +test_response_header("'true'"); +test_response_header('false') +test_response_header('1') +test_response_header('0') +test_response_header(',true'); +test_response_header('true,'); +test_response_header('true%0B'); +test_response_header('true%0C'); + +</script> diff --git a/testing/web-platform/tests/cors/image-tainting-in-cross-origin-iframe.sub.html b/testing/web-platform/tests/cors/image-tainting-in-cross-origin-iframe.sub.html new file mode 100644 index 0000000000..0cd87756e9 --- /dev/null +++ b/testing/web-platform/tests/cors/image-tainting-in-cross-origin-iframe.sub.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +</body> +<script> +async_test(t => { + const img = document.createElement('img'); + img.onload = t.step_func(() => { + const iframe = document.createElement('iframe'); + window.onmessage = t.step_func_done(e => { + assert_equals(e.data, 'DONE'); + }); + iframe.src = 'http://{{domains[www1]}}:{{ports[http][0]}}/cors/resources/image-tainting-checker.sub.html'; + document.body.appendChild(iframe); + }); + img.src = '/images/blue-png-cachable.py'; + document.body.appendChild(img); +}, 'An image resource that is same-origin to the top-level frame loaded in ' + + 'the frame is not treated as same-origin for an iframe that is ' + + 'cross-origin to the top-level frame, and therefore a canvas where the ' + + 'image is drawn gets tainted.'); +</script> diff --git a/testing/web-platform/tests/cors/late-upload-events.htm b/testing/web-platform/tests/cors/late-upload-events.htm new file mode 100644 index 0000000000..02cd27322b --- /dev/null +++ b/testing/web-platform/tests/cors/late-upload-events.htm @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Adding upload event listeners after send()</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=support.js?pipe=sub></script> + +<h1>Adding upload event listeners after send()</h1> + +<div id=log></div> + +<script> +function doTest(desc, headers) { + async_test("Late listeners: " + desc).step(function() { + var client = new XMLHttpRequest(); + var eventCounter = 0; + client.open("POST", CROSSDOMAIN + "resources/status.py?headers=custom-header"); + + for (var name in headers) { + client.setRequestHeader(name, headers[name]); + } + + client.onreadystatechange = this.step_func(function(e) { + // Irrelevant if request is not finished + if (client.readyState < 4) return; + assert_equals(client.status, 200); + assert_equals(eventCounter, 0, 'Events fired, but should not have'); + this.done(); + }); + client.send((new Array(3000)).join('xo')); + client.upload.onprogress = client.upload.onloadend = client.upload.onloadstart = client.upload.onload = this.step_func(function(e) { + eventCounter++; + assert_unreached("Upload events should not fire, but did: " + e.type); + }); + }); +} + +doTest("No preflight", {}); +doTest("Preflight", {"custom-header":"test"}); +</script> diff --git a/testing/web-platform/tests/cors/origin.htm b/testing/web-platform/tests/cors/origin.htm new file mode 100644 index 0000000000..3d46002933 --- /dev/null +++ b/testing/web-platform/tests/cors/origin.htm @@ -0,0 +1,126 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Access-Control-Allow-Origin handling</title> +<meta name="timeout" content="long"> +<link rel=help href=https://fetch.spec.whatwg.org/> +<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com"> + +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=support.js?pipe=sub></script> + +<h1>Access-Control-Allow-Origin handling</h1> + +<div id=log></div> + +<script> + +/* + * Origin header + */ +function shouldPass(origin) { + test(function () { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + + '/resources/cors-makeheader.py?origin=' + + encodeURIComponent(origin), + false) + client.send() + r = JSON.parse(client.response) + var host = location.protocol + "//" + location.host + assert_equals(r['origin'], host, 'Request Origin: should be ' + host) + }, 'Allow origin: ' + origin.replace(/\t/g, "[tab]").replace(/ /g, '_')); +} + +shouldPass('*'); +shouldPass(' * '); +shouldPass(' *'); +shouldPass(location.protocol + "//" + location.host); +shouldPass(" "+location.protocol + "//" + location.host); +shouldPass(" "+location.protocol + "//" + location.host + " "); +shouldPass(" "+location.protocol + "//" + location.host); + + +function shouldFail(origin) { + test(function () { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + + '/resources/cors-makeheader.py?origin=' + + encodeURIComponent(origin), + false) + assert_throws_dom("NetworkError", function() { client.send() }, 'send') + }, 'Disallow origin: ' + origin.replace(/\0/g, "\\0")); +} + +shouldFail(location.protocol + "//" + SUBDOMAIN + "." + location.host) +shouldFail("//" + location.host) +shouldFail("://" + location.host) +shouldFail("ftp://" + location.host) +shouldFail("http:://" + location.host) +shouldFail("http:/" + location.host) +shouldFail("http:" + location.host) +shouldFail(location.host) +shouldFail(location.protocol + "//" + location.host + "?") +shouldFail(location.protocol + "//" + location.host + "/") +shouldFail(location.protocol + "//" + location.host + " /") +shouldFail(location.protocol + "//" + location.host + "#") +shouldFail(location.protocol + "//" + location.host + "%23") +shouldFail(location.protocol + "//" + location.host + ":80") +shouldFail(location.protocol + "//" + location.host + ", *") +shouldFail(location.protocol + "//" + location.host + "\0") +shouldFail((location.protocol + "//" + location.host).toUpperCase()) +shouldFail(location.protocol.toUpperCase() + "//" + location.host) +shouldFail("-") +shouldFail("**") +shouldFail(",*"); +shouldFail("*,"); +shouldFail("\0*") +shouldFail("\u000B*"); +shouldFail("\u000C*"); +shouldFail("*\0") +shouldFail("*\u000B"); +shouldFail("*\u000C"); +shouldFail("'*'") +shouldFail('"*"') +shouldFail("* *") +shouldFail("* null") +shouldFail("*" + location.protocol + "//" + "*") +shouldFail("*" + location.protocol + "//" + location.host) +shouldFail("* " + location.protocol + "//" + location.host) +shouldFail("*, " + location.protocol + "//" + location.host) +shouldFail("\0" + location.protocol + "//" + location.host) +shouldFail("null " + location.protocol + "//" + location.host) +shouldFail('http://example.net') +shouldFail('null') +shouldFail('null *') +shouldFail('') +shouldFail(location.href) +shouldFail(dirname(location.href)) +shouldFail(CROSSDOMAIN) +shouldFail(location.host.replace(/^[^\.]+\./, "")) +shouldFail("." + location.host.replace(/^[^\.]+\./, "")) +shouldFail("*." + location.host.replace(/^[^\.]+\./, "")) +shouldFail("http://" + location.host.replace(/^[^\.]+\./, "")) +shouldFail("http://." + location.host.replace(/^[^\.]+\./, "")) +shouldFail("http://*." + location.host.replace(/^[^\.]+\./, "")) + +function doubleOrigin(origin, origin2) { + test(function () { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + + '/resources/cors-makeheader.py?origin=' + + encodeURIComponent(origin) + + '&origin2=' + encodeURIComponent(origin2), + false) + assert_throws_dom("NetworkError", function() { client.send() }, 'send') + }, 'Disallow multiple headers (' + origin + ', ' + origin2 + ')'); +} + +doubleOrigin('', '*'); +doubleOrigin('*', ''); +doubleOrigin('*', '*'); +doubleOrigin('', location.protocol + "//" + location.host); +doubleOrigin('*', location.protocol + "//" + location.host); +doubleOrigin(location.protocol + "//" + location.host, location.protocol + "//" + location.host); + +</script> diff --git a/testing/web-platform/tests/cors/preflight-cache-partitioning.sub.window.js b/testing/web-platform/tests/cors/preflight-cache-partitioning.sub.window.js new file mode 100644 index 0000000000..6b37a843c4 --- /dev/null +++ b/testing/web-platform/tests/cors/preflight-cache-partitioning.sub.window.js @@ -0,0 +1,42 @@ +// META: script=/common/utils.js + +const TEST_PAGE = + "http://{{host}}:{{ports[http][0]}}/cors/resources/preflight-cache-partitioning.sub.html"; +const TEST_ANOTHER_PAGE = + "http://{{hosts[alt][]}}:{{ports[http][0]}}/cors/resources/preflight-cache-partitioning.sub.html"; + +promise_test(async t => { + let uuid_token = token(); + + const TEST_PAGES = [TEST_PAGE, TEST_ANOTHER_PAGE]; + + // We will load the same page with different top-level origins to check if the + // CORS preflight cache is partitioned. The page will load the iframe with one + // origin and trigger the CORS preflight through fetching a cross-origin + // resources in the iframe. + + for (let test_page of TEST_PAGES) { + let win; + + await new Promise(resolve => { + window.onmessage = (e) => { + if (e.data.type === "loaded") { + resolve(); + } + }; + + win = window.open(test_page); + }); + + await new Promise(resolve => { + win.postMessage({ type: "run", token: uuid_token }, "*"); + + window.onmessage = (e) => { + assert_equals(e.data.type, "pass", e.data.msg); + resolve(); + }; + }); + + win.close(); + } +}, "The preflight cache should be partitioned"); diff --git a/testing/web-platform/tests/cors/preflight-cache.htm b/testing/web-platform/tests/cors/preflight-cache.htm new file mode 100644 index 0000000000..b3de663ebc --- /dev/null +++ b/testing/web-platform/tests/cors/preflight-cache.htm @@ -0,0 +1,150 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CORS - preflight cache</title> +<meta name="timeout" content="long"> +<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com"> + +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=/common/utils.js></script> +<script src=support.js?pipe=sub></script> + +<h1>Preflight cache</h1> + +<div id=log></div> +<script> + +/* + * Cache + */ + +function did_preflight(expect, client, settings) { + var uuid_token = (settings && settings.token) || token(); + if(!settings) + settings = {} + + set = { + method: 'method' in settings ? settings.method : 'GET', + extra: 'extra' in settings ? '&' + settings.extra : '' + } + + client.open(set.method, + CROSSDOMAIN + 'resources/preflight.py?token=' + uuid_token + set.extra, + false) + client.setRequestHeader('x-print', uuid_token) + client.send() + + client.open('GET', 'resources/preflight.py?check&token=' + uuid_token, false) + client.send() + assert_equals(client.response, expect === true ? '1' : '0', "did preflight") + return uuid_token; +} + +/* + * Should run preflight + */ + +test(function() { + var time = new Date().getTime() + var client = new XMLHttpRequest() + did_preflight(true, client); +}, +'Test preflight') + +test(function() { + var time = new Date().getTime() + var client = new XMLHttpRequest() + + var id = did_preflight(true, client) + did_preflight(false, client, {token: id}) +}, +'preflight for x-print should be cached') + +test(function() { + var time = new Date().getTime() + var client = new XMLHttpRequest() + + var id = did_preflight(true, client, {extra:'max_age='}) + did_preflight(false, client, {extra:'max_age=', token: id}) +}, +'age = blank, should be cached') + +test(function() { + var time = new Date().getTime() + var client = new XMLHttpRequest() + + var id = did_preflight(true, client, {extra:'max_age=0'}) + did_preflight(true, client, {extra:'max_age=0', token: id}) +}, +'age = 0, should not be cached') + +test(function() { + var time = new Date().getTime() + var client = new XMLHttpRequest() + + var id = did_preflight(true, client, {extra:'max_age=-1'}) + did_preflight(true, client, {extra:'max_age=-1', token: id}) +}, +'age = -1, should not be cached'); + +(function() { + var test = async_test("preflight first request, second from cache, wait, third should preflight again"), + time = new Date().getTime(), + dothing = function (url, msg, set_request, func) { + client = new XMLHttpRequest(), + client.open('GET', url, true) + if (set_request) + client.setRequestHeader('x-print', msg) + client.onload = test.step_func(function() { + assert_equals(client.response, msg, "response " + url) + if (func) + test.step(func) + }) + client.onerror = test.step_func(function(e) { + assert_unreached("Got unexpected error event on the XHR object") + }) + client.send() + } + + var token1 = token(); + test.step(function() { + /* First cycle, gets x-print into the cache, with timeout 1 */ + var request_url = CROSSDOMAIN + 'resources/preflight.py?max_age=1&token=' + token1; + dothing(request_url, + 'first', true, function() { + test = test; + + /* Check if we did a preflight like we expected */ + dothing('resources/preflight.py?check&1&token=' + token1, + '1', false, function() { + test = test; + dothing(request_url, + 'second', true, function() { + test = test; + + /* Check that we didn't do a preflight (hasn't gone 1 second yet) */ + dothing('resources/preflight.py?check&2&token=' + token1, + '0', false, function() { + test = test; + + /* Wait until the preflight cache age is old (and thus cleared) */ + test.step_timeout(() => { + dothing(request_url, + 'third', true, function() { + test = test; + + /* Expect that we did indeed do a preflight */ + dothing('resources/preflight.py?check&3&token=' + token1, + '1', false, function() { + test.done() + }) + }) + }, 1500) + }) + }) + }) + }) + }) +})(); + +</script> diff --git a/testing/web-platform/tests/cors/preflight-failure.htm b/testing/web-platform/tests/cors/preflight-failure.htm new file mode 100644 index 0000000000..fd34d55b79 --- /dev/null +++ b/testing/web-platform/tests/cors/preflight-failure.htm @@ -0,0 +1,62 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CORS - Preflight responds with non-2XX status code</title> +<meta name=author title="Fernando Jiménez Moreno" href="mailto:ferjmoreno@gmail.com"> + +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=/common/get-host-info.sub.js></script> + +<h1>Preflight responds with non-2XX status code</h1> + +<div id=log></div> +<script> + +// Request count for cache busting and easy identifying of request in traffic +// analyzer. +var req_c = 0; + +var CROSSDOMAIN_URL = get_host_info().HTTP_REMOTE_ORIGIN + '/cors/resources/cors-makeheader.py?'; + +/* + * Redirection with preflights. + */ +function preflight_failure(code) { + var isCodeOK = code >= 200 && code <= 299, + descOK = isCodeOK ? 'succeed' : 'throw error', + desc = 'Should ' + descOK + ' if preflight has status ' + code; + async_test(desc).step(function() { + var client = new XMLHttpRequest(); + var redirect = + encodeURIComponent(CROSSDOMAIN_URL + 'headers=x-test&' + req_c++); + + client.open('GET', + CROSSDOMAIN_URL + 'headers=x-test&location=' + redirect + + '&code=' + code + '&preflight=' + code + + '&' + req_c++, + true /* async */); + client.setRequestHeader('x-test', 'test'); + client.onerror = this.step_func(function() { + assert_false(isCodeOK); + this.done(); + }); + client.onreadystatechange = this.step_func(function() { + if (!isCodeOK) + assert_equals(client.status, 0); + }); + client.onload = this.step_func(function() { + assert_true(isCodeOK); + this.done(); + }); + client.send(null); + }); +} +[200, 299, + 300, 301, 302, 303, 304, 305, 306, 307, 308, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, + 500, 501, 502, 503, 504, 505, + 680, + 790 +].forEach(preflight_failure); + +</script> diff --git a/testing/web-platform/tests/cors/redirect-origin.htm b/testing/web-platform/tests/cors/redirect-origin.htm new file mode 100644 index 0000000000..5463292fa5 --- /dev/null +++ b/testing/web-platform/tests/cors/redirect-origin.htm @@ -0,0 +1,195 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CORS - redirect</title> +<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com"> + +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=support.js?pipe=sub></script> + +<h1>CORS redirect handling</h1> + +<div id=log></div> + +<script> + + // Test count for cache busting and easy identifying of request in traffic analyzer + var num_test = 0, + + origin = location.protocol + "//" + location.host, + remote_origin = origin.replace('://', '://' + SUBDOMAIN + '.'), + + local = dirname(location.href) + 'resources/cors-makeheader.py', + remote = local.replace('://', '://' + SUBDOMAIN + '.'), + remote2 = local.replace('://', '://' + SUBDOMAIN2 + '.'); + + + /* First page Redirect to Expect what */ + + // local -> remote + + redir_test([ 'local', '*' ], [ 'remote', '*' ], origin ); + redir_test([ 'local', '*' ], [ 'remote', origin ], origin ); + redir_test([ 'local', '*' ], [ 'remote', 'null' ], 'disallow'); + redir_test([ 'local', '*' ], [ 'remote', 'none' ], 'disallow'); + + redir_test([ 'local', origin ], [ 'remote', '*' ], origin ); + redir_test([ 'local', origin ], [ 'remote', origin ], origin ); + redir_test([ 'local', origin ], [ 'remote', 'null' ], 'disallow'); + redir_test([ 'local', origin ], [ 'remote', 'none' ], 'disallow'); + + redir_test([ 'local', 'null' ], [ 'remote', '*' ], origin ); + redir_test([ 'local', 'none' ], [ 'remote', '*' ], origin ); + + + // remote -> local + + redir_test([ 'remote', '*' ], [ 'local', '*' ], 'null' ); + redir_test([ 'remote', '*' ], [ 'local', origin ], 'disallow'); + redir_test([ 'remote', '*' ], [ 'local', 'null' ], 'null' ); + redir_test([ 'remote', '*' ], [ 'local', 'none' ], 'disallow'); + + redir_test([ 'remote', origin ], [ 'local', '*' ], 'null' ); + redir_test([ 'remote', origin ], [ 'local', origin ], 'disallow'); + redir_test([ 'remote', origin ], [ 'local', 'null' ], 'null' ); + redir_test([ 'remote', origin ], [ 'local', 'none' ], 'disallow'); + + redir_test([ 'remote', 'null' ], [ 'local', '*' ], 'disallow'); + redir_test([ 'remote', 'none' ], [ 'local', '*' ], 'disallow'); + + + // remote -> remote + + redir_test([ 'remote', '*' ], [ 'remote', '*' ], origin ); + redir_test([ 'remote', '*' ], [ 'remote', origin ], origin ); + redir_test([ 'remote', '*' ], [ 'remote', 'null' ], 'disallow'); + redir_test([ 'remote', '*' ], [ 'remote', 'none' ], 'disallow'); + + redir_test([ 'remote', origin ], [ 'remote', '*' ], origin ); + redir_test([ 'remote', origin ], [ 'remote', origin ], origin ); + redir_test([ 'remote', origin ], [ 'remote', 'null' ], 'disallow'); + redir_test([ 'remote', origin ], [ 'remote', 'none' ], 'disallow'); + + redir_test([ 'remote', 'null' ], [ 'remote', '*' ], 'disallow'); + redir_test([ 'remote', 'none' ], [ 'remote', '*' ], 'disallow'); + + + // remote -> remote2 + + redir_test([ 'remote', '*' ], [ 'remote2', '*' ], 'null' ); + redir_test([ 'remote', '*' ], [ 'remote2', origin ], 'disallow'); + redir_test([ 'remote', '*' ], [ 'remote2', 'null' ], 'null' ); + redir_test([ 'remote', '*' ], [ 'remote2', 'none' ], 'disallow'); + + redir_test([ 'remote', origin ], [ 'remote2', '*' ], 'null' ); + redir_test([ 'remote', origin ], [ 'remote2', origin ], 'disallow'); + redir_test([ 'remote', origin ], [ 'remote2', 'null' ], 'null'); + redir_test([ 'remote', origin ], [ 'remote2', 'none' ], 'disallow'); + + redir_test([ 'remote', 'null' ], [ 'remote2', '*' ], 'disallow'); + redir_test([ 'remote', 'none' ], [ 'remote2', '*' ], 'disallow'); + + + // Bonus weird edge checks + + redir_test([ 'remote', '*' ], [ 'remote', remote_origin ], 'disallow'); + redir_test([ 'remote', '*' ], [ 'remote2', remote_origin ], 'disallow'); + redir_test([ 'remote', remote_origin ], [ 'remote', "*" ], 'disallow'); + + + + /* + * The helpers + */ + + function redir_test(first, second, expect_origin) { + var first_url, second_url, + urls = { "remote": remote, "local": local, "remote2": remote2 }; + + first_url = urls[first[0]] + "?origin=" + first[1]; + second_url = urls[second[0]] + "?origin=" + second[1]; + + if (expect_origin=="disallow") { + shouldFail(first[0]+" ("+first[1]+") to " + + second[0]+" ("+second[1]+"), expect to fail", [ first_url, second_url ]); + } + else { + shouldPass(first[0]+" ("+first[1]+") to " + + second[0]+" ("+second[1]+"), expect origin="+expect_origin, expect_origin, [ first_url, second_url ]); + } + + } + + function shouldPass(desc, expected_origin, urls) { + var test_id = num_test, + t = async_test(desc); + + num_test++; + + t.step(function() { + var final_url, + client = new XMLHttpRequest(); + + client.open('GET', buildURL(urls, test_id)); + + client.onreadystatechange = t.step_func(function() { + if (client.readyState != client.DONE) + return; + assert_true(!!client.response, "Got response"); + r = JSON.parse(client.response) + assert_equals(r['origin'], expected_origin, 'Origin Header') + assert_equals(r['get_value'], 'last', 'get_value') + t.done(); + }); + client.send(null) + }); + } + + function shouldFail(desc, urls) { + var test_id = num_test, + t = async_test(desc); + + num_test++; + + t.step(function() { + var client = new XMLHttpRequest(); + + client.open('GET', buildURL(urls, test_id)); + + client.onreadystatechange = t.step_func(function() { + if (client.readyState != client.DONE) + return; + assert_false(!!client.response, "Got response"); + }); + client.onerror = t.step_func(function(e) { + t.done(); + }); + + client.send(null) + }); + } + + + function buildURL(urls, id) { + var tmp_url; + + if (typeof(urls) == "string") { + return urls + "&" + id + "_0"; + } + + for (var i = urls.length; i--; ) { + if (!tmp_url) + { + tmp_url = urls[i] + "&get_value=last&" + id + "_" + i; + continue; + } + tmp_url = urls[i] + + "&location=" + + encodeURIComponent(tmp_url) + + "&" + id + "_" + i; + } + + return tmp_url; + } + +</script> diff --git a/testing/web-platform/tests/cors/redirect-preflight-2.htm b/testing/web-platform/tests/cors/redirect-preflight-2.htm new file mode 100644 index 0000000000..fe58d90a26 --- /dev/null +++ b/testing/web-platform/tests/cors/redirect-preflight-2.htm @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CORS - preflight after a redirect</title> +<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com"> + +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=support.js?pipe=sub></script> +<script src=/common/utils.js></script> + +<h1>Preflight after redirect</h1> + +<div id=log></div> +<script> + +async_test(function() { + var test_id = "fail_" + new Date().getTime() + var client = new XMLHttpRequest() + var last_url = CROSSDOMAIN + 'resources/cors-makeheader.py?origin=*&ident=' + test_id + + client.open('GET', 'resources/cors-makeheader.py?origin=*&location=' + encodeURIComponent(last_url)) + client.setRequestHeader('custom-header', 'admin') + client.onerror = this.step_func(function() { + this.done() + }) + client.onload = this.step_func(function(e) { assert_unreached("Request should not succeed!") }) + client.send() +}, "Same-origin custom-header request, redirect to cross-origin fails after doing a non-successful preflight") + + +async_test(function() { + var client = new XMLHttpRequest() + var uuid_token = token(); + var last_url = CROSSDOMAIN + 'resources/cors-makeheader.py?headers=custom-header&origin=*&token=' + uuid_token; + + client.open('GET', 'resources/cors-makeheader.py?origin=*&location=' + encodeURIComponent(last_url)) + client.setRequestHeader('custom-header', 'admin') + client.onload = this.step_func(function() { + // Test that I got custom-header + + /* To check whether we did a preflight */ + client.open('GET', 'resources/cors-makeheader.py?check&token=' + uuid_token) + client.onload = this.step_func(function() { + assert_equals(client.response, "1", "did preflight") + this.done() + }) + client.onerror = this.step_func(function(e) { assert_unreached("Error on getting preflight data") }) + client.send() + }) + client.onerror = this.step_func(function(e) { assert_unreached("Error during request", e) }) + client.send() +}, "Same-origin custom-header request, redirect to cross-origin succeeds after doing a preflight") + + +</script> diff --git a/testing/web-platform/tests/cors/redirect-preflight.htm b/testing/web-platform/tests/cors/redirect-preflight.htm new file mode 100644 index 0000000000..ff64284e90 --- /dev/null +++ b/testing/web-platform/tests/cors/redirect-preflight.htm @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CORS - redirect with preflight</title> +<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com"> +<meta name=author title="Fernando Jiménez Moreno" href="mailto:ferjmoreno@gmail.com"> + +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=support.js?pipe=sub></script> + +<h1>Redirect with preflight</h1> + +<div id=log></div> +<script> + +// Request count for cache busting and easy identifying of request in traffic +// analyzer. +var req_c = 0; + +var CROSSDOMAIN_URL = CROSSDOMAIN + 'resources/cors-makeheader.py?'; + +/* + * Redirection after successfull (200) preflight. + */ +function redir_after_successfull_preflight(code) { + var desc = 'Should allow redirect ' + code + ' after succesful (200) preflight'; + async_test(desc).step(function() { + var client = new XMLHttpRequest(); + var redirect = encodeURIComponent( + CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-test&' + req_c++ + ); + + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?' + + 'preflight=200&headers=x-test&location=' + + redirect + '&code=' + code + '&' + req_c++, + true /* async */); + client.setRequestHeader('x-test', 'test'); + client.onreadystatechange = this.step_func(function() { + assert_equals(client.status, 200, 'Successfull redirect'); + this.done(); + }); + client.send(null); + }); +} +[301, 302, 303, 307, 308].forEach(redir_after_successfull_preflight); + +</script> diff --git a/testing/web-platform/tests/cors/redirect-userinfo.htm b/testing/web-platform/tests/cors/redirect-userinfo.htm new file mode 100644 index 0000000000..fd3864d249 --- /dev/null +++ b/testing/web-platform/tests/cors/redirect-userinfo.htm @@ -0,0 +1,99 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CORS - redirect with userinfo</title> +<meta name=author title="Odin Hørthe Omdal" href="mailto:odinho@opera.com"> + +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=support.js?pipe=sub></script> + +<h1>CORS userinfo redirect handling</h1> + +<div id=log></div> + +<script> + + // Test count for cache busting and easy identifying of request in traffic analyzer + var num_test = 0 + + shouldFail("Disallow redirect with userinfo (user:pass@)", [ + CROSSDOMAIN + "resources/cors-makeheader.py?", + CROSSDOMAIN.replace("http://", "http://test:test@") + "resources/cors-makeheader.py?"]); + + shouldFail("Disallow redirect with userinfo (user:@)", [ + CROSSDOMAIN + "resources/cors-makeheader.py?", + CROSSDOMAIN.replace("http://", "http://user:@") + "resources/cors-makeheader.py?"]); + + shouldFail("Disallow redirect with userinfo (user@)", [ + CROSSDOMAIN + "resources/cors-makeheader.py?", + CROSSDOMAIN.replace("http://", "http://user:@") + "resources/cors-makeheader.py?"]); + + shouldPass("Allow redirect without userinfo (:@ is trimmed during URL parsing)", [ + CROSSDOMAIN + "resources/cors-makeheader.py?", + CROSSDOMAIN.replace("http://", "http://:@") + "resources/cors-makeheader.py?"]); + + shouldFail("Disallow redirect with userinfo (:pass@)", [ + CROSSDOMAIN + "resources/cors-makeheader.py?", + CROSSDOMAIN.replace("http://", "http://:pass@") + "resources/cors-makeheader.py?"]); + + shouldPass("Allow redirect without userinfo (@ is trimmed during URL parsing)", [ + CROSSDOMAIN + "resources/cors-makeheader.py?", + CROSSDOMAIN.replace("http://", "http://@") + "resources/cors-makeheader.py?"]); + + function shouldFail(desc, urls) { + var test_id = num_test, + t = async_test(desc); + + num_test++; + + t.step(function() { + var client = new XMLHttpRequest(); + + client.open('GET', buildURL(urls, test_id)); + + client.onload = t.unreached_func(); + client.onerror = t.step_func_done(); + + client.send(null) + }); + } + + function shouldPass(desc, urls) { + var test_id = num_test, + t = async_test(desc); + + num_test++; + + t.step(function() { + var client = new XMLHttpRequest(); + + client.open('GET', buildURL(urls, test_id)); + + client.onload = t.step_func_done(function() { + r = JSON.parse(client.response) + assert_equals(r['get_value'], 'last', 'get_value') + }); + client.onerror = t.unreached_func() + client.send(null) + }); + } + + function buildURL(urls, id) { + var tmp_url; + + for (var i = urls.length; i--; ) { + if (!tmp_url) + { + tmp_url = urls[i] + "&get_value=last&" + id + "_" + i; + continue; + } + tmp_url = urls[i] + + "&location=" + + encodeURIComponent(tmp_url) + + "&" + id + "_" + i; + } + + return tmp_url; + } + +</script> diff --git a/testing/web-platform/tests/cors/remote-origin.htm b/testing/web-platform/tests/cors/remote-origin.htm new file mode 100644 index 0000000000..0726775169 --- /dev/null +++ b/testing/web-platform/tests/cors/remote-origin.htm @@ -0,0 +1,121 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Access-Control-Allow-Origin handling</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=support.js?pipe=sub></script> + +<h1>Access-Control-Allow-Origin handling</h1> + +<div id=log></div> + +<script> + +var remote_tests = []; +var iframe = document.createElement("iframe") +iframe.src = CROSSDOMAIN + 'resources/remote-xhrer.html'; +document.body.appendChild(iframe); + +function reverseOrigin(expect_pass, origin) +{ + var real_origin = origin.replace("<host>", REMOTE_HOST) + .replace("<remote_origin>", location.protocol + "//" + location.host) + .replace("<origin>", REMOTE_ORIGIN) + .replace("<protocol>", REMOTE_PROTOCOL) + .replace("<HOST>", REMOTE_HOST.toUpperCase()) + .replace("<ORIGIN>", REMOTE_ORIGIN.toUpperCase()) + .replace("<PROTOCOL>", REMOTE_PROTOCOL.toUpperCase()); + + var t = async_test((expect_pass ? 'Allow origin: ' : 'Disallow origin: ') + real_origin + .replace(/\0/g, "\\0") + .replace(/\t/g, "[tab]") + .replace(/ /g, '_')); + t.step(function() { + this.test_url = dirname(location.href) + + 'resources/cors-makeheader.py?origin=' + + encodeURIComponent(real_origin); + iframe.contentWindow.postMessage({ url: this.test_url, origin: origin }, "*"); + }); + + if (expect_pass) + { + t.callback = t.step_func(function(e) { + assert_equals(e.state, "load"); + r = JSON.parse(e.response) + assert_equals(r['origin'], REMOTE_ORIGIN, 'Request Origin: should be ' + REMOTE_ORIGIN) + this.done(); + }); + } + else + { + t.callback = t.step_func(function(e) { + assert_equals(e.state, "error"); + assert_equals(e.response, ""); + this.done(); + }); + } + + remote_tests[origin] = t; +} + +function shouldPass(origin) { reverseOrigin(true, origin); } +function shouldFail(origin) { reverseOrigin(false, origin); } + + +iframe.onload = function() { + shouldPass('*'); + shouldPass(' * '); + shouldPass(' *'); + shouldPass("<origin>"); + shouldPass(" <origin>"); + shouldPass(" <origin> "); + shouldPass(" <origin>"); + + shouldFail("<remote_origin>") + shouldFail("//" + "<host>") + shouldFail("://" + "<host>") + shouldFail("ftp://" + "<host>") + shouldFail("http:://" + "<host>") + shouldFail("http:/" + "<host>") + shouldFail("http:" + "<host>") + shouldFail("<host>") + shouldFail("<origin>" + "?") + shouldFail("<origin>" + "/") + shouldFail("<origin>" + " /") + shouldFail("<origin>" + "#") + shouldFail("<origin>" + "%23") + shouldFail("<origin>" + ":80") + shouldFail("<origin>" + ", *") + shouldFail("<origin>" + "\0") + shouldFail(("<ORIGIN>")) + shouldFail("<PROTOCOL>//<host>") + shouldFail("<protocol>//<HOST>") + shouldFail("-") + shouldFail("**") + shouldFail("\0*") + shouldFail("*\0") + shouldFail("'*'") + shouldFail('"*"') + shouldFail("* *") + shouldFail("*" + "<protocol>" + "//" + "*") + shouldFail("*" + "<origin>") + shouldFail("* " + "<origin>") + shouldFail("*, " + "<origin>") + shouldFail("\0" + "<origin>") + shouldFail("null " + "<origin>") + shouldFail('http://example.net') + shouldFail('null') + shouldFail('') + shouldFail(location.href) + shouldFail(dirname(location.href)) + shouldFail(CROSSDOMAIN) +} + +window.addEventListener("message", function(e) { + remote_tests[e.data.origin].callback(e.data); +}); + +add_completion_callback(function() { + iframe.parentElement.removeChild(iframe); +}); +</script> diff --git a/testing/web-platform/tests/cors/request-headers.htm b/testing/web-platform/tests/cors/request-headers.htm new file mode 100644 index 0000000000..7634eca9fc --- /dev/null +++ b/testing/web-platform/tests/cors/request-headers.htm @@ -0,0 +1,80 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CORS - request headers - Access-Control-Allow-Headers</title> +<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com"> + +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=support.js?pipe=sub></script> + +<h1>Request headers</h1> +<div id=log></div> +<script> + +/* + * Request Headers + */ + +test(function() { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false) + client.setRequestHeader('x-print', 'unicorn') + client.send(null) + + res = JSON.parse(client.response) + assert_equals(res['x-print'], 'unicorn') +}, 'basic request header') + +test(function() { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print,', false) + client.setRequestHeader('x-print', 'unicorn') + client.setRequestHeader('content-type', 'text/plain') + client.setRequestHeader('accept', 'test') + client.setRequestHeader('accept-language', 'nn') + client.setRequestHeader('content-language', 'nn') + client.send(null) + + res = JSON.parse(client.response) + assert_equals(res['x-print'], 'unicorn') + assert_equals(res['content-type'], 'text/plain') + assert_equals(res['accept'], 'test') + assert_equals(res['accept-language'], 'nn') + assert_equals(res['content-language'], 'nn') +}, 'Simple request headers need not be in allow-headers') + +test(function() { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false) + client.setRequestHeader('x-print', 'unicorn') + client.setRequestHeader('y-print', 'unicorn') + assert_throws_dom("NetworkError", function() { client.send(null) }) +}, 'Unspecified request headers are disallowed') + +test(function() { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=,y-lol,x-PriNT,%20,,,Y-PRINT', false) + client.setRequestHeader('x-print', 'unicorn') + client.setRequestHeader('y-print', 'narwhal') + client.send(null) + + res = JSON.parse(client.response) + assert_equals(res['x-print'], 'unicorn') + assert_equals(res['y-print'], 'narwhal') +}, 'Strange allowheaders (case insensitive)') + +test(function() { + var client = new XMLHttpRequest() + assert_throws_dom('INVALID_STATE_ERR', function() { client.setRequestHeader('x-print', 'unicorn') }) +}, +'INVALID_STATE_ERR on setRequestHeader before open()') + +test(function() { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=,y-lol,x-PriNT,%20,,,Y-PRINT', false) + client.send() + assert_throws_dom('INVALID_STATE_ERR', function() { client.setRequestHeader('x-print', 'unicorn') }) +}, +'INVALID_STATE_ERR on setRequestHeader after send()') + +</script> diff --git a/testing/web-platform/tests/cors/resources/304.py b/testing/web-platform/tests/cors/resources/304.py new file mode 100644 index 0000000000..aa5f5ab560 --- /dev/null +++ b/testing/web-platform/tests/cors/resources/304.py @@ -0,0 +1,60 @@ +# A header used to correlate requests and responses +state_header = b"content-language" + +# Static ETag to use (and expect) +etag = b"abcdef" + +def error(msg): + return (299, u"Client Error"), [ + (b'content-type', b'text/plain'), + (b'access-control-allow-origin', b"*"), + (b'access-control-expose-headers', state_header), + (b'cache-control', b'no-store') + ], msg + +def main(request, response): + headers = [] + + inm = request.headers.get(b'if-none-match', None) + raw_req_num = request.headers.get(state_header, None) + if raw_req_num == None: + return error(u"no req_num header in request") + else: + req_num = int(raw_req_num) + if req_num > 8: + return error(u"req_num %s out of range" % req_num) + + headers.append((b"Access-Control-Expose-Headers", state_header)) + headers.append((state_header, req_num)) + headers.append((b"A", req_num)) + headers.append((b"B", req_num)) + + if req_num % 2: # odd requests are the first in a test pair + if inm: + # what are you doing here? This should be a fresh request. + return error(u"If-None-Match on first request") + else: + status = 200, b"OK" + headers.append((b"Access-Control-Allow-Origin", b"*")) + headers.append((b"Content-Type", b"text/plain")) + headers.append((b"Cache-Control", b"private, max-age=3, must-revalidate")) + headers.append((b"ETag", etag)) + return status, headers, b"Success" + else: # even requests are the second in a pair, and should have a good INM. + if inm != etag: + # Bad browser. + if inm == None: + return error(u"If-None-Match missing") + else: + return error(u"If-None-Match '%s' mismatches") + else: + if req_num == 2: + pass # basic, vanilla check + elif req_num == 4: + headers.append((b"Access-Control-Expose-Headers", b"a, b")) + elif req_num == 6: + headers.append((b"Access-Control-Expose-Headers", b"a")) + elif req_num == 8: + headers.append((b"Access-Control-Allow-Origin", b"other.origin.example:80")) + status = 304, b"Not Modified" + return status, headers, b"" diff --git a/testing/web-platform/tests/cors/resources/access-control-expose-headers.json b/testing/web-platform/tests/cors/resources/access-control-expose-headers.json new file mode 100644 index 0000000000..e8915f7ffe --- /dev/null +++ b/testing/web-platform/tests/cors/resources/access-control-expose-headers.json @@ -0,0 +1,62 @@ +[ + { + "input": "access-control-expose-headers: BB-8", + "exposed": true + }, + { + "input": "Access-Control-Expose-Headers: bb-8,,@#$#%%&^&^*()()11!", + "exposed": false + }, + { + "input": "Access-Control-Expose-Headers: bb-8, no no", + "exposed": false + }, + { + "input": "Access-Control-Expose-Headers: @#$#%%&^&^*()()11!,bb-8", + "exposed": false + }, + { + "input": "Access-Control-Expose-Headers: bb-8\r\nAccess-Control-Expose-Headers: no", + "exposed": true + }, + { + "input": "Access-Control-Expose-Headers: bb-8\r\nAccess-Control-Expose-Headers: no no", + "exposed": false + }, + { + "input": "Access-Control-Expose-Headers: no\r\nAccess-Control-Expose-Headers: bb-8", + "exposed": true + }, + { + "input": "Access-Control-Expose-Headers:\r\nAccess-Control-Expose-Headers: bb-8", + "exposed": true + }, + { + "input": "Access-Control-Expose-Headers: ,bb-8", + "exposed": true + }, + { + "input": "Access-Control-Expose-Headers: bb-8\u000C", + "exposed": false + }, + { + "input": "Access-Control-Expose-Headers: bb-8\u000B", + "exposed": false + }, + { + "input": "Access-Control-Expose-Headers: bb-8\u000B,bb-8", + "exposed": false + }, + { + "input": "Access-Control-Expose-Headers: 'bb-8'", + "exposed": false + }, + { + "input": "Access-Control-Expose-Headers: 'bb-8',bb-8", + "exposed": true + }, + { + "input": "Access-Control-Expose-Headers: \"bb-8\",bb-8", + "exposed": false + } +] diff --git a/testing/web-platform/tests/cors/resources/cache-304.py b/testing/web-platform/tests/cors/resources/cache-304.py new file mode 100644 index 0000000000..0626091794 --- /dev/null +++ b/testing/web-platform/tests/cors/resources/cache-304.py @@ -0,0 +1,10 @@ +def main(request, response): + match = request.headers.get(b"If-None-Match", None) + if match is not None and match == b"mybestscript-v1": + response.status = (304, b"YEP") + return b"" + response.headers.set(b"Access-Control-Allow-Origin", b"*") + response.headers.set(b"Cache-Control", b"must-revalidate") + response.headers.set(b"ETag", b"mybestscript-v1") + response.headers.set(b"Content-Type", b"text/javascript") + return b"function hep() { }" diff --git a/testing/web-platform/tests/cors/resources/checkandremove.py b/testing/web-platform/tests/cors/resources/checkandremove.py new file mode 100644 index 0000000000..5cd7868199 --- /dev/null +++ b/testing/web-platform/tests/cors/resources/checkandremove.py @@ -0,0 +1,6 @@ +def main(request, response): + token = request.GET.first(b"token") + if request.server.stash.remove(token) is not None: + return u"1" + else: + return u"0" diff --git a/testing/web-platform/tests/cors/resources/cors-cookie.py b/testing/web-platform/tests/cors/resources/cors-cookie.py new file mode 100644 index 0000000000..ee42494ad0 --- /dev/null +++ b/testing/web-platform/tests/cors/resources/cors-cookie.py @@ -0,0 +1,21 @@ + +def main(request, response): + origin = request.GET.first(b"origin", request.headers[b"origin"]) + credentials = request.GET.first(b"credentials", b"true") + + headers = [(b"Content-Type", b"text/plain")] + if origin != b'none': + headers.append((b"Access-Control-Allow-Origin", origin)) + if credentials != b'none': + headers.append((b"Access-Control-Allow-Credentials", credentials)) + + ident = request.GET.first(b'ident', b'test') + + if ident in request.cookies: + body = request.cookies[ident].value + response.delete_cookie(ident) + else: + response.set_cookie(ident, b"COOKIE") + body = u"NO_COOKIE" + + return headers, body diff --git a/testing/web-platform/tests/cors/resources/cors-headers.asis b/testing/web-platform/tests/cors/resources/cors-headers.asis new file mode 100644 index 0000000000..2e92da28a0 --- /dev/null +++ b/testing/web-platform/tests/cors/resources/cors-headers.asis @@ -0,0 +1,25 @@ +HTTP/1.1 200 OK
+Access-Control-Allow-Origin: *
+Access-Control-Expose-Headers: X-Custom-Header, X-Custom-Header-Empty, X-Custom-Header-Comma, X-Custom-Header-Bytes
+Access-Control-Expose-Headers: X-Second-Expose
+Access-Control-Expose-Headers: Date
+Content-Type: text/plain
+Content-Length: 4 +X-Custom-Header: test
+X-Custom-Header: test
+Set-Cookie: test1=t1;max-age=2
+Set-Cookie2: test2=t2;Max-Age=2
+X-Custom-Header-Empty:
+X-Custom-Header-Comma: 1
+X-Custom-Header-Comma: 2
+X-Custom-Header-Bytes: …
+X-Nonexposed: unicorn
+X-Second-Expose: flyingpig
+Cache-Control: no-cache
+Content-Language: nn
+Expires: Thu, 01 Dec 1994 16:00:00 GMT
+Last-Modified: Thu, 01 Dec 1994 10:00:00 GMT
+Pragma: no-cache
+Date: Wed, 22 Oct 2013 10:00:00 GMT
+
+TEST diff --git a/testing/web-platform/tests/cors/resources/cors-makeheader.py b/testing/web-platform/tests/cors/resources/cors-makeheader.py new file mode 100644 index 0000000000..e0576a003d --- /dev/null +++ b/testing/web-platform/tests/cors/resources/cors-makeheader.py @@ -0,0 +1,69 @@ +import json + +from wptserve.utils import isomorphic_decode + +def main(request, response): + origin = request.GET.first(b"origin", request.headers.get(b'origin') or b'none') + + if b"check" in request.GET: + token = request.GET.first(b"token") + value = request.server.stash.take(token) + if value is not None: + if request.GET.first(b"check", None) == b"keep": + request.server.stash.put(token, value) + body = u"1" + else: + body = u"0" + return [(b"Content-Type", b"text/plain")], body + + + if origin != b'none': + response.headers.set(b"Access-Control-Allow-Origin", origin) + if b'origin2' in request.GET: + response.headers.append(b"Access-Control-Allow-Origin", request.GET.first(b'origin2')) + + #Preflight + if b'headers' in request.GET: + response.headers.set(b"Access-Control-Allow-Headers", request.GET.first(b'headers')) + if b'credentials' in request.GET: + response.headers.set(b"Access-Control-Allow-Credentials", request.GET.first(b'credentials')) + if b'methods' in request.GET: + response.headers.set(b"Access-Control-Allow-Methods", request.GET.first(b'methods')) + + code_raw = request.GET.first(b'code', None) + if code_raw: + code = int(code_raw) + else: + code = None + if request.method == u'OPTIONS': + #Override the response code if we're in a preflight and it's asked + if b'preflight' in request.GET: + code = int(request.GET.first(b'preflight')) + + #Log that the preflight actually happened if we have an ident + if b'token' in request.GET: + request.server.stash.put(request.GET[b'token'], True) + + if b'location' in request.GET: + if code is None: + code = 302 + + if code >= 300 and code < 400: + response.headers.set(b"Location", request.GET.first(b'location')) + + headers = {} + for name, values in request.headers.items(): + if len(values) == 1: + headers[isomorphic_decode(name)] = isomorphic_decode(values[0]) + else: + #I have no idea, really + headers[name] = values + + headers[u'get_value'] = isomorphic_decode(request.GET.first(b'get_value', b'')) + + body = json.dumps(headers) + + if code: + return (code, b"StatusText"), [], body + else: + return body diff --git a/testing/web-platform/tests/cors/resources/expose-headers.py b/testing/web-platform/tests/cors/resources/expose-headers.py new file mode 100644 index 0000000000..e9de401cf2 --- /dev/null +++ b/testing/web-platform/tests/cors/resources/expose-headers.py @@ -0,0 +1,10 @@ +def main(request, response): + response.add_required_headers = False + output = b"HTTP/1.1 221 ALL YOUR BASE BELONG TO H1\r\n" + output += b"Access-Control-Allow-Origin: *\r\n" + output += b"BB-8: hey\r\n" + output += b"Content-Language: mkay\r\n" + output += request.GET.first(b"expose") + b"\r\n" + output += b"\r\n" + response.writer.write(output) + response.close_connection = True diff --git a/testing/web-platform/tests/cors/resources/image-tainting-checker.sub.html b/testing/web-platform/tests/cors/resources/image-tainting-checker.sub.html new file mode 100644 index 0000000000..59de9e7a23 --- /dev/null +++ b/testing/web-platform/tests/cors/resources/image-tainting-checker.sub.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<body> +<canvas id="canvas"></canvas> +<script> +// Used by image-tainting-in-cross-origin-iframe.sub.html to check that an +// image resource loaded by the top level frame that is same-origin to the +// frame isn't treated as a same-origin resource in a cross-origin iframe. +const canvas = document.getElementById('canvas'); +const ctx = canvas.getContext('2d'); +const img = new Image(); +img.src = 'http://{{host}}:{{ports[http][0]}}/images/blue-png-cachable.py'; +img.onload = () => { + ctx.drawImage(img, 0, 0); + try { + ctx.getImageData(0, 0, 1, 1); + parent.postMessage('FAIL: getImageData() didn\'t throw', '*'); + } catch (e) { + parent.postMessage('DONE', '*'); + } +}; +</script> +</body> diff --git a/testing/web-platform/tests/cors/resources/preflight-cache-partitioning-iframe.sub.html b/testing/web-platform/tests/cors/resources/preflight-cache-partitioning-iframe.sub.html new file mode 100644 index 0000000000..031cc94d10 --- /dev/null +++ b/testing/web-platform/tests/cors/resources/preflight-cache-partitioning-iframe.sub.html @@ -0,0 +1,27 @@ +<script> +window.onmessage = async (e) => { + if (e.data.type === "run") { + let token = e.data.token; + const test_url = + `http://{{hosts[alt][]}}:{{ports[http][0]}}/cors/resources/preflight-partitioning.py?token=${token}`; + + let response = await fetch( + new Request(test_url, { + mode: "cors", + method: "GET", + headers: [["x-print", token]], + }) + ); + + let result = await response.text(); + + if (result === "1") { + parent.postMessage({ type: "pass", msg: "The CORS preflight was sent" }, "*"); + } else { + parent.postMessage({ type: "fail", msg: "The CORS preflight wasn't sent" }, "*"); + } + } +}; + +parent.postMessage({ type: "loaded" }, "*"); +</script> diff --git a/testing/web-platform/tests/cors/resources/preflight-cache-partitioning.sub.html b/testing/web-platform/tests/cors/resources/preflight-cache-partitioning.sub.html new file mode 100644 index 0000000000..e4bbdec013 --- /dev/null +++ b/testing/web-platform/tests/cors/resources/preflight-cache-partitioning.sub.html @@ -0,0 +1,20 @@ +<!doctype html> +<meta charset="utf-8"> +<title>Helper page for testing preflight cache partitioning</title> +<iframe id="iframe" src="http://{{host}}:{{ports[http][0]}}/cors/resources/preflight-cache-partitioning-iframe.sub.html"></iframe> +<script> +window.onmessage = (e) => { + switch (e.data.type || "") { + case "pass": + case "fail": + case "loaded": + opener.postMessage(e.data, "*"); + break; + default: + let iframe = document.getElementById("iframe"); + iframe.contentWindow.postMessage(e.data, "*"); + break; + } +}; + +</script> diff --git a/testing/web-platform/tests/cors/resources/preflight-partitioning.py b/testing/web-platform/tests/cors/resources/preflight-partitioning.py new file mode 100644 index 0000000000..d0d9a4ebb6 --- /dev/null +++ b/testing/web-platform/tests/cors/resources/preflight-partitioning.py @@ -0,0 +1,35 @@ +def main(request, response): + headers = [(b"Content-Type", b"text/plain")] + headers.append((b"Access-Control-Allow-Origin", b"*")) + + if request.method == u"GET": + token = request.GET.first(b"token") + value = request.server.stash.take(token) + if value == None: + body = u"0" + else: + if request.GET.first(b"check", None) == b"keep": + request.server.stash.put(token, value) + body = u"1" + + return headers, body + + if request.method == u"OPTIONS": + if not b"Access-Control-Request-Method" in request.headers: + response.set_error(400, u"No Access-Control-Request-Method header") + return u"ERROR: No access-control-request-method in preflight!" + + headers.append((b"Access-Control-Allow-Methods", + request.headers[b'Access-Control-Request-Method'])) + + if b"max_age" in request.GET: + headers.append((b"Access-Control-Max-Age", request.GET[b'max_age'])) + + if b"token" in request.GET: + request.server.stash.put(request.GET.first(b"token"), 1) + + headers.append((b"Access-Control-Allow-Headers", b"x-print")) + + body = request.headers.get(b"x-print", b"NO") + + return headers, body diff --git a/testing/web-platform/tests/cors/resources/preflight.py b/testing/web-platform/tests/cors/resources/preflight.py new file mode 100644 index 0000000000..d8bce26d28 --- /dev/null +++ b/testing/web-platform/tests/cors/resources/preflight.py @@ -0,0 +1,35 @@ +def main(request, response): + headers = [(b"Content-Type", b"text/plain")] + + if b"check" in request.GET: + token = request.GET.first(b"token") + value = request.server.stash.take(token) + if value == None: + body = u"0" + else: + if request.GET.first(b"check", None) == b"keep": + request.server.stash.put(token, value) + body = u"1" + + return headers, body + + if request.method == u"OPTIONS": + if not b"Access-Control-Request-Method" in request.headers: + response.set_error(400, u"No Access-Control-Request-Method header") + return u"ERROR: No access-control-request-method in preflight!" + + headers.append((b"Access-Control-Allow-Methods", + request.headers[b'Access-Control-Request-Method'])) + + if b"max_age" in request.GET: + headers.append((b"Access-Control-Max-Age", request.GET[b'max_age'])) + + if b"token" in request.GET: + request.server.stash.put(request.GET.first(b"token"), 1) + + headers.append((b"Access-Control-Allow-Origin", b"*")) + headers.append((b"Access-Control-Allow-Headers", b"x-print")) + + body = request.headers.get(b"x-print", b"NO") + + return headers, body diff --git a/testing/web-platform/tests/cors/resources/remote-xhrer.html b/testing/web-platform/tests/cors/resources/remote-xhrer.html new file mode 100644 index 0000000000..73a7cb444f --- /dev/null +++ b/testing/web-platform/tests/cors/resources/remote-xhrer.html @@ -0,0 +1,28 @@ +<!doctype html> +<title>Child helper</title> + +<script> +window.addEventListener("message", function(e) { +// e.source.postMessage(e.data, e.origin); + + var client = new XMLHttpRequest(); + var localurl = e.data.url + .replace("<host>", location.host) + .replace("<protocol>", location.protocol); + + client.open('GET', localurl, true); + client.onload = function() { + e.data.state = "load"; + e.data.response = client.response; + e.source.postMessage(e.data, e.origin); + } + client.onerror = function() { + e.data.state = "error"; + e.data.response = client.response; + e.source.postMessage(e.data, e.origin); + } + client.send(); +}); +</script> + +The remote window diff --git a/testing/web-platform/tests/cors/resources/status.py b/testing/web-platform/tests/cors/resources/status.py new file mode 100644 index 0000000000..5d37e7a038 --- /dev/null +++ b/testing/web-platform/tests/cors/resources/status.py @@ -0,0 +1,39 @@ +from wptserve.utils import isomorphic_encode + +def main(request, response): + response.headers.set(b"Access-Control-Allow-Origin", request.headers.get(b"origin")) + response.headers.set(b"Access-Control-Expose-Headers", b"X-Request-Method") + + if request.method == u'OPTIONS': + response.headers.set(b"Access-Control-Allow-Methods", b"GET, CHICKEN, HEAD, POST, PUT") + + if b'headers' in request.GET: + response.headers.set(b"Access-Control-Allow-Headers", request.GET.first(b'headers')) + + response.headers.set(b"X-Request-Method", isomorphic_encode(request.method)) + + response.headers.set(b"X-A-C-Request-Method", request.headers.get(b"Access-Control-Request-Method", b"")) + + + #This should reasonably work for most response codes. + try: + code = int(request.GET.first(b"code", 200)) + except ValueError: + code = 200 + + text = request.GET.first(b"text", b"OMG") + + if request.method == u"OPTIONS" and b"preflight" in request.GET: + try: + code = int(request.GET.first(b'preflight')) + except KeyError: + pass + + status = code, text + + if b"type" in request.GET: + response.headers.set(b"Content-Type", request.GET.first(b'type')) + + body = request.GET.first(b'content', b"") + + return status, [], body diff --git a/testing/web-platform/tests/cors/response-headers.htm b/testing/web-platform/tests/cors/response-headers.htm new file mode 100644 index 0000000000..cc06a239a6 --- /dev/null +++ b/testing/web-platform/tests/cors/response-headers.htm @@ -0,0 +1,105 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CORS - Response headers</title> +<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com"> + +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=support.js?pipe=sub></script> + +<h1>Response headers</h1> +<div id=log></div> +<script> + +/* + * Response Headers + */ + +function check_response_header(head, value, desc) { + test(function() { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-headers.asis', false) + client.send(null) + + if (typeof value === 'function') + value(client, head) + else + assert_equals(client.getResponseHeader(head), value, head) + }, + desc) +} +check_response_header('X-Custom-Header-Comma', '1, 2', 'getResponseHeader: Expose Access-Control-Expose-Headers (x-custom-header-comma)') +check_response_header('X-Second-Expose', 'flyingpig', 'getResponseHeader: Expose second Access-Control-Expose-Headers (x-second-expose)') +check_response_header(' x-custom-header', null, 'getResponseHeader: Don\'t trim whitespace') +check_response_header('x-custom-header-bytes', "\xE2\x80\xA6", 'getResponseHeader: x-custom-header bytes') +check_response_header('Date', + function(client, head) { assert_true(client.getResponseHeader(head).length > 2) }, + 'getResponseHeader: Exposed server field readable (Date)') + +function default_readable(head, value) { + check_response_header(head, value, 'getResponseHeader: '+head+': readable by default') +} +default_readable("Cache-Control", "no-cache"); +default_readable("Content-Language", "nn"); +default_readable("Expires", "Thu, 01 Dec 1994 16:00:00 GMT"); +default_readable("Last-Modified", "Thu, 01 Dec 1994 10:00:00 GMT"); +default_readable("Pragma", "no-cache"); +default_readable("Content-Length", "4"); +default_readable("Content-Type", "text/plain"); + + +function default_unreadable(head) { + check_response_header(head, null, 'getResponseHeader: '+head+': unreadable by default') +} +default_unreadable("Server") +default_unreadable("X-Powered-By") + + +async_test("getResponseHeader: Combined testing of cors response headers") +.step(function() +{ + var client = new XMLHttpRequest(); + client.open("GET", CROSSDOMAIN + 'resources/cors-headers.asis') + window.c=client; + client.onreadystatechange = this.step_func(function() + { + if (client.readyState == 1) + { + assert_equals(client.getResponseHeader("x-custom-header"), null, 'x-custom-header') + } + if (client.readyState > 1) + { + assert_equals(client.getResponseHeader("x-custom-header"), "test, test", 'x-custom-header') + assert_equals(client.getResponseHeader("x-custom-header-empty"), "", 'x-custom-header-empty') + assert_equals(client.getResponseHeader("set-cookie"), null) + assert_equals(client.getResponseHeader("set-cookie2"), null) + assert_equals(client.getResponseHeader("x-non-existent-header"), null) + assert_equals(client.getResponseHeader("x-nonexposed"), null) + } + if (client.readyState == 4) + { + this.done() + } + }) + client.send() +}) + +test(function() { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-headers.asis', false) + client.send(null) + assert_equals(client.getResponseHeader("x-custom-header"), "test, test", 'x-custom-header') + assert_equals(client.getResponseHeader("x-nonexposed"), null, 'x-nonexposed') +}, "getResponse: don't expose x-nonexposed") + +test(function() { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-headers.asis', false) + client.send(null) + + h = client.getAllResponseHeaders().toLowerCase() + assert_true( h.indexOf('x-custom-header') >= 0, 'x-custom-header present') + assert_true( h.indexOf('x-nonexposed') === -1, 'x-nonexposed not present') +}, "getAllResponseHeaders: don't expose x-nonexposed") + +</script> diff --git a/testing/web-platform/tests/cors/script-304.html b/testing/web-platform/tests/cors/script-304.html new file mode 100644 index 0000000000..e164ca6f41 --- /dev/null +++ b/testing/web-platform/tests/cors/script-304.html @@ -0,0 +1,40 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/common/get-host-info.sub.js"></script> + <script src="/common/utils.js"></script> + </head> + <body> + <div id="testDiv"></div> + <script> + +const scriptURL = get_host_info().HTTP_REMOTE_ORIGIN + "/cors/resources/cache-304.py?" + token(); + +function loadScript(test) +{ + const script = document.createElement("script"); + script.crossOrigin = "anonymous"; + script.src = scriptURL; + return new Promise((resolve, reject) => { + // Let's add a small timeout so that the script is fully loaded in memory cache before reloading it. + script.onload = test.step_timeout(resolve, 50); + script.onerror = reject; + testDiv.appendChild(script); + }); +} + +promise_test((test) => { + return loadScript(test); +}, "Load a fresh cross-origin script"); + +promise_test((test) => { + return loadScript(test); +}, "Reload same cross-origin script from the memory cache after revalidation"); + +</script> + </body> +</html> + diff --git a/testing/web-platform/tests/cors/simple-requests-ch.tentative.htm b/testing/web-platform/tests/cors/simple-requests-ch.tentative.htm new file mode 100644 index 0000000000..78212de49e --- /dev/null +++ b/testing/web-platform/tests/cors/simple-requests-ch.tentative.htm @@ -0,0 +1,60 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CORS - simple requests</title> +<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com"> + +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=support.js?pipe=sub></script> +<script src=/common/utils.js></script> + +<h1>Simple requests</h1> +<p>Simple requests shouldn't trigger preflight</p> + +<div id=log></div> +<script> + +var test_c = 0; + +function check_simple(method, headers) +{ + test(function() { + var client = new XMLHttpRequest() + var uuid_token = token(); + client.open(method, CROSSDOMAIN + 'resources/preflight.py?token=' + + uuid_token, false) + for (head in headers) + client.setRequestHeader(head, headers[head]) + client.send("data") + assert_equals(client.getResponseHeader('content-type'), "text/plain") + if (method == 'HEAD') + assert_equals(client.response, '', 'response') + else + assert_equals(client.response, 'NO', 'response') + + client.open('GET', 'resources/preflight.py?check&token=' + + uuid_token, false) + client.send("data") + assert_equals(client.response, "0", "Found preflight log") + }, + 'No preflight ' + method + ' and ' + JSON.stringify(headers)) +} + +function check_simple_headers(headers) { + check_simple('GET', headers) + check_simple('HEAD', headers) + check_simple('POST', headers) +} + +check_simple_headers({ + 'save-data': 'on', + 'device-memory': '2.0', + 'dpr': '3.0', + 'width': '1200', + 'viewport-width': '1300', + 'rtt': '1', + 'downlink': '1.0', + 'ect': '2g' + }) + +</script> diff --git a/testing/web-platform/tests/cors/simple-requests.htm b/testing/web-platform/tests/cors/simple-requests.htm new file mode 100644 index 0000000000..185e9a75e3 --- /dev/null +++ b/testing/web-platform/tests/cors/simple-requests.htm @@ -0,0 +1,90 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CORS - simple requests</title> +<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com"> + +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=support.js?pipe=sub></script> +<script src=/common/utils.js></script> + +<h1>Simple requests</h1> +<p>Simple requests shouldn't trigger preflight</p> + +<div id=log></div> +<script> + +var test_c = 0; + +function check_simple(method, headers) +{ + test(function() { + var client = new XMLHttpRequest() + var uuid_token = token(); + client.open(method, CROSSDOMAIN + 'resources/preflight.py?token=' + + uuid_token, false) + for (head in headers) + client.setRequestHeader(head, headers[head]) + client.send("data") + assert_equals(client.getResponseHeader('content-type'), "text/plain") + if (method == 'HEAD') + assert_equals(client.response, '', 'response') + else + assert_equals(client.response, 'NO', 'response') + + client.open('GET', 'resources/preflight.py?check&token=' + + uuid_token, false) + client.send("data") + assert_equals(client.response, "0", "Found preflight log") + }, + 'No preflight ' + method + ' and ' + JSON.stringify(headers)) +} + +function check_simple_headers(headers) { + check_simple('GET', headers) + check_simple('HEAD', headers) + check_simple('POST', headers) +} + +check_simple_headers({'Accept': 'test'}) +check_simple_headers({'accept-language': 'test'}) +check_simple_headers({'CONTENT-language': 'test'}) + +check_simple_headers({'Content-Type': 'application/x-www-form-urlencoded'}) +check_simple_headers({'content-type': 'multipart/form-data'}) +check_simple_headers({'content-type': 'text/plain'}) + +check_simple_headers({ + 'accept': 'test', + 'accept-language': 'test', + 'content-language': 'test', + 'content-type': 'text/plain; parameter=whatever' + }) + +check_simple('Get', {'content-type': 'text/plain; parameter=extra_bonus'}) +check_simple('post', {'content-type': 'text/plain'}) + +/* Extra async test */ + +var simple_async = async_test("Check simple headers (async)") +simple_async.step(function (){ + var time = new Date().getTime(), + client = new XMLHttpRequest() + var uuid_token = token(); + client.open('POST', CROSSDOMAIN + 'resources/preflight.py?token=' + + uuid_token, true) + + client.setRequestHeader('Accept', 'jewelry') + client.setRequestHeader('accept-language', 'nn-NO,nn,en') + client.setRequestHeader('content-type', 'text/plain; parameter=extra') + client.setRequestHeader('content-Language', 'nn-NO') + + client.onload = simple_async.step_func(function() { + assert_equals(client.getResponseHeader('content-type'), "text/plain", 'content-type response header') + assert_equals(client.response, 'NO', 'response') + simple_async.done() + }) + client.onerror = simple_async.step_func(function () { assert_unreached('onerror') }) + client.send() +}) +</script> diff --git a/testing/web-platform/tests/cors/status-async.htm b/testing/web-platform/tests/cors/status-async.htm new file mode 100644 index 0000000000..5bcfa0cf7c --- /dev/null +++ b/testing/web-platform/tests/cors/status-async.htm @@ -0,0 +1,114 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CORS - status</title> +<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com"> +<meta name=timeout content=long> + +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=support.js?pipe=sub></script> + +<h1>Status returned</h1> + +<div id=log></div> +<script> + +function statusRequest(method, code, text, content, type) { + async_test("Status on " + method + " " + code) + .step(function() { + var client = new XMLHttpRequest() + client.open(method, CROSSDOMAIN + "resources/status.py?code=" + + code + "&text=" + text + "&content=" + content + "&type=" + type, true) + client.onreadystatechange = this.step_func(function() { + if (client.readyState != client.DONE) + return + + assert_equals(client.status, code, 'response status') + assert_equals(client.statusText, text, 'response status text') + assert_equals(client.getResponseHeader("X-Request-Method"), method, 'method') + if(method != "HEAD") { + if(type == "text/xml") { + assert_equals(client.responseXML.documentElement.localName, + "x", 'responseXML') + } + assert_equals(client.response, content, 'response content') + } + this.done() + }) + + client.send(null) + }) +} + + /* method code text content type */ + statusRequest("GET", 200, 'OK', 'Not today.', '') + statusRequest("GET", 201, 'OK/Created', 'Not today 01.', '') + statusRequest("GET", 202, 'OK/Accepted', 'Not today 02.', '') + statusRequest("GET", 203, 'OK/Non-Authoritative Information', 'Not today 03.', '') + statusRequest("GET", 204, 'OK/No Content', '', '') // specifically no-content + statusRequest("GET", 205, 'OK/Reset Content', '', '') // specifically no-content + statusRequest("GET", 206, 'OK/Partial Content', 'Not today 06.', '') + statusRequest("GET", 209, 'OK', 'Not today 09.', '') + statusRequest("GET", 299, 'OK', 'Not today 99.', '') + statusRequest("POST", 200, 'OK', '<x>402<\/x>', 'text/xml') + statusRequest("HEAD", 200, 'OK', 'Nice!', 'text/doesnotmatter') + statusRequest("PUT", 200, 'OK', '400', 'text/plain') + statusRequest("CHICKEN", 200, 'OK', 'bah', '') + + +function statusRequestFail(method, code, expect_code, nonsimple) { + if (expect_code === undefined) + expect_code = code + + async_test("Status on " + method + " " + code + (nonsimple?' (nonsimple)':'')) + .step(function() { + var client = new XMLHttpRequest() + + client.open(method, CROSSDOMAIN + "resources/status.py?code=" + + code + '&headers=x-nonsimple&text=OHAI', true) + + if (nonsimple) + client.setRequestHeader('x-nonsimple', true) + + client.onreadystatechange = this.step_func(function() { + if (client.readyState < client.HEADERS_RECEIVED) + return + assert_equals(client.response, "", "response data") + assert_equals(client.status, expect_code, "response status") + /* Response code 200 forces webserver to send OK(?) */ + if(expect_code == 200) + assert_equals(client.statusText, "OK", "response statusText") + else + assert_equals(client.statusText, (expect_code == 0 ? "" : "OHAI"), "response statusText") + if (client.readyState == client.DONE) + this.done() + }) + + client.onerror = this.step_func(function(e) { + assert_unreached("Got error event.") + }) + + client.send() + }) +} + + /* expect + method code status */ + statusRequestFail("GET", 400) + statusRequestFail("HEAD", 401) + statusRequestFail("POST", 404) + statusRequestFail("POST", 500) + + /* Preflight response status is not 200, so the algorithm set status to 0. */ + statusRequestFail("PUT", 699, 0) + statusRequestFail("CHICKEN", 501, 0) + + /* "forced" + preflight */ + statusRequestFail("GET", 400, 0, true) + statusRequestFail("HEAD", 401, 0, true) + statusRequestFail("POST", 404, 0, true) + statusRequestFail("PUT", 699, 0, true) + statusRequestFail("CHICKEN", 501, 0, true) + +</script> diff --git a/testing/web-platform/tests/cors/status-preflight.htm b/testing/web-platform/tests/cors/status-preflight.htm new file mode 100644 index 0000000000..a72c2fd79f --- /dev/null +++ b/testing/web-platform/tests/cors/status-preflight.htm @@ -0,0 +1,65 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CORS - status after preflight</title> +<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com"> + +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=support.js?pipe=sub></script> + +<h1>Status after preflight</h1> + +<div id=log></div> +<script> +var counter = 0 + +function statusAfterPreflight(method, code) { + counter++ + + async_test(document.title + " on " + method + " " + code).step(function() { + var client = new XMLHttpRequest() + client.open(method, CROSSDOMAIN + "resources/status.py?" + counter + +"&code=" + code + '&headers=x-nonsimple&preflight=200', true) + + client.setRequestHeader('x-nonsimple', true) + client.onreadystatechange = this.step_func(function() { + if (client.readyState < client.HEADERS_RECEIVED) + return + assert_equals(client.response, "", "response data") + assert_equals(client.status, code, "response status") + if (client.readyState == client.DONE) { + /* Wait for spurious error events */ + this.step_timeout(() => { this.done() }, 10) + } + }) + + client.onerror = this.step_func(function() { + assert_unreached("Shouldn't throw no error event!") + }) + + client.send() + }) +} + +/* method code */ +statusAfterPreflight("GET", 200) +statusAfterPreflight("GET", 204) +statusAfterPreflight("GET", 400) +statusAfterPreflight("GET", 401) + +statusAfterPreflight("HEAD", 200) +statusAfterPreflight("HEAD", 204) +statusAfterPreflight("HEAD", 400) +statusAfterPreflight("HEAD", 401) +statusAfterPreflight("HEAD", 501) +statusAfterPreflight("HEAD", 699) + +statusAfterPreflight("POST", 204) +statusAfterPreflight("POST", 400) +statusAfterPreflight("POST", 401) +statusAfterPreflight("POST", 404) + +statusAfterPreflight("PUT", 699) +statusAfterPreflight("CHICKEN", 501) + +</script> diff --git a/testing/web-platform/tests/cors/status.htm b/testing/web-platform/tests/cors/status.htm new file mode 100644 index 0000000000..73244288d4 --- /dev/null +++ b/testing/web-platform/tests/cors/status.htm @@ -0,0 +1,80 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CORS status</title> +<link rel=help href=https://fetch.spec.whatwg.org/> +<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com"> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support.js?pipe=sub"></script> + +<h1>The returned status code in different scenarios</h1> + +<script> + + var counter = 0 + + function testit(allow, preflight, response, status) { + async_test( + (++counter) + '. ' + + (allow ? 'CORS allowed' : 'CORS disallowed') + + (preflight ? ', preflight status '+preflight : '') + + (response ? ', response status '+response : '') + + '.' + ).step(function() { + var client = new XMLHttpRequest() + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?' + counter + + (allow ? '&headers=x-custom': '&origin=none') + + (response ? '&code='+response : '') + + (preflight ? '&preflight='+preflight : '') + ) + + if (preflight) + client.setRequestHeader('X-Custom', 'preflight') + + client.onload = this.step_func(function() { + if (!status) + assert_unreached("load event") + + /* Allow spurious error events to fire */ + this.step_timeout(() => { + assert_equals(client.status, status, "status") + this.done() + }, 10) + }) + + client.onerror = this.step_func(function() { + if (status) + assert_unreached("error event") + + assert_equals(client.readyState, client.DONE, 'readyState') + assert_equals(client.status, 0, 'status') + this.done() + }) + + client.send() + + }) + } + + /* allow pref resp status */ + testit(false, null, 400, 0) + testit(false, 200, null, 0) + testit(true, null, 400, 400) + testit(true, 200, 400, 400) + testit(true, 400, null, 0) + +</script> + +<pre> + allowed preflight response | status | + ------- --------- -------- | ------ | + 1 no x 400 | 0 | + 2 no 200 x | 0 | + 3 yes x 400 | 400 | + 4 yes 200 400 | 400 | + 5 yes 400 x | 0 | +</pre> + +<div id=log></div> + diff --git a/testing/web-platform/tests/cors/support.js b/testing/web-platform/tests/cors/support.js new file mode 100644 index 0000000000..2dde95d9a0 --- /dev/null +++ b/testing/web-platform/tests/cors/support.js @@ -0,0 +1,17 @@ +function dirname(path) { + return path.replace(/\/[^\/]*$/, '/') +} + +/* This subdomain should point to this same location */ +var SUBDOMAIN = 'www1' +var SUBDOMAIN2 = 'www2' +var PORT = {{ports[http][1]}} +//XXX HTTPS +var PORTS = {{ports[https][0]}} + +/* Changes http://example.com/abc/def/cool.htm to http://www1.example.com/abc/def/ */ +var CROSSDOMAIN = dirname(location.href) + .replace('://', '://' + SUBDOMAIN + '.') +var REMOTE_HOST = SUBDOMAIN + '.' + location.host +var REMOTE_PROTOCOL = location.protocol +var REMOTE_ORIGIN = REMOTE_PROTOCOL + '//' + REMOTE_HOST |