diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /testing/web-platform/tests/cors | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'testing/web-platform/tests/cors')
27 files changed, 1918 insertions, 0 deletions
diff --git a/testing/web-platform/tests/cors/OWNERS b/testing/web-platform/tests/cors/OWNERS new file mode 100644 index 000000000..c89797924 --- /dev/null +++ b/testing/web-platform/tests/cors/OWNERS @@ -0,0 +1,5 @@ +@sideshowbarker +@zqzhang +@Velmont +@hillbrad +@jdm diff --git a/testing/web-platform/tests/cors/allow-headers.htm b/testing/web-platform/tests/cors/allow-headers.htm new file mode 100644 index 000000000..08e00a527 --- /dev/null +++ b/testing/web-platform/tests/cors/allow-headers.htm @@ -0,0 +1,89 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Access-Control-Allow-Headers 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-Headers 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(null, function() { client.send() }, 'send') + }, 'Disallow origin: ' + origin.replace('\0', '\\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("\0*") +shouldFail("*\0") +shouldFail("'*'") +shouldFail('"*"') +shouldFail("* *") +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('') +shouldFail(location.href) +shouldFail(dirname(location.href)) +shouldFail(CROSSDOMAIN) + +</script> diff --git a/testing/web-platform/tests/cors/basic.htm b/testing/web-platform/tests/cors/basic.htm new file mode 100644 index 000000000..c46e9a0a1 --- /dev/null +++ b/testing/web-platform/tests/cors/basic.htm @@ -0,0 +1,63 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Basic CORS</title> +<meta name=help href=http://dvcs.w3.org/hg/cors/raw-file/tip/Overview.html#simple-cross-origin-request-0> +<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> +<div id=log></div> + +<script> + +var counter = 0; + +function cors(desc, scheme, subdomain, port) { + if (!scheme) { + var url = ""; + } else { + if (!port) { + port = location.port; + } + var url = scheme + "://" + (subdomain ? subdomain + "." : "") + location.hostname + ":" + port + dirname(location.pathname) + } + async_test(desc).step(function() { + var client = new XMLHttpRequest(); + this.count = counter++; + + client.open("GET", url + "resources/cors-makeheader.py?get_value=hest_er_best&origin=none&" + this.count); + + client.onreadystatechange = this.step_func(function(e) { + // First request, test that it fails with no origin + if (client.readyState < 4) return; + if (!url) + assert_true(client.response.indexOf("hest_er_best") != -1, "Got response"); + else + assert_false(!!client.response, "Got CORS-disallowed response"); + + client = new XMLHttpRequest(); + client.open("GET", url + "resources/cors-makeheader.py?get_value=hest_er_best&" + this.count); + client.onreadystatechange = this.step_func(function(e) { + // Second request, test that it passes with the allowed-origin + if (client.readyState < 4) return; + assert_true(client.response.indexOf("hest_er_best") != -1, "Got CORS-allowed response"); + this.done(); + }); + client.send(); + }); + client.send(); + }); +} + +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/credentials-flag.htm b/testing/web-platform/tests/cors/credentials-flag.htm new file mode 100644 index 000000000..bacd43c4e --- /dev/null +++ b/testing/web-platform/tests/cors/credentials-flag.htm @@ -0,0 +1,129 @@ +<!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('false') +test_response_header('1') +test_response_header('0') + +</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 000000000..367955aa4 --- /dev/null +++ b/testing/web-platform/tests/cors/late-upload-events.htm @@ -0,0 +1,46 @@ +<!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, expectEvents) { + 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); + if (expectEvents) { + assert_true(eventCounter > 3, 'Events did fire'); + } else { + assert_equals(eventCounter, 0, 'No events did fire'); + } + 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++; + if (!expectEvents) { + assert_unreached("Upload events should not fire, but did: " + e.type); + } + }); + }); +} + +doTest("No preflight", {}, false); +doTest("Preflight", {"custom-header":"test"}, true); +</script> diff --git a/testing/web-platform/tests/cors/origin.htm b/testing/web-platform/tests/cors/origin.htm new file mode 100644 index 000000000..a090b3340 --- /dev/null +++ b/testing/web-platform/tests/cors/origin.htm @@ -0,0 +1,119 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Access-Control-Allow-Origin handling</title> +<meta name=help href=http://dvcs.w3.org/hg/cors/raw-file/tip/Overview.html#resource-sharing-check> +<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(null, 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("\0*") +shouldFail("*\0") +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(null, 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.htm b/testing/web-platform/tests/cors/preflight-cache.htm new file mode 100644 index 000000000..f84c15ab3 --- /dev/null +++ b/testing/web-platform/tests/cors/preflight-cache.htm @@ -0,0 +1,140 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CORS - preflight cache</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=/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=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", { timeout: 6000 }), + 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) */ + setTimeout(test.step_func(function(){ + 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/redirect-origin.htm b/testing/web-platform/tests/cors/redirect-origin.htm new file mode 100644 index 000000000..5463292fa --- /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 000000000..fe58d90a2 --- /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 000000000..bb47f2cae --- /dev/null +++ b/testing/web-platform/tests/cors/redirect-preflight.htm @@ -0,0 +1,65 @@ +<!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"> + +<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> + +var req_c = 0 // Request count for cache busting and easy identifying of request in traffic analyzer + +/* + * Redirection with preflights + */ + +function redir_preflight(code) { + test(function() { + var client = new XMLHttpRequest(), + redirect = CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-test&' + req_c++ + + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?' + + 'headers=x-test&location=' + encodeURIComponent(redirect) + + '&code=' + code + '&preflight=' + code + '&' + req_c++, + false) + client.setRequestHeader('x-test', 'test') + assert_throws(null, function() { client.send(null) }); + + }, + 'Redirect ' + code + ' on preflight') +} +redir_preflight(301) +redir_preflight(302) +redir_preflight(303) +redir_preflight(307) +redir_preflight(308) + +/* Even thought the preflight was allowed (200), CORS should not follow + a subsequent redirect */ +function redir_after_preflight(code) { + test(function() { + var client = new XMLHttpRequest(), + redirect = CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-test&' + req_c++ + + client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?' + + 'preflight=200&headers=x-test&location=' + + encodeURIComponent(redirect) + '&code=' + code + '&' + req_c++, + false) + client.setRequestHeader('x-test', 'test') + assert_throws(null, function() { client.send(null) }); + + }, + 'Disallow redirect ' + code + ' after succesful (200) preflight') +} +redir_after_preflight(301) +redir_after_preflight(302) +redir_after_preflight(303) +redir_after_preflight(307) +redir_after_preflight(308) + +</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 000000000..1775d30df --- /dev/null +++ b/testing/web-platform/tests/cors/redirect-userinfo.htm @@ -0,0 +1,110 @@ +<!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?"]); + + shouldFail("Disallow redirect with userinfo (//:@)", [ + 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 with userinfo (//@)", [ + 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.step_func(function() { + assert_false(!!client.response, "Got response"); + }); + client.onerror = t.step_func(function(e) { + t.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.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['get_value'], 'last', 'get_value') + 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/remote-origin.htm b/testing/web-platform/tests/cors/remote-origin.htm new file mode 100644 index 000000000..072677516 --- /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 000000000..8adaeb4f8 --- /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(null, 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('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('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/.gitignore b/testing/web-platform/tests/cors/resources/.gitignore new file mode 100644 index 000000000..7b987d036 --- /dev/null +++ b/testing/web-platform/tests/cors/resources/.gitignore @@ -0,0 +1 @@ +logs.txt 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 000000000..f713d990a --- /dev/null +++ b/testing/web-platform/tests/cors/resources/checkandremove.py @@ -0,0 +1,6 @@ +def main(request, response): + token = request.GET.first("token") + if request.server.stash.remove(token) is not None: + return "1" + else: + return "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 000000000..76fa6c165 --- /dev/null +++ b/testing/web-platform/tests/cors/resources/cors-cookie.py @@ -0,0 +1,21 @@ + +def main(request, response): + origin = request.GET.first("origin", request.headers["origin"]) + credentials = request.GET.first("credentials", "true") + + headers = [("Content-Type", "text/plain")] + if origin != 'none': + headers.append(("Access-Control-Allow-Origin", origin)) + if credentials != 'none': + headers.append(("Access-Control-Allow-Credentials", credentials)) + + ident = request.GET.first('ident', 'test') + + if ident in request.cookies: + body = request.cookies[ident].value + response.delete_cookie(ident) + else: + response.set_cookie(ident, "COOKIE"); + body = "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 000000000..ce21245f1 --- /dev/null +++ b/testing/web-platform/tests/cors/resources/cors-headers.asis @@ -0,0 +1,24 @@ +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
+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 000000000..eab35eedf --- /dev/null +++ b/testing/web-platform/tests/cors/resources/cors-makeheader.py @@ -0,0 +1,67 @@ +import json + +def main(request, response): + origin = request.GET.first("origin", request.headers.get('origin')) + + if "check" in request.GET: + token = request.GET.first("token") + value = request.server.stash.take(token) + if value is not None: + if request.GET.first("check", None) == "keep": + request.server.stash.put(token, value) + body = "1" + else: + body = "0" + return [("Content-Type", "text/plain")], body + + + if origin != 'none': + response.headers.set("Access-Control-Allow-Origin", origin) + if 'origin2' in request.GET: + response.headers.append("Access-Control-Allow-Origin", request.GET.first('origin2')) + + #Preflight + if 'headers' in request.GET: + response.headers.set("Access-Control-Allow-Headers", request.GET.first('headers')) + if 'credentials' in request.GET: + response.headers.set("Access-Control-Allow-Credentials", request.GET.first('credentials')) + if 'methods' in request.GET: + response.headers.set("Access-Control-Allow-Methods", request.GET.first('methods')) + + code_raw = request.GET.first('code', None) + if code_raw: + code = int(code_raw) + else: + code = None + if request.method == 'OPTIONS': + #Override the response code if we're in a preflight and it's asked + if 'preflight' in request.GET: + code = int(request.GET.first('preflight')) + + #Log that the preflight actually happened if we have an ident + if 'token' in request.GET: + request.server.stash.put(request.GET['token'], True) + + if 'location' in request.GET: + if code is None: + code = 302 + + if code >= 300 and code < 400: + response.headers.set("Location", request.GET.first('location')) + + headers = {} + for name, values in request.headers.iteritems(): + if len(values) == 1: + headers[name] = values[0] + else: + #I have no idea, really + headers[name] = values + + headers['get_value'] = request.GET.first('get_value', '') + + body = json.dumps(headers) + + if code: + return (code, "StatusText"), [], body + else: + return 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 000000000..978e97c5d --- /dev/null +++ b/testing/web-platform/tests/cors/resources/preflight.py @@ -0,0 +1,35 @@ +def main(request, response): + headers = [("Content-Type", "text/plain")] + + if "check" in request.GET: + token = request.GET.first("token") + value = request.server.stash.take(token) + if value == None: + body = "0" + else: + if request.GET.first("check", None) == "keep": + request.server.stash.put(token, value) + body = "1" + + return headers, body + + if request.method == "OPTIONS": + if not "Access-Control-Request-Method" in request.headers: + response.set_error(400, "No Access-Control-Request-Method header") + return "ERROR: No access-control-request-method in preflight!" + + headers.append(("Access-Control-Allow-Methods", + request.headers['Access-Control-Request-Method'])) + + if "max_age" in request.GET: + headers.append(("Access-Control-Max-Age", request.GET['max_age'])) + + if "token" in request.GET: + request.server.stash.put(request.GET.first("token"), 1) + + headers.append(("Access-Control-Allow-Origin", "*")) + headers.append(("Access-Control-Allow-Headers", "x-print")) + + body = request.headers.get("x-print", "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 000000000..73a7cb444 --- /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 000000000..8d441f06e --- /dev/null +++ b/testing/web-platform/tests/cors/resources/status.py @@ -0,0 +1,37 @@ +def main(request, response): + response.headers.set("Access-Control-Allow-Origin", request.headers.get("origin") ) + response.headers.set("Access-Control-Expose-Headers", "X-Request-Method") + + if request.method == 'OPTIONS': + response.headers.set("Access-Control-Allow-Methods", "GET, CHICKEN, HEAD, POST, PUT") + + if 'headers' in request.GET: + response.headers.set("Access-Control-Allow-Headers", request.GET.first('headers')) + + response.headers.set("X-Request-Method", request.method) + + response.headers.set("X-A-C-Request-Method", request.headers.get("Access-Control-Request-Method", "")); + + + #This should reasonably work for most response codes. + try: + code = int(request.GET.first("code", 200)) + except ValueError: + code = 200 + + text = request.GET.first("text", "OMG") + + if request.method == "OPTIONS" and "preflight" in request.GET: + try: + code = int(request.GET.first('preflight')) + except KeyError, ValueError: + pass + + status = code, text + + if "type" in request.GET: + response.headers.set("Content-Type", request.GET.first('type')) + + body = request.GET.first('content', "") + + 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 000000000..d4d7cf231 --- /dev/null +++ b/testing/web-platform/tests/cors/response-headers.htm @@ -0,0 +1,103 @@ +<!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"); + + +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/simple-requests.htm b/testing/web-platform/tests/cors/simple-requests.htm new file mode 100644 index 000000000..441a8c1ac --- /dev/null +++ b/testing/web-platform/tests/cors/simple-requests.htm @@ -0,0 +1,91 @@ +<!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 000000000..3573ee88f --- /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, { timeout: 15000 }) + .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)':''), { timeout: 15000 }) + .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 000000000..54b9e8e84 --- /dev/null +++ b/testing/web-platform/tests/cors/status-preflight.htm @@ -0,0 +1,64 @@ +<!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 */ + setTimeout(this.step_func(function() { 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 000000000..4997ddab0 --- /dev/null +++ b/testing/web-platform/tests/cors/status.htm @@ -0,0 +1,80 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>CORS status</title> +<meta name=help href=http://dvcs.w3.org/hg/cors/raw-file/tip/Overview.html#cross-origin-request-with-preflight-0> +<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 */ + setTimeout(this.step_func(function() { + 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 000000000..551eb70d0 --- /dev/null +++ b/testing/web-platform/tests/cors/support.js @@ -0,0 +1,30 @@ +// For ignoring exception names (just for testing) +/* +_real_assert_throws = assert_throws; +function assert_throws(d, func, desc) { + try { + func(); + } catch(e) { + return true; + } + assert_unreached("Didn't throw!"); +} +*/ + +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 |