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/fetch/api/request | |
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/fetch/api/request')
13 files changed, 1839 insertions, 0 deletions
diff --git a/testing/web-platform/tests/fetch/api/request/request-cache.html b/testing/web-platform/tests/fetch/api/request/request-cache.html new file mode 100644 index 000000000..f2c442a54 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/request/request-cache.html @@ -0,0 +1,626 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title>Request cache</title> + <meta name="help" href="https://fetch.spec.whatwg.org/#request"> + <meta name="timeout" content="long"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/common/utils.js"></script> + <script src="/common/get-host-info.sub.js"></script> + </head> + <body> + <script> + var now = new Date(); + /** + * Each test is run twice: once using etag/If-None-Match and once with + * date/If-Modified-Since. Each test run gets its own URL and randomized + * content and operates independently. + * + * The test steps are run with request_cache.length fetch requests issued + * and their immediate results sanity-checked. The cache.py server script + * stashes an entry containing any If-None-Match, If-Modified-Since, Pragma, + * and Cache-Control observed headers for each request it receives. When + * the test fetches have run, this state is retrieved from cache.py and the + * expected_* lists are checked, including their length. + * + * This means that if a request_* fetch is expected to hit the cache and not + * touch the network, then there will be no entry for it in the expect_* + * lists. AKA (request_cache.length - expected_validation_headers.length) + * should equal the number of cache hits that didn't touch the network. + * + * Test dictionary keys: + * - state: required string that determines whether the Expires response for + * the fetched document should be set in the future ("fresh") or past + * ("stale"). + * - vary: optional string to be passed to the server for it to quote back + * in a Vary header on the response to us. + * - cache_control: optional string to be passed to the server for it to + * quote back in a Cache-Control header on the response to us. + * - redirect: optional string "same-origin" or "cross-origin". If + * provided, the server will issue an absolute redirect to the script on + * the same or a different origin, as appropriate. The redirected + * location is the script with the redirect parameter removed, so the + * content/state/etc. will be as if you hadn't specified a redirect. + * - request_cache: required array of cache modes to use (via `cache`). + * - request_headers: optional array of explicit fetch `headers` arguments. + * If provided, the server will log an empty dictionary for each request + * instead of the request headers it would normally log. + * - response: optional array of specialized response handling. Right now, + * "error" array entries indicate a network error response is expected + * which will reject with a TypeError. + * - expected_validation_headers: required boolean array indicating whether + * the server should have seen an If-None-Match/If-Modified-Since header + * in the request. + * - expected_no_cache_headers: required boolean array indicating whether + * the server should have seen Pragma/Cache-control:no-cache headers in + * the request. + * - expected_max_age_headers: optional boolean array indicating whether + * the server should have seen a Cache-Control:max-age=0 header in the + * request. + */ + var tests = [ + { + name: 'RequestCache "default" mode checks the cache for previously cached content and goes to the network for stale responses', + state: "stale", + request_cache: ["default", "default"], + expected_validation_headers: [false, true], + expected_no_cache_headers: [false, false], + }, + { + name: 'RequestCache "default" mode checks the cache for previously cached content and avoids going to the network if a fresh response exists', + state: "fresh", + request_cache: ["default", "default"], + expected_validation_headers: [false], + expected_no_cache_headers: [false], + }, + { + name: 'RequestCache "no-cache" mode revalidates stale responses found in the cache', + state: "stale", + request_cache: ["default", "no-cache"], + expected_validation_headers: [false, true], + expected_no_cache_headers: [false, false], + expected_max_age_headers: [false, true], + }, + { + name: 'RequestCache "no-cache" mode revalidates fresh responses found in the cache', + state: "fresh", + request_cache: ["default", "no-cache"], + expected_validation_headers: [false, true], + expected_no_cache_headers: [false, false], + expected_max_age_headers: [false, true], + }, + { + name: 'RequestCache "force-cache" mode checks the cache for previously cached content and avoid revalidation for stale responses', + state: "stale", + request_cache: ["default", "force-cache"], + expected_validation_headers: [false], + expected_no_cache_headers: [false], + }, + { + name: 'RequestCache "force-cache" mode checks the cache for previously cached content and avoid revalidation for fresh responses', + state: "fresh", + request_cache: ["default", "force-cache"], + expected_validation_headers: [false], + expected_no_cache_headers: [false], + }, + { + name: 'RequestCache "force-cache" mode checks the cache for previously cached content and goes to the network if a cached response is not found', + state: "stale", + request_cache: ["force-cache"], + expected_validation_headers: [false], + expected_no_cache_headers: [false], + }, + { + name: 'RequestCache "force-cache" mode checks the cache for previously cached content and goes to the network if a cached response is not found', + state: "fresh", + request_cache: ["force-cache"], + expected_validation_headers: [false], + expected_no_cache_headers: [false], + }, + { + name: 'RequestCache "force-cache" mode checks the cache for previously cached content and goes to the network if a cached response would vary', + state: "stale", + vary: "*", + request_cache: ["default", "force-cache"], + expected_validation_headers: [false, true], + expected_no_cache_headers: [false, false], + }, + { + name: 'RequestCache "force-cache" mode checks the cache for previously cached content and goes to the network if a cached response would vary', + state: "fresh", + vary: "*", + request_cache: ["default", "force-cache"], + expected_validation_headers: [false, true], + expected_no_cache_headers: [false, false], + }, + { + name: 'RequestCache "force-cache" stores the response in the cache if it goes to the network', + state: "stale", + request_cache: ["force-cache", "default"], + expected_validation_headers: [false, true], + expected_no_cache_headers: [false, false], + }, + { + name: 'RequestCache "force-cache" stores the response in the cache if it goes to the network', + state: "fresh", + request_cache: ["force-cache", "default"], + expected_validation_headers: [false], + expected_no_cache_headers: [false], + }, + { + name: 'RequestCache "only-if-cached" mode checks the cache for previously cached content and avoids revalidation for stale responses', + state: "stale", + request_cache: ["default", "only-if-cached"], + expected_validation_headers: [false], + expected_no_cache_headers: [false] + }, + { + name: 'RequestCache "only-if-cached" mode checks the cache for previously cached content and avoids revalidation for fresh responses', + state: "fresh", + request_cache: ["default", "only-if-cached"], + expected_validation_headers: [false], + expected_no_cache_headers: [false] + }, + { + name: 'RequestCache "only-if-cached" mode checks the cache for previously cached content and does not go to the network if a cached response is not found', + state: "fresh", + request_cache: ["only-if-cached"], + response: ["error"], + expected_validation_headers: [], + expected_no_cache_headers: [] + }, + { + name: 'RequestCache "only-if-cached" (with "same-origin") uses cached same-origin redirects to same-origin content', + state: "fresh", + request_cache: ["default", "only-if-cached"], + redirect: "same-origin", + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, false], + }, + { + name: 'RequestCache "only-if-cached" (with "same-origin") uses cached same-origin redirects to same-origin content', + state: "stale", + request_cache: ["default", "only-if-cached"], + redirect: "same-origin", + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, false], + }, + { + name: 'RequestCache "only-if-cached" (with "same-origin") does not follow redirects across origins and rejects', + state: "fresh", + request_cache: ["default", "only-if-cached"], + redirect: "cross-origin", + response: [null, "error"], + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, false], + }, + { + name: 'RequestCache "only-if-cached" (with "same-origin") does not follow redirects across origins and rejects', + state: "stale", + request_cache: ["default", "only-if-cached"], + redirect: "cross-origin", + response: [null, "error"], + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, false], + }, + { + name: 'RequestCache "no-store" mode does not check the cache for previously cached content and goes to the network regardless', + state: "stale", + request_cache: ["default", "no-store"], + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, true], + }, + { + name: 'RequestCache "no-store" mode does not check the cache for previously cached content and goes to the network regardless', + state: "fresh", + request_cache: ["default", "no-store"], + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, true], + }, + { + name: 'RequestCache "no-store" mode does not store the response in the cache', + state: "stale", + request_cache: ["no-store", "default"], + expected_validation_headers: [false, false], + expected_no_cache_headers: [true, false], + }, + { + name: 'RequestCache "no-store" mode does not store the response in the cache', + state: "fresh", + request_cache: ["no-store", "default"], + expected_validation_headers: [false, false], + expected_no_cache_headers: [true, false], + }, + { + name: 'RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store"', + state: "stale", + request_cache: ["default", "default"], + request_headers: [{}, {"If-Modified-Since": now.toGMTString()}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, true], + }, + { + name: 'RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store"', + state: "fresh", + request_cache: ["default", "default"], + request_headers: [{}, {"If-Modified-Since": now.toGMTString()}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, true], + }, + { + name: 'RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store"', + state: "stale", + request_cache: ["default", "default"], + request_headers: [{"If-Modified-Since": now.toGMTString()}, {}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [true, false], + }, + { + name: 'RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store"', + state: "fresh", + request_cache: ["default", "default"], + request_headers: [{"If-Modified-Since": now.toGMTString()}, {}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [true, false], + }, + { + name: 'RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store"', + state: "stale", + request_cache: ["default", "default"], + request_headers: [{}, {"If-None-Match": '"foo"'}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, true], + }, + { + name: 'RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store"', + state: "fresh", + request_cache: ["default", "default"], + request_headers: [{}, {"If-None-Match": '"foo"'}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, true], + }, + { + name: 'RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store"', + state: "stale", + request_cache: ["default", "default"], + request_headers: [{"If-None-Match": '"foo"'}, {}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [true, false], + }, + { + name: 'RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store"', + state: "fresh", + request_cache: ["default", "default"], + request_headers: [{"If-None-Match": '"foo"'}, {}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [true, false], + }, + { + name: 'RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store"', + state: "stale", + request_cache: ["default", "default"], + request_headers: [{}, {"If-Unmodified-Since": now.toGMTString()}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, true], + }, + { + name: 'RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store"', + state: "fresh", + request_cache: ["default", "default"], + request_headers: [{}, {"If-Unmodified-Since": now.toGMTString()}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, true], + }, + { + name: 'RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store"', + state: "stale", + request_cache: ["default", "default"], + request_headers: [{"If-Unmodified-Since": now.toGMTString()}, {}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [true, false], + }, + { + name: 'RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store"', + state: "fresh", + request_cache: ["default", "default"], + request_headers: [{"If-Unmodified-Since": now.toGMTString()}, {}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [true, false], + }, + { + name: 'RequestCache "default" mode with an If-Match header is treated similarly to "no-store"', + state: "stale", + request_cache: ["default", "default"], + request_headers: [{}, {"If-Match": '"foo"'}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, true], + }, + { + name: 'RequestCache "default" mode with an If-Match header is treated similarly to "no-store"', + state: "fresh", + request_cache: ["default", "default"], + request_headers: [{}, {"If-Match": '"foo"'}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, true], + }, + { + name: 'RequestCache "default" mode with an If-Match header is treated similarly to "no-store"', + state: "stale", + request_cache: ["default", "default"], + request_headers: [{"If-Match": '"foo"'}, {}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [true, false], + }, + { + name: 'RequestCache "default" mode with an If-Match header is treated similarly to "no-store"', + state: "fresh", + request_cache: ["default", "default"], + request_headers: [{"If-Match": '"foo"'}, {}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [true, false], + }, + { + name: 'RequestCache "default" mode with an If-Range header is treated similarly to "no-store"', + state: "stale", + request_cache: ["default", "default"], + request_headers: [{}, {"If-Range": '"foo"'}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, true], + }, + { + name: 'RequestCache "default" mode with an If-Range header is treated similarly to "no-store"', + state: "fresh", + request_cache: ["default", "default"], + request_headers: [{}, {"If-Range": '"foo"'}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, true], + }, + { + name: 'RequestCache "default" mode with an If-Range header is treated similarly to "no-store"', + state: "stale", + request_cache: ["default", "default"], + request_headers: [{"If-Range": '"foo"'}, {}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [true, false], + }, + { + name: 'RequestCache "default" mode with an If-Range header is treated similarly to "no-store"', + state: "fresh", + request_cache: ["default", "default"], + request_headers: [{"If-Range": '"foo"'}, {}], + expected_validation_headers: [false, false], + expected_no_cache_headers: [true, false], + }, + { + name: 'Responses with the "Cache-Control: no-store" header are not stored in the cache', + state: "stale", + cache_control: "no-store", + request_cache: ["default", "default"], + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, false], + }, + { + name: 'Responses with the "Cache-Control: no-store" header are not stored in the cache', + state: "fresh", + cache_control: "no-store", + request_cache: ["default", "default"], + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, false], + }, + { + name: 'RequestCache "reload" mode does not check the cache for previously cached content and goes to the network regardless', + state: "stale", + request_cache: ["default", "reload"], + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, true], + }, + { + name: 'RequestCache "reload" mode does not check the cache for previously cached content and goes to the network regardless', + state: "fresh", + request_cache: ["default", "reload"], + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, true], + }, + { + name: 'RequestCache "reload" mode does store the response in the cache', + state: "stale", + request_cache: ["reload", "default"], + expected_validation_headers: [false, true], + expected_no_cache_headers: [true, false], + }, + { + name: 'RequestCache "reload" mode does store the response in the cache', + state: "fresh", + request_cache: ["reload", "default"], + expected_validation_headers: [false], + expected_no_cache_headers: [true], + }, + { + name: 'RequestCache "reload" mode does store the response in the cache even if a previous response is already stored', + state: "stale", + request_cache: ["default", "reload", "default"], + expected_validation_headers: [false, false, true], + expected_no_cache_headers: [false, true, false], + }, + { + name: 'RequestCache "reload" mode does store the response in the cache even if a previous response is already stored', + state: "fresh", + request_cache: ["default", "reload", "default"], + expected_validation_headers: [false, false], + expected_no_cache_headers: [false, true], + }, + ]; + function base_path() { + return location.pathname.replace(/\/[^\/]*$/, '/'); + } + function make_url(uuid, id, value, content, info) { + var dates = { + fresh: new Date(now.getFullYear() + 1, now.getMonth(), now.getDay()).toGMTString(), + stale: new Date(now.getFullYear() - 1, now.getMonth(), now.getDay()).toGMTString(), + }; + var vary = ""; + if ("vary" in info) { + vary = "&vary=" + info.vary; + } + var cache_control = ""; + if ("cache_control" in info) { + cache_control = "&cache_control=" + info.cache_control; + } + var redirect = ""; + + var ignore_request_headers = ""; + if ("request_headers" in info) { + // Ignore the request headers that we send since they may be synthesized by the test. + ignore_request_headers = "&ignore"; + } + var url_sans_redirect = "resources/cache.py?token=" + uuid + + "&content=" + content + + "&" + id + "=" + value + + "&expires=" + dates[info.state] + + vary + cache_control + ignore_request_headers; + // If there's a redirect, the target is the script without any redirect at + // either the same domain or a different domain. + if ("redirect" in info) { + var host_info = get_host_info(); + var origin; + switch (info.redirect) { + case "same-origin": + origin = host_info['HTTP_ORIGIN']; + break; + case "cross-origin": + origin = host_info['HTTP_REMOTE_ORIGIN']; + break; + } + var redirected_url = origin + base_path() + url_sans_redirect; + return url_sans_redirect + "&redirect=" + encodeURIComponent(redirected_url); + } else { + return url_sans_redirect; + } + } + function expected_status(type, identifier, init) { + if (type == "date" && + init.headers && + init.headers["If-Modified-Since"] == identifier) { + // The server will respond with a 304 in this case. + return [304, "Not Modified"]; + } + return [200, "OK"]; + } + function expected_response_text(type, identifier, init, content) { + if (type == "date" && + init.headers && + init.headers["If-Modified-Since"] == identifier) { + // The server will respond with a 304 in this case. + return ""; + } + return content; + } + function server_state(uuid) { + return fetch("resources/cache.py?querystate&token=" + uuid) + .then(function(response) { + return response.text(); + }).then(function(text) { + // null will be returned if the server never received any requests + // for the given uuid. Normalize that to an empty list consistent + // with our representation. + return JSON.parse(text) || []; + }); + } + function make_test(type, info) { + return function(test) { + var uuid = token(); + var identifier = (type == "tag" ? Math.random() : now.toGMTString()); + var content = Math.random().toString(); + var url = make_url(uuid, type, identifier, content, info); + var fetch_functions = []; + for (var i = 0; i < info.request_cache.length; ++i) { + fetch_functions.push(function(idx) { + var init = {cache: info.request_cache[idx]}; + if ("request_headers" in info) { + init.headers = info.request_headers[idx]; + } + if (init.cache === "only-if-cached") { + // only-if-cached requires we use same-origin mode. + init.mode = "same-origin"; + } + return fetch(url, init) + .then(function(response) { + if ("response" in info && info.response[idx] === "error") { + assert_true(false, "fetch should have been an error"); + return; + } + assert_array_equals([response.status, response.statusText], + expected_status(type, identifier, init)); + return response.text(); + }).then(function(text) { + assert_equals(text, expected_response_text(type, identifier, init, content)); + }, function(reason) { + if ("response" in info && info.response[idx] === "error") { + assert_throws(new TypeError(), function() { throw reason; }); + } else { + throw reason; + } + }); + }); + } + var i = 0; + function run_next_step() { + if (fetch_functions.length) { + return fetch_functions.shift()(i++) + .then(run_next_step); + } else { + return Promise.resolve(); + } + } + return run_next_step() + .then(function() { + // Now, query the server state + return server_state(uuid); + }).then(function(state) { + var expectedState = []; + info.expected_validation_headers.forEach(function (validate) { + if (validate) { + if (type == "tag") { + expectedState.push({"If-None-Match": '"' + identifier + '"'}); + } else { + expectedState.push({"If-Modified-Since": identifier}); + } + } else { + expectedState.push({}); + } + }); + for (var i = 0; i < info.expected_no_cache_headers.length; ++i) { + if (info.expected_no_cache_headers[i]) { + expectedState[i]["Pragma"] = "no-cache"; + expectedState[i]["Cache-Control"] = "no-cache"; + } + } + if ("expected_max_age_headers" in info) { + for (var i = 0; i < info.expected_max_age_headers.length; ++i) { + if (info.expected_max_age_headers[i]) { + expectedState[i]["Cache-Control"] = "max-age=0"; + } + } + } + assert_equals(state.length, expectedState.length); + for (var i = 0; i < state.length; ++i) { + for (var header in state[i]) { + assert_equals(state[i][header], expectedState[i][header]); + delete expectedState[i][header]; + } + for (var header in expectedState[i]) { + assert_false(header in state[i]); + } + } + }); + }; + } + tests.forEach(function(info) { + promise_test(make_test("tag", info), info.name + " with Etag and " + info.state + " response"); + promise_test(make_test("date", info), info.name + " with date and " + info.state + " response"); + }); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/fetch/api/request/request-clone.sub.html b/testing/web-platform/tests/fetch/api/request/request-clone.sub.html new file mode 100644 index 000000000..c690bb3dc --- /dev/null +++ b/testing/web-platform/tests/fetch/api/request/request-clone.sub.html @@ -0,0 +1,63 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title>Request clone</title> + <meta name="help" href="https://fetch.spec.whatwg.org/#request"> + <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="../resources/utils.js"></script> + </head> + <body> + <script> + var headers = new Headers({"name" : "value"}); + var emptyHeaders = new Headers(); + + var initValuesDict = {"method" : "POST", + "referrer" : "http://{{host}}:{{ports[http][0]}}/", + "referrerPolicy" : "origin", + "mode" : "same-origin", + "credentials" : "include", + "cache" : "no-cache", + "redirect" : "error", + "integrity" : "Request's Integrity", + "headers" : headers, + "body" : "Request's body" + }; + + var expectedInitialized = {"method" : "POST", + "referrer" : "http://{{host}}:{{ports[http][0]}}/", + "referrerPolicy" : "origin", + "mode" : "same-origin", + "credentials" : "include", + "cache" : "no-cache", + "redirect" : "error", + "integrity" : "Request's Integrity", + "headers" : headers, + "body" : "Request's body" + }; + + test(function() { + var RequestInitialized = new Request("", initValuesDict); + var requestToCheck = RequestInitialized.clone(); + checkRequest(requestToCheck, expectedInitialized); + }, "Check cloning a request"); + + test(function() { + var initialRequest = new Request("", {"headers" : new Headers({"a": "1", "b" : "2"})}); + var request = initialRequest.clone(); + assert_equals(request.headers.get("a"), "1", "cloned request should have header 'a'"); + assert_equals(request.headers.get("b"), "2", "cloned request should have header 'b'"); + + initialRequest.headers.delete("a"); + assert_equals(request.headers.get("a"), "1", "cloned request should still have header 'a'"); + + request.headers.delete("a"); + assert_equals(initialRequest.headers.get("b"), "2", "initial request should have header 'b'"); + + }, "Check cloning a request copies the headers"); + + </script> + </body> +</html> diff --git a/testing/web-platform/tests/fetch/api/request/request-consume-empty.html b/testing/web-platform/tests/fetch/api/request/request-consume-empty.html new file mode 100644 index 000000000..a2bb3e2a9 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/request/request-consume-empty.html @@ -0,0 +1,103 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title>Request consume empty bodies</title> + <meta name="help" href="https://fetch.spec.whatwg.org/#request"> + <meta name="help" href="https://fetch.spec.whatwg.org/#body-mixin"> + <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <script> + function checkBodyText(request) { + return request.text().then(function(bodyAsText) { + assert_equals(bodyAsText, "", "Resolved value should be empty"); + assert_false(request.bodyUsed); + }); + } + + function checkBodyBlob(request) { + return request.blob().then(function(bodyAsBlob) { + var promise = new Promise(function(resolve, reject) { + var reader = new FileReader(); + reader.onload = function(evt) { + resolve(reader.result) + }; + reader.onerror = function() { + reject("Blob's reader failed"); + }; + reader.readAsText(bodyAsBlob); + }); + return promise.then(function(body) { + assert_equals(body, "", "Resolved value should be empty"); + assert_false(request.bodyUsed); + }); + }); + } + + function checkBodyArrayBuffer(request) { + return request.arrayBuffer().then(function(bodyAsArrayBuffer) { + assert_equals(bodyAsArrayBuffer.byteLength, 0, "Resolved value should be empty"); + assert_false(request.bodyUsed); + }); + } + + function checkBodyJSON(request) { + return request.json().then( + function(bodyAsJSON) { + assert_unreached("JSON parsing should fail"); + }, + function() { + assert_false(request.bodyUsed); + }); + } + + function checkBodyFormData(request) { + return request.formData().then(function(bodyAsFormData) { + assert_true(bodyAsFormData instanceof FormData, "Should receive a FormData"); + assert_false(request.bodyUsed); + }); + } + + function checkRequestWithNoBody(bodyType, checkFunction) { + promise_test(function(test) { + var request = new Request("", {"method": "POST"}); + assert_false(request.bodyUsed); + return checkFunction(request); + }, "Consume request's body as " + bodyType); + } + + var formData = new FormData(); + checkRequestWithNoBody("text", checkBodyText); + checkRequestWithNoBody("blob", checkBodyBlob); + checkRequestWithNoBody("arrayBuffer", checkBodyArrayBuffer); + checkRequestWithNoBody("json", checkBodyJSON); + checkRequestWithNoBody("formData", checkBodyFormData); + + function checkRequestWithEmptyBody(bodyType, body, asText) { + promise_test(function(test) { + var request = new Request("", {"method": "POST", "body": body}); + assert_false(request.bodyUsed, "bodyUsed is false at init"); + if (asText) { + return request.text().then(function(bodyAsString) { + assert_equals(bodyAsString.length, 0, "Resolved value should be empty"); + assert_true(request.bodyUsed, "bodyUsed is true after being consumed"); + }); + } + return request.arrayBuffer().then(function(bodyAsArrayBuffer) { + assert_equals(bodyAsArrayBuffer.byteLength, 0, "Resolved value should be empty"); + assert_true(request.bodyUsed, "bodyUsed is true after being consumed"); + }); + }, "Consume empty " + bodyType + " request body as " + (asText ? "text" : "arrayBuffer")); + } + + // FIXME: Add BufferSource, FormData and URLSearchParams. + checkRequestWithEmptyBody("blob", new Blob([], { "type" : "text/plain" }), false); + checkRequestWithEmptyBody("text", "", false); + checkRequestWithEmptyBody("blob", new Blob([], { "type" : "text/plain" }), true); + checkRequestWithEmptyBody("text", "", true); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/fetch/api/request/request-consume.html b/testing/web-platform/tests/fetch/api/request/request-consume.html new file mode 100644 index 000000000..9ac70412a --- /dev/null +++ b/testing/web-platform/tests/fetch/api/request/request-consume.html @@ -0,0 +1,158 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title>Request consume</title> + <meta name="help" href="https://fetch.spec.whatwg.org/#request"> + <meta name="help" href="https://fetch.spec.whatwg.org/#body-mixin"> + <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="../resources/utils.js"></script> + </head> + <body> + <script> + function checkBodyText(request, expectedBody) { + return request.text().then(function(bodyAsText) { + assert_equals(bodyAsText, expectedBody, "Retrieve and verify request's body"); + assert_true(request.bodyUsed, "body as text: bodyUsed turned true"); + }); + } + + function checkBodyBlob(request, expectedBody, checkContentType) { + return request.blob().then(function(bodyAsBlob) { + if (checkContentType) + assert_equals(bodyAsBlob.type, "text/plain", "Blob body type should be computed from the request Content-Type"); + + var promise = new Promise(function (resolve, reject) { + var reader = new FileReader(); + reader.onload = function(evt) { + resolve(reader.result) + }; + reader.onerror = function() { + reject("Blob's reader failed"); + }; + reader.readAsText(bodyAsBlob); + }); + return promise.then(function(body) { + assert_equals(body, expectedBody, "Retrieve and verify request's body"); + assert_true(request.bodyUsed, "body as blob: bodyUsed turned true"); + }); + }); + } + + function checkBodyArrayBuffer(request, expectedBody) { + return request.arrayBuffer().then(function(bodyAsArrayBuffer) { + validateBufferFromString(bodyAsArrayBuffer, expectedBody, "Retrieve and verify request's body"); + assert_true(request.bodyUsed, "body as arrayBuffer: bodyUsed turned true"); + }); + } + + function checkBodyJSON(request, expectedBody) { + return request.json().then(function(bodyAsJSON) { + var strBody = JSON.stringify(bodyAsJSON) + assert_equals(strBody, expectedBody, "Retrieve and verify request's body"); + assert_true(request.bodyUsed, "body as json: bodyUsed turned true"); + }); + } + + function checkBodyFormData(request, expectedBody) { + return request.formData().then(function(bodyAsFormData) { + assert_true(bodyAsFormData instanceof FormData, "Should receive a FormData"); + assert_true(request.bodyUsed, "body as formData: bodyUsed turned true"); + }); + } + + function checkRequestBody(body, expected, bodyType) { + promise_test(function(test) { + var request = new Request("", {"method": "POST", "body": body, "headers": [["Content-Type", "text/PLAIN"]] }); + assert_false(request.bodyUsed, "bodyUsed is false at init"); + return checkBodyText(request, expected); + }, "Consume " + bodyType + " request's body as text"); + promise_test(function(test) { + var request = new Request("", {"method": "POST", "body": body }); + assert_false(request.bodyUsed, "bodyUsed is false at init"); + return checkBodyBlob(request, expected); + }, "Consume " + bodyType + " request's body as blob"); + promise_test(function(test) { + var request = new Request("", {"method": "POST", "body": body }); + assert_false(request.bodyUsed, "bodyUsed is false at init"); + return checkBodyArrayBuffer(request, expected); + }, "Consume " + bodyType + " request's body as arrayBuffer"); + promise_test(function(test) { + var request = new Request("", {"method": "POST", "body": body }); + assert_false(request.bodyUsed, "bodyUsed is false at init"); + return checkBodyJSON(request, expected); + }, "Consume " + bodyType + " request's body as JSON"); + } + + var textData = JSON.stringify("This is response's body"); + var blob = new Blob([textData], { "type" : "text/plain" }); + + checkRequestBody(textData, textData, "String"); + + var string = "\"123456\""; + function getArrayBuffer() { + var arrayBuffer = new ArrayBuffer(8); + var int8Array = new Int8Array(arrayBuffer); + for (var cptr = 0; cptr < 8; cptr++) + int8Array[cptr] = string.charCodeAt(cptr); + return arrayBuffer; + } + + function getArrayBufferWithZeros() { + var arrayBuffer = new ArrayBuffer(10); + var int8Array = new Int8Array(arrayBuffer); + for (var cptr = 0; cptr < 8; cptr++) + int8Array[cptr + 1] = string.charCodeAt(cptr); + return arrayBuffer; + } + + checkRequestBody(getArrayBuffer(), string, "ArrayBuffer"); + checkRequestBody(new Uint8Array(getArrayBuffer()), string, "Uint8Array"); + checkRequestBody(new Int8Array(getArrayBufferWithZeros(), 1, 8), string, "Int8Array"); + checkRequestBody(new Float32Array(getArrayBuffer()), string, "Float32Array"); + checkRequestBody(new DataView(getArrayBufferWithZeros(), 1, 8), string, "DataView"); + + promise_test(function(test) { + var formData = new FormData(); + formData.append("name", "value") + var request = new Request("", {"method": "POST", "body": formData }); + assert_false(request.bodyUsed, "bodyUsed is false at init"); + return checkBodyFormData(request, formData); + }, "Consume FormData request's body as FormData"); + + function checkBlobResponseBody(blobBody, blobData, bodyType, checkFunction) { + promise_test(function(test) { + var response = new Response(blobBody); + assert_false(response.bodyUsed, "bodyUsed is false at init"); + return checkFunction(response, blobData); + }, "Consume blob response's body as " + bodyType); + } + + checkBlobResponseBody(blob, textData, "blob", checkBodyBlob); + checkBlobResponseBody(blob, textData, "text", checkBodyText); + checkBlobResponseBody(blob, textData, "json", checkBodyJSON); + checkBlobResponseBody(blob, textData, "arrayBuffer", checkBodyArrayBuffer); + checkBlobResponseBody(new Blob([""]), "", "blob (empty blob as input)", checkBodyBlob); + + var goodJSONValues = ["null", "1", "true", "\"string\""]; + goodJSONValues.forEach(function(value) { + promise_test(function(test) { + var request = new Request("", {"method": "POST", "body": value}); + return request.json().then(function(v) { + assert_equals(v, JSON.parse(value)); + }); + }, "Consume JSON from text: '" + JSON.stringify(value) + "'"); + }); + + var badJSONValues = ["undefined", "{", "a", "["]; + badJSONValues.forEach(function(value) { + promise_test(function(test) { + var request = new Request("", {"method": "POST", "body": value}); + return promise_rejects(test, new SyntaxError(), request.json()); + }, "Trying to consume bad JSON text as JSON: '" + value + "'"); + }); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/fetch/api/request/request-disturbed.html b/testing/web-platform/tests/fetch/api/request/request-disturbed.html new file mode 100644 index 000000000..dbc218e50 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/request/request-disturbed.html @@ -0,0 +1,77 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title>Request disturbed</title> + <meta name="help" href="https://fetch.spec.whatwg.org/#request"> + <meta name="help" href="https://fetch.spec.whatwg.org/#body-mixin"> + <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <script> + var initValuesDict = {"method" : "POST", + "body" : "Request's body" + }; + + var noBodyConsumed = new Request(""); + noBodyConsumed.blob(); + var bodyConsumed = new Request("", initValuesDict); + bodyConsumed.blob(); + + test(function() { + assert_false(noBodyConsumed.bodyUsed , "bodyUsed is false when request is not disturbed"); + try { + noBodyConsumed.clone(); + } catch (e) { + assert_unreached("Can use request not disturbed for creating or cloning request"); + } + }, "Request without body cannot be disturbed"); + + test(function() { + assert_true(bodyConsumed.bodyUsed , "bodyUsed is true when request is disturbed"); + assert_throws(new TypeError(), function() { bodyConsumed.clone(); }); + }, "Check cloning a disturbed request"); + + test(function() { + assert_true(bodyConsumed.bodyUsed , "bodyUsed is true when request is disturbed"); + assert_throws(new TypeError(), function() { new Request(bodyConsumed); }); + }, "Check creating a new request from a disturbed request"); + + test(function() { + var bodyRequest = new Request("", initValuesDict); + assert_false(bodyRequest.bodyUsed , "bodyUsed is false when request is not disturbed"); + var requestFromRequest = new Request(bodyRequest); + assert_true(bodyRequest.bodyUsed , "bodyUsed is true when request is disturbed"); + }, "Input request used for creating new request became disturbed"); + + promise_test(function(test) { + assert_true(bodyConsumed.bodyUsed , "bodyUsed is true when request is disturbed"); + return promise_rejects(test, new TypeError(), bodyConsumed.blob()); + }, "Check consuming a disturbed request"); + + test(function() { + var req = new Request(URL, {method: 'POST', body: 'hello'}); + assert_false(req.bodyUsed, + 'Request should not be flagged as used if it has not been ' + + 'consumed.'); + assert_throws(new TypeError(), + function() { new Request(req, {method: 'GET'}); }, + 'A get request may not have body.'); + + assert_false(req.bodyUsed, 'After the GET case'); + + assert_throws(new TypeError(), + function() { new Request(req, {method: 'CONNECT'}); }, + 'Request() with a forbidden method must throw.'); + + assert_false(req.bodyUsed, 'After the forbidden method case'); + + var req2 = new Request(req); + assert_true(req.bodyUsed, + 'Request should be flagged as used if it has been consumed.'); + }, 'Request construction failure should not set "bodyUsed"'); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/fetch/api/request/request-error.html b/testing/web-platform/tests/fetch/api/request/request-error.html new file mode 100644 index 000000000..c87429e76 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/request/request-error.html @@ -0,0 +1,110 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title>Request error</title> + <meta name="help" href="https://fetch.spec.whatwg.org/#request"> + <meta name="help" href="https://fetch.spec.whatwg.org/#body-mixin"> + <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <script> + test(function() { + assert_throws(new TypeError() , function() { new Request("", {"window" : "http://test.url"}); }, + "Expect TypeError exception"); + },"RequestInit's window is not null"); + + test(function() { + assert_throws(new TypeError() , function() { new Request("http://:not a valid URL"); }, + "Expect TypeError exception"); + },"Input URL is not valid") + + test(function() { + assert_throws(new TypeError() , function() { new Request("http://user:pass@test.url"); }, + "Expect TypeError exception"); + },"Input URL has credentials"); + + test(function() { + assert_throws(new TypeError() , function() { new Request("", {"mode" : "navigate"}); }, + "Expect TypeError exception"); + },"RequestInit's mode is navigate"); + + test(function() { + assert_throws(new TypeError() , function() { new Request("", {"referrer" : "http://:not a valid URL"}); }, + "Expect TypeError exception"); + },"RequestInit's referrer is invalid"); + + test(function() { + assert_throws(new TypeError() , function() { new Request("", {"referrer" : "http://test.url"}); }, + "Expect TypeError exception"); + },"RequestInit's referrer has invalid origin") + + test(function() { + assert_throws(new TypeError() , function() { new Request("", {"method" : "IN VALID"}); }, + "Expect TypeError exception"); + }, "RequestInit's method is invalid"); + + test(function() { + assert_throws(new TypeError() , function() { new Request("", {"method" : "TRACE"}); }, + "Expect TypeError exception"); + }, "RequestInit's method is forbidden"); + + test(function() { + assert_throws(new TypeError() , function() { new Request("", {"mode" : "no-cors", "method" : "PUT"}); }, + "Expect TypeError exception"); + },"RequestInit's mode is no-cors and method is not simple"); + + test(function() { + assert_throws(new TypeError() , + function() { new Request("", {"mode" : "no-cors", "integrity" : "not an empty string"}); }, + "Expect TypeError exception"); + },"RequestInit's mode is no-cors and integrity is not empty"); + + test(function() { + assert_throws(new TypeError() , + function() { new Request("", {"mode" : "cors", "cache" : "only-if-cached"}); }, + "Expect TypeError exception"); + },"RequestInit's cache mode is only-if-cached and mode is not same-origin"); + + test(function() { + var initialHeaders = new Headers([["Content-Type", "potato"]]); + var initialRequest = new Request("", {"headers" : initialHeaders}); + var request = new Request(initialRequest); + assert_equals(request.headers.get("Content-Type"), "potato"); + }, "Request should get its content-type from the init request"); + + test(function() { + var initialHeaders = new Headers([["Content-Type", "potato"]]); + var initialRequest = new Request("", {"headers" : initialHeaders}); + var headers = new Headers([]); + var request = new Request(initialRequest, {"headers" : headers}); + assert_false(request.headers.has("Content-Type")); + }, "Request should not get its content-type from the init request if init headers are provided"); + + test(function() { + var initialHeaders = new Headers([["Content-Type-Extra", "potato"]]); + var initialRequest = new Request("", {"headers" : initialHeaders, "body" : "this is my plate", "method" : "POST"}); + var request = new Request(initialRequest); + assert_equals(request.headers.get("Content-Type"), "text/plain;charset=UTF-8"); + }, "Request should get its content-type from the body if none is provided"); + + test(function() { + var initialHeaders = new Headers([["Content-Type", "potato"]]); + var initialRequest = new Request("", {"headers" : initialHeaders, "body" : "this is my plate", "method" : "POST"}); + var request = new Request(initialRequest); + assert_equals(request.headers.get("Content-Type"), "potato"); + }, "Request should get its content-type from init headers if one is provided"); + + var parameters = ["referrerPolicy", "mode", "credentials", "cache", "redirect"]; + parameters.forEach(function(parameter) { + test(function() { + var options = { }; + options[parameter] = "BAD"; + assert_throws(new TypeError(), function() { new Request("", options); }); + },"Bad " + parameter +" init parameter value"); + }); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/fetch/api/request/request-headers.html b/testing/web-platform/tests/fetch/api/request/request-headers.html new file mode 100644 index 000000000..1d54728b7 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/request/request-headers.html @@ -0,0 +1,173 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title>Request Headers</title> + <meta name="help" href="https://fetch.spec.whatwg.org/#request"> + <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <script> + var validRequestHeaders = [ + ["Content-Type", "OK"], + ["Potato", "OK"], + ["proxy", "OK"], + ["proxya", "OK"], + ["sec", "OK"], + ["secb", "OK"], + ]; + var invalidRequestHeaders = [ + ["Accept-Charset", "KO"], + ["accept-charset", "KO"], + ["ACCEPT-ENCODING", "KO"], + ["Accept-Encoding", "KO"], + ["Access-Control-Request-Headers", "KO"], + ["Access-Control-Request-Method", "KO"], + ["Connection", "KO"], + ["Content-Length", "KO"], + ["Cookie", "KO"], + ["Cookie2", "KO"], + ["Date", "KO"], + ["DNT", "KO"], + ["Expect", "KO"], + ["Host", "KO"], + ["Keep-Alive", "KO"], + ["Origin", "KO"], + ["Referer", "KO"], + ["TE", "KO"], + ["Trailer", "KO"], + ["Transfer-Encoding", "KO"], + ["Upgrade", "KO"], + ["Via", "KO"], + ["Proxy-", "KO"], + ["proxy-a", "KO"], + ["Sec-", "KO"], + ["sec-b", "KO"], + ]; + + var validRequestNoCorsHeaders = [ + ["Accept", "OK"], + ["Accept-Language", "OK"], + ["content-language", "OK"], + ["content-type", "application/x-www-form-urlencoded"], + ["content-type", "application/x-www-form-urlencoded;charset=UTF-8"], + ["content-type", "multipart/form-data"], + ["content-type", "multipart/form-data;charset=UTF-8"], + ["content-TYPE", "text/plain"], + ["CONTENT-type", "text/plain;charset=UTF-8"], + ]; + var invalidRequestNoCorsHeaders = [ + ["Content-Type", "KO"], + ["Potato", "KO"], + ["proxy", "KO"], + ["proxya", "KO"], + ["sec", "KO"], + ["secb", "KO"], + ]; + + validRequestHeaders.forEach(function(header) { + test(function() { + var request = new Request(""); + request.headers.set(header[0], header[1]); + assert_equals(request.headers.get(header[0]), header[1]); + }, "Adding valid request header \"" + header[0] + ": " + header[1] + "\""); + }); + invalidRequestHeaders.forEach(function(header) { + test(function() { + var request = new Request(""); + request.headers.set(header[0], header[1]); + assert_equals(request.headers.get(header[0]), null); + }, "Adding invalid request header \"" + header[0] + ": " + header[1] + "\""); + }); + + validRequestNoCorsHeaders.forEach(function(header) { + test(function() { + var requestNoCors = new Request("", {"mode": "no-cors"}); + requestNoCors.headers.set(header[0], header[1]); + assert_equals(requestNoCors.headers.get(header[0]), header[1]); + }, "Adding valid no-cors request header \"" + header[0] + ": " + header[1] + "\""); + }); + invalidRequestNoCorsHeaders.forEach(function(header) { + test(function() { + var requestNoCors = new Request("", {"mode": "no-cors"}); + requestNoCors.headers.set(header[0], header[1]); + assert_equals(requestNoCors.headers.get(header[0]), null); + }, "Adding invalid no-cors request header \"" + header[0] + ": " + header[1] + "\""); + }); + + test(function() { + var headers = new Headers([["Cookie2", "potato"]]); + var request = new Request("", {"headers": headers}); + assert_equals(request.headers.get("Cookie2"), null); + }, "Check that request constructor is filtering headers provided as init parameter"); + + test(function() { + var headers = new Headers([["Content-Type", "potato"]]); + var request = new Request("", {"headers": headers, "mode": "no-cors"}); + assert_equals(request.headers.get("Content-Type"), null); + }, "Check that no-cors request constructor is filtering headers provided as init parameter"); + + test(function() { + var headers = new Headers([["Content-Type", "potato"]]); + var initialRequest = new Request("", {"headers": headers}); + var request = new Request(initialRequest, {"mode": "no-cors"}); + assert_equals(request.headers.get("Content-Type"), null); + }, "Check that no-cors request constructor is filtering headers provided as part of request parameter"); + + test(function() { + var initialHeaders = new Headers([["Content-Type", "potato"]]); + var initialRequest = new Request("", {"headers" : initialHeaders}); + var request = new Request(initialRequest); + assert_equals(request.headers.get("Content-Type"), "potato"); + }, "Request should get its content-type from the init request"); + + test(function() { + var initialHeaders = new Headers([["Content-Type", "potato"]]); + var initialRequest = new Request("", {"headers" : initialHeaders}); + var headers = new Headers([]); + var request = new Request(initialRequest, {"headers" : headers}); + assert_false(request.headers.has("Content-Type")); + }, "Request should not get its content-type from the init request if init headers are provided"); + + test(function() { + var initialHeaders = new Headers([["Content-Type-Extra", "potato"]]); + var initialRequest = new Request("", {"headers" : initialHeaders, "body" : "this is my plate", "method" : "POST"}); + var request = new Request(initialRequest); + assert_equals(request.headers.get("Content-Type"), "text/plain;charset=UTF-8"); + }, "Request should get its content-type from the body if none is provided"); + + test(function() { + var initialHeaders = new Headers([["Content-Type", "potato"]]); + var initialRequest = new Request("", {"headers" : initialHeaders, "body" : "this is my plate", "method" : "POST"}); + var request = new Request(initialRequest); + assert_equals(request.headers.get("Content-Type"), "potato"); + }, "Request should get its content-type from init headers if one is provided"); + + test(function() { + var array = [["hello", "worldAHH"]]; + var object = {"hello": 'worldOOH'}; + var headers = new Headers(array); + + assert_equals(headers.get("hello"), "worldAHH"); + + var request1 = new Request("", {"headers": headers}); + var request2 = new Request("", {"headers": array}); + var request3 = new Request("", {"headers": object}); + + assert_equals(request1.headers.get("hello"), "worldAHH"); + assert_equals(request2.headers.get("hello"), "worldAHH"); + assert_equals(request3.headers.get("hello"), "worldOOH"); + }, "Testing request header creations with various objects"); + + promise_test(function(test) { + var request = new Request("", {"headers" : [["Content-Type", ""]], "body" : "this is my plate", "method" : "POST"}); + return request.blob().then(function(blob) { + assert_equals(blob.type, "", "Blob type should be the empty string"); + }); + }, "Testing empty Request Content-Type header"); + + </script> + </body> +</html> diff --git a/testing/web-platform/tests/fetch/api/request/request-idl.html b/testing/web-platform/tests/fetch/api/request/request-idl.html new file mode 100644 index 000000000..4f1590c75 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/request/request-idl.html @@ -0,0 +1,86 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title>Request idl interface</title> + <meta name="help" href="https://fetch.spec.whatwg.org/#response"> + <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/WebIDLParser.js"></script> + <script src="/resources/idlharness.js"></script> + </head> + <body> + <script id="body-idl" type="text/plain"> + typedef any JSON; + typedef (Blob or BufferSource or FormData or URLSearchParams or USVString) BodyInit; + + [NoInterfaceObject, + Exposed=(Window,Worker)] + interface Body { + readonly attribute boolean bodyUsed; + [NewObject] Promise<ArrayBuffer> arrayBuffer(); + [NewObject] Promise<Blob> blob(); + [NewObject] Promise<FormData> formData(); + [NewObject] Promise<JSON> json(); + [NewObject] Promise<USVString> text(); + }; + </script> + <script id="request-idl" type="text/plain"> + typedef (Request or USVString) RequestInfo; + + [Constructor(RequestInfo input, optional RequestInit init), + Exposed=(Window,Worker)] + interface Request { + readonly attribute ByteString method; + readonly attribute USVString url; + [SameObject] readonly attribute Headers headers; + + readonly attribute RequestType type; + readonly attribute RequestDestination destination; + readonly attribute USVString referrer; + readonly attribute ReferrerPolicy referrerPolicy; + readonly attribute RequestMode mode; + readonly attribute RequestCredentials credentials; + readonly attribute RequestCache cache; + readonly attribute RequestRedirect redirect; + readonly attribute DOMString integrity; + + [NewObject] Request clone(); + }; + Request implements Body; + + dictionary RequestInit { + ByteString method; + HeadersInit headers; + BodyInit? body; + USVString referrer; + ReferrerPolicy referrerPolicy; + RequestMode mode; + RequestCredentials credentials; + RequestCache cache; + RequestRedirect redirect; + DOMString integrity; + any window; // can only be set to null + }; + + enum RequestType { "", "audio", "font", "image", "script", "style", "track", "video" }; + enum RequestDestination { "", "document", "sharedworker", "subresource", "unknown", "worker" }; + enum RequestMode { "navigate", "same-origin", "no-cors", "cors" }; + enum RequestCredentials { "omit", "same-origin", "include" }; + enum RequestCache { "default", "no-store", "reload", "no-cache", "force-cache", "only-if-cached" }; + enum RequestRedirect { "follow", "error", "manual" }; + enum ReferrerPolicy { "", "no-referrer", "no-referrer-when-downgrade", "origin-only", "origin-when-cross-origin", "unsafe-url" }; + </script> + <script> + var idlsArray = new IdlArray(); + var idl = document.getElementById("body-idl").innerHTML + idl += document.getElementById("request-idl").innerHTML + + idlsArray.add_idls(idl); + idlsArray.add_untested_idls("interface Headers {};"); + idlsArray.add_objects({ Request: ['new Request("")'] }); + idlsArray.test(); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/fetch/api/request/request-init-001.sub.html b/testing/web-platform/tests/fetch/api/request/request-init-001.sub.html new file mode 100644 index 000000000..b41f6f0fe --- /dev/null +++ b/testing/web-platform/tests/fetch/api/request/request-init-001.sub.html @@ -0,0 +1,92 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title>Request init: simple cases</title> + <meta name="help" href="https://fetch.spec.whatwg.org/#request"> + <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <script> + var methods = {"givenValues" : ["GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS", "head"], + "expectedValues" : ["GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS", "HEAD"] + }; + var referrers = {"givenValues" : ["/relative/ressource", + "http://{{host}}:{{ports[http][0]}}/relative/ressource?query=true#fragment", + "http://{{host}}:{{ports[http][0]}}/", + "about:client", + "" + ], + "expectedValues" : ["http://{{host}}:{{ports[http][0]}}/relative/ressource", + "http://{{host}}:{{ports[http][0]}}/relative/ressource?query=true#fragment", + "http://{{host}}:{{ports[http][0]}}/", + "about:client", + "" + ] + }; + var referrerPolicies = {"givenValues" : [ "", + "no-referrer", + "no-referrer-when-downgrade", + "origin", + "origin-when-cross-origin", + "unsafe-url" + ], + "expectedValues" : ["", + "no-referrer", + "no-referrer-when-downgrade", + "origin", + "origin-when-cross-origin", + "unsafe-url" + ] + }; + var modes = {"givenValues" : ["same-origin", "no-cors", "cors"], + "expectedValues" : ["same-origin", "no-cors", "cors"] + }; + var credentials = {"givenValues" : ["omit", "same-origin", "include"], + "expectedValues" : ["omit", "same-origin", "include"] + }; + var caches = {"givenValues" : [ "default", "no-store", "reload", "no-cache", "force-cache"], + "expectedValues" : [ "default", "no-store", "reload", "no-cache", "force-cache"] + }; + var redirects = {"givenValues" : ["follow", "error", "manual"], + "expectedValues" : ["follow", "error", "manual"] + }; + var integrities = {"givenValues" : ["", "AZERTYUIOP1234567890" ], + "expectedValues" : ["", "AZERTYUIOP1234567890"] + }; + + //there is no getter for window, init's window might be null + var windows = {"givenValues" : [ null ], + "expectedValues" : [undefined] + }; + + var initValuesDict = { "method" : methods, + "referrer" : referrers, + "referrerPolicy" : referrerPolicies, + "mode" : modes, + "credentials" : credentials, + "cache" : caches, + "redirect" : redirects, + "integrity" : integrities, + "window" : windows + }; + + for (var attributeName in initValuesDict) { + var valuesToTest = initValuesDict[attributeName]; + for (var valueIdx in valuesToTest["givenValues"]) { + var givenValue = valuesToTest["givenValues"][valueIdx]; + var expectedValue = valuesToTest["expectedValues"][valueIdx]; + test(function() { + var requestInit = {}; + requestInit[attributeName] = givenValue + var request = new Request("", requestInit); + assert_equals(request[attributeName], expectedValue, + "Expect request's " + attributeName + " is " + expectedValue + " when initialized with " + givenValue); + }, "Check " + attributeName + " init value of " + givenValue + " and associated getter"); + } + } + </script> + </body> +</html> diff --git a/testing/web-platform/tests/fetch/api/request/request-init-002.html b/testing/web-platform/tests/fetch/api/request/request-init-002.html new file mode 100644 index 000000000..5d92b09c5 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/request/request-init-002.html @@ -0,0 +1,71 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title>Request init: headers and body</title> + <meta name="help" href="https://fetch.spec.whatwg.org/#request"> + <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <script> + test(function() { + var headerDict = {"name1": "value1", + "name2": "value2", + "name3": "value3" + }; + var headers = new Headers(headerDict); + var request = new Request("", { "headers" : headers }) + for (var name in headerDict) { + assert_equals(request.headers.get(name), headerDict[name], + "request's headers has " + name + " : " + headerDict[name]); + } + }, "Initialize Request with headers values"); + + function makeRequestInit(body, method) { + return {"method": method, "body": body}; + } + + function checkRequestInit(body, bodyType, expectedTextBody) { + promise_test(function(test) { + var request = new Request("", makeRequestInit(body, "POST")); + if (body) { + assert_throws(new TypeError(), function() { new Request("", makeRequestInit(body, "GET")); }); + assert_throws(new TypeError(), function() { new Request("", makeRequestInit(body, "HEAD")); }); + } else { + new Request("", makeRequestInit(body, "GET")); // should not throw + } + var reqHeaders = request.headers; + var mime = reqHeaders.get("Content-Type"); + assert_true(!body || (mime && mime.search(bodyType) > -1), "Content-Type header should be \"" + bodyType + "\", not \"" + mime + "\""); + return request.text().then(function(bodyAsText) { + //not equals: cannot guess formData exact value + assert_true( bodyAsText.search(expectedTextBody) > -1, "Retrieve and verify request body"); + }); + }, "Initialize Request's body with " + bodyType); + } + + var blob = new Blob(["This is a blob"], {type: "application/octet-binary"}); + var formaData = new FormData(); + formaData.append("name", "value"); + var usvString = "This is a USVString" + + checkRequestInit(undefined, undefined, ""); + checkRequestInit(null, null, ""); + checkRequestInit(blob, "application/octet-binary", "This is a blob"); + checkRequestInit(formaData, "multipart/form-data", "name=\"name\"\r\n\r\nvalue"); + checkRequestInit(usvString, "text/plain;charset=UTF-8", "This is a USVString"); + + // Ensure test does not time out in case of missing URLSearchParams support. + if (window.URLSearchParams) { + var urlSearchParams = new URLSearchParams("name=value"); + checkRequestInit(urlSearchParams, "application/x-www-form-urlencoded;charset=UTF-8", "name=value"); + } else { + promise_test(function(test) { + return Promise.reject("URLSearchParams not supported"); + }, "Initialize Request's body with application/x-www-form-urlencoded;charset=UTF-8"); + } + </script> + </body> +</html> diff --git a/testing/web-platform/tests/fetch/api/request/request-init-003.sub.html b/testing/web-platform/tests/fetch/api/request/request-init-003.sub.html new file mode 100644 index 000000000..8860d6074 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/request/request-init-003.sub.html @@ -0,0 +1,84 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title>Request: init with request or url</title> + <meta name="help" href="https://fetch.spec.whatwg.org/#request"> + <meta name="help" href="https://url.spec.whatwg.org/#concept-url-serializer"> + <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <script src="../resources/utils.js"></script> + <script> + var headers = new Headers( {"name":"value"} ); + var emptyHeaders = new Headers(); + + var initValuesDict = {"method" : "POST", + "referrer" : "http://{{host}}:{{ports[http][0]}}/", + "referrerPolicy" : "origin", + "mode" : "same-origin", + "credentials" : "include", + "cache" : "no-cache", + "redirect" : "error", + "integrity" : "Request's Integrity", + "headers" : headers, + "body" : "Request's body" + }; + + var expectedInitialized = {"method" : "POST", + "referrer" : "http://{{host}}:{{ports[http][0]}}/", + "referrerPolicy" : "origin", + "mode" : "same-origin", + "credentials" : "include", + "cache" : "no-cache", + "redirect" : "error", + "integrity" : "Request's Integrity", + "headers" : headers, + "body" : "Request's body" + }; + + var expectedDefault = {"method" : "GET", + "url" : location.href, + "referrer" : "http://{{host}}:{{ports[http][0]}}/", + "referrerPolicy" : "", + "mode" : "cors", + "credentials" : "omit", + "cache" : "default", + "redirect" : "follow", + "integrity" : "", + "headers" : emptyHeaders + }; + + var requestDefault = new Request(""); + var requestInitialized = new Request("", initValuesDict); + + test(function() { + var requestToCheck = new Request(requestInitialized); + checkRequest(requestToCheck, expectedInitialized); + }, "Check request values when initialized from Request"); + + test(function() { + var requestToCheck = new Request(requestDefault, initValuesDict); + checkRequest(requestToCheck, expectedInitialized); + }, "Check request values when initialized from Request and init values"); + + test(function() { + var url = "http://url.test:1234/path/subpath?query=true"; + url += "#fragment"; + expectedDefault["url"] = url; + var requestToCheck = new Request(url); + checkRequest(requestToCheck, expectedDefault); + }, "Check request values when initialized from url string"); + + test(function() { + var url = "http://url.test:1234/path/subpath?query=true"; + url += "#fragment"; + expectedInitialized["url"] = url; + var requestToCheck = new Request(url , initValuesDict); + checkRequest(requestToCheck, expectedInitialized); + }, "Check request values when initialized from url and init values"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/fetch/api/request/request-structure.html b/testing/web-platform/tests/fetch/api/request/request-structure.html new file mode 100644 index 000000000..cbe6cee99 --- /dev/null +++ b/testing/web-platform/tests/fetch/api/request/request-structure.html @@ -0,0 +1,134 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title>Request structure</title> + <meta name="help" href="https://fetch.spec.whatwg.org/#request"> + <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <script> + var request = new Request(""); + var methods = ["clone", + //Request implements Body + "arrayBuffer", + "blob", + "formData", + "json", + "text" + ]; + var attributes = ["method", + "url", + "headers", + "type", + "destination", + "referrer", + "referrerPolicy", + "mode", + "credentials", + "cache", + "redirect", + "integrity", + //Request implements Body + "bodyUsed" + ]; + + function IsreadOnly(request, attributeToCheck) { + var defaultValue = undefined; + var newValue = undefined; + switch (attributeToCheck) { + case "method": + defaultValue = "GET"; + newValue = "POST"; + break; + + case "url": + //default value is base url + //i.e http://web-platform.test:8000/fetch/api/request-structure.html + newValue = "http://url.test"; + break; + + case "headers": + request.headers = new Headers ({"name":"value"}); + assert_false(request.headers.has("name"), "Headers attribute is read only"); + return; + break; + + case "type": + defaultValue = ""; + newValue = "style"; + break; + + case "destination": + defaultValue = ""; + newValue = "worker"; + break; + + case "referrer": + defaultValue = "about:client"; + newValue = "http://url.test"; + break; + + case "referrerPolicy": + defaultValue = ""; + newValue = "unsafe-url"; + break; + + case "mode": + defaultValue = "cors"; + newValue = "navigate"; + break; + + case "credentials": + defaultValue = "omit"; + newValue = "cors"; + break; + + case "cache": + defaultValue = "default"; + newValue = "reload"; + break; + + case "redirect": + defaultValue = "follow"; + newValue = "manual"; + break; + + case "integrity": + newValue = "CannotWriteIntegrity"; + break; + + case "bodyUsed": + defaultValue = false; + newValue = true; + break; + + default: + return; + } + + request[attributeToCheck] = newValue; + if (defaultValue === undefined) + assert_not_equals(request[attributeToCheck], newValue, "Attribute " + attributeToCheck + " is read only"); + else + assert_equals(request[attributeToCheck], defaultValue, + "Attribute " + attributeToCheck + " is read only. Default value is " + defaultValue); + } + + for (var idx in methods) { + test(function() { + assert_true(methods[idx] in request, "request has " + methods[idx] + " method"); + }, "Request has " + methods[idx] + " method"); + } + + for (var idx in attributes) { + test(function() { + assert_true(attributes[idx] in request, "request has " + attributes[idx] + " attribute"); + IsreadOnly(request, attributes[idx]); + }, "Check " + attributes[idx] + " attribute"); + } + </script> + </body> +</html> diff --git a/testing/web-platform/tests/fetch/api/request/resources/cache.py b/testing/web-platform/tests/fetch/api/request/resources/cache.py new file mode 100644 index 000000000..85265679a --- /dev/null +++ b/testing/web-platform/tests/fetch/api/request/resources/cache.py @@ -0,0 +1,62 @@ +def main(request, response): + token = request.GET.first("token", None) + if "querystate" in request.GET: + from json import JSONEncoder + response.headers.set("Content-Type", "text/plain") + return JSONEncoder().encode(request.server.stash.take(token)) + content = request.GET.first("content", None) + tag = request.GET.first("tag", None) + date = request.GET.first("date", None) + expires = request.GET.first("expires", None) + vary = request.GET.first("vary", None) + cc = request.GET.first("cache_control", None) + redirect = request.GET.first("redirect", None) + inm = request.headers.get("If-None-Match", None) + ims = request.headers.get("If-Modified-Since", None) + pragma = request.headers.get("Pragma", None) + cache_control = request.headers.get("Cache-Control", None) + ignore = "ignore" in request.GET + + server_state = request.server.stash.take(token) + if not server_state: + server_state = [] + state = dict() + if not ignore: + if inm: + state["If-None-Match"] = inm + if ims: + state["If-Modified-Since"] = ims + if pragma: + state["Pragma"] = pragma + if cache_control: + state["Cache-Control"] = cache_control + server_state.append(state) + request.server.stash.put(token, server_state) + + if tag: + response.headers.set("ETag", '"%s"' % tag) + elif date: + response.headers.set("Last-Modified", date) + if expires: + response.headers.set("Expires", expires) + if vary: + response.headers.set("Vary", vary) + if cc: + response.headers.set("Cache-Control", cc) + + # The only-if-cached redirect tests wants CORS to be okay, the other tests + # are all same-origin anyways and don't care. + response.headers.set("Access-Control-Allow-Origin", "*"); + + if redirect: + response.headers.set("Location", redirect); + response.status = (302, "Redirect") + return "" + elif ((inm is not None and inm == tag) or + (ims is not None and ims == date)): + response.status = (304, "Not Modified") + return "" + else: + response.status = (200, "OK") + response.headers.set("Content-Type", "text/plain") + return content |