summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/fetch/api/request
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /testing/web-platform/tests/fetch/api/request
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-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')
-rw-r--r--testing/web-platform/tests/fetch/api/request/request-cache.html626
-rw-r--r--testing/web-platform/tests/fetch/api/request/request-clone.sub.html63
-rw-r--r--testing/web-platform/tests/fetch/api/request/request-consume-empty.html103
-rw-r--r--testing/web-platform/tests/fetch/api/request/request-consume.html158
-rw-r--r--testing/web-platform/tests/fetch/api/request/request-disturbed.html77
-rw-r--r--testing/web-platform/tests/fetch/api/request/request-error.html110
-rw-r--r--testing/web-platform/tests/fetch/api/request/request-headers.html173
-rw-r--r--testing/web-platform/tests/fetch/api/request/request-idl.html86
-rw-r--r--testing/web-platform/tests/fetch/api/request/request-init-001.sub.html92
-rw-r--r--testing/web-platform/tests/fetch/api/request/request-init-002.html71
-rw-r--r--testing/web-platform/tests/fetch/api/request/request-init-003.sub.html84
-rw-r--r--testing/web-platform/tests/fetch/api/request/request-structure.html134
-rw-r--r--testing/web-platform/tests/fetch/api/request/resources/cache.py62
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