summaryrefslogtreecommitdiffstats
path: root/dom/tests/mochitest/fetch/test_fetch_cors.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/tests/mochitest/fetch/test_fetch_cors.js')
-rw-r--r--dom/tests/mochitest/fetch/test_fetch_cors.js1883
1 files changed, 1883 insertions, 0 deletions
diff --git a/dom/tests/mochitest/fetch/test_fetch_cors.js b/dom/tests/mochitest/fetch/test_fetch_cors.js
new file mode 100644
index 0000000000..05ce221435
--- /dev/null
+++ b/dom/tests/mochitest/fetch/test_fetch_cors.js
@@ -0,0 +1,1883 @@
+var path = "/tests/dom/base/test/";
+
+function isOpaqueResponse(response) {
+ return (
+ response.type == "opaque" &&
+ response.status === 0 &&
+ response.statusText === ""
+ );
+}
+
+function testModeSameOrigin() {
+ // Fetch spec Section 4, step 4, "request's mode is same-origin".
+ var req = new Request("http://example.com", { mode: "same-origin" });
+ return fetch(req).then(
+ function (res) {
+ ok(
+ false,
+ "Attempting to fetch a resource from a different origin with mode same-origin should fail."
+ );
+ },
+ function (e) {
+ ok(
+ e instanceof TypeError,
+ "Attempting to fetch a resource from a different origin with mode same-origin should fail."
+ );
+ }
+ );
+}
+
+function testNoCorsCtor() {
+ // Request constructor Step 19.1
+ var simpleMethods = ["GET", "HEAD", "POST"];
+ for (var i = 0; i < simpleMethods.length; ++i) {
+ var r = new Request("http://example.com", {
+ method: simpleMethods[i],
+ mode: "no-cors",
+ });
+ ok(
+ true,
+ "no-cors Request with simple method " + simpleMethods[i] + " is allowed."
+ );
+ }
+
+ var otherMethods = ["DELETE", "OPTIONS", "PUT"];
+ for (var i = 0; i < otherMethods.length; ++i) {
+ try {
+ var r = new Request("http://example.com", {
+ method: otherMethods[i],
+ mode: "no-cors",
+ });
+ ok(
+ false,
+ "no-cors Request with non-simple method " +
+ otherMethods[i] +
+ " is not allowed."
+ );
+ } catch (e) {
+ ok(
+ true,
+ "no-cors Request with non-simple method " +
+ otherMethods[i] +
+ " is not allowed."
+ );
+ }
+ }
+
+ // Request constructor Step 19.2, check guarded headers.
+ var r = new Request(".", { mode: "no-cors" });
+ r.headers.append("Content-Type", "multipart/form-data");
+ is(
+ r.headers.get("content-type"),
+ "multipart/form-data",
+ "Appending simple header should succeed"
+ );
+ r.headers.append("custom", "value");
+ ok(!r.headers.has("custom"), "Appending custom header should fail");
+ r.headers.append("DNT", "value");
+ ok(!r.headers.has("DNT"), "Appending forbidden header should fail");
+}
+
+var corsServerPath =
+ "/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?";
+function testModeNoCors() {
+ // Fetch spec, section 4, step 4, response tainting should be set opaque, so
+ // that fetching leads to an opaque filtered response in step 8.
+ var r = new Request("http://example.com" + corsServerPath + "status=200", {
+ mode: "no-cors",
+ });
+ return fetch(r).then(
+ function (res) {
+ ok(
+ isOpaqueResponse(res),
+ "no-cors Request fetch should result in opaque response"
+ );
+ },
+ function (e) {
+ ok(false, "no-cors Request fetch should not error");
+ }
+ );
+}
+
+function testSameOriginCredentials() {
+ var cookieStr = "type=chocolatechip";
+ var tests = [
+ {
+ // Initialize by setting a cookie.
+ pass: 1,
+ setCookie: cookieStr,
+ withCred: "same-origin",
+ },
+ {
+ // Default mode is "same-origin".
+ pass: 1,
+ cookie: cookieStr,
+ },
+ {
+ pass: 1,
+ noCookie: 1,
+ withCred: "omit",
+ },
+ {
+ pass: 1,
+ cookie: cookieStr,
+ withCred: "same-origin",
+ },
+ {
+ pass: 1,
+ cookie: cookieStr,
+ withCred: "include",
+ },
+ ];
+
+ var finalPromiseResolve, finalPromiseReject;
+ var finalPromise = new Promise(function (res, rej) {
+ finalPromiseResolve = res;
+ finalPromiseReject = rej;
+ });
+
+ function makeRequest(test) {
+ req = {
+ // Add a default query param just to make formatting the actual params
+ // easier.
+ url: corsServerPath + "a=b",
+ method: test.method,
+ headers: test.headers,
+ withCred: test.withCred,
+ };
+
+ if (test.setCookie) {
+ req.url += "&setCookie=" + escape(test.setCookie);
+ }
+ if (test.cookie) {
+ req.url += "&cookie=" + escape(test.cookie);
+ }
+ if (test.noCookie) {
+ req.url += "&noCookie";
+ }
+
+ return new Request(req.url, {
+ method: req.method,
+ headers: req.headers,
+ credentials: req.withCred,
+ });
+ }
+
+ function testResponse(res, test) {
+ ok(test.pass, "Expected test to pass " + JSON.stringify(test));
+ is(res.status, 200, "wrong status in test for " + JSON.stringify(test));
+ is(res.statusText, "OK", "wrong status text for " + JSON.stringify(test));
+ return res.text().then(function (v) {
+ is(
+ v,
+ "<res>hello pass</res>\n",
+ "wrong text in test for " + JSON.stringify(test)
+ );
+ });
+ }
+
+ function runATest(tests, i) {
+ var test = tests[i];
+ var request = makeRequest(test);
+ console.log(request.url);
+ fetch(request).then(
+ function (res) {
+ testResponse(res, test).then(function () {
+ if (i < tests.length - 1) {
+ runATest(tests, i + 1);
+ } else {
+ finalPromiseResolve();
+ }
+ });
+ },
+ function (e) {
+ ok(!test.pass, "Expected test to fail " + JSON.stringify(test));
+ ok(e instanceof TypeError, "Test should fail " + JSON.stringify(test));
+ if (i < tests.length - 1) {
+ runATest(tests, i + 1);
+ } else {
+ finalPromiseResolve();
+ }
+ }
+ );
+ }
+
+ runATest(tests, 0);
+ return finalPromise;
+}
+
+function testModeCors() {
+ var tests = [
+ // Plain request
+ { pass: 1, method: "GET", noAllowPreflight: 1 },
+
+ // undefined username
+ { pass: 1, method: "GET", noAllowPreflight: 1, username: undefined },
+
+ // undefined username and password
+ {
+ pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ username: undefined,
+ password: undefined,
+ },
+
+ // nonempty username
+ { pass: 0, method: "GET", noAllowPreflight: 1, username: "user" },
+
+ // nonempty password
+ { pass: 0, method: "GET", noAllowPreflight: 1, password: "password" },
+
+ // Default allowed headers
+ {
+ pass: 1,
+ method: "GET",
+ headers: {
+ "Content-Type": "text/plain",
+ Accept: "foo/bar",
+ "Accept-Language": "sv-SE",
+ },
+ noAllowPreflight: 1,
+ },
+
+ {
+ pass: 0,
+ method: "GET",
+ headers: {
+ "Content-Type": "foo/bar",
+ Accept: "foo/bar",
+ "Accept-Language": "sv-SE",
+ },
+ noAllowPreflight: 1,
+ },
+
+ {
+ pass: 0,
+ method: "GET",
+ headers: { "Content-Type": "foo/bar, text/plain" },
+ noAllowPreflight: 1,
+ },
+
+ {
+ pass: 0,
+ method: "GET",
+ headers: { "Content-Type": "foo/bar, text/plain, garbage" },
+ noAllowPreflight: 1,
+ },
+
+ // Custom headers
+ {
+ pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ {
+ pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "X-My-Header",
+ },
+ {
+ pass: 1,
+ method: "GET",
+ headers: {
+ "x-my-header": "myValue",
+ "long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header":
+ "secondValue",
+ },
+ allowHeaders:
+ "x-my-header, long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header",
+ },
+ {
+ pass: 1,
+ method: "GET",
+ headers: { "x-my%-header": "myValue" },
+ allowHeaders: "x-my%-header",
+ },
+ { pass: 0, method: "GET", headers: { "x-my-header": "myValue" } },
+ { pass: 0, method: "GET", headers: { "x-my-header": "" } },
+ {
+ pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "",
+ },
+ {
+ pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "y-my-header",
+ },
+ {
+ pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header y-my-header",
+ },
+ {
+ pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header, y-my-header z",
+ },
+ {
+ pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header, y-my-he(ader",
+ },
+ {
+ pass: 0,
+ method: "GET",
+ headers: { myheader: "" },
+ allowMethods: "myheader",
+ },
+ {
+ pass: 1,
+ method: "GET",
+ headers: { "User-Agent": "myValue" },
+ allowHeaders: "User-Agent",
+ },
+ { pass: 0, method: "GET", headers: { "User-Agent": "myValue" } },
+
+ // Multiple custom headers
+ {
+ pass: 1,
+ method: "GET",
+ headers: {
+ "x-my-header": "myValue",
+ "second-header": "secondValue",
+ "third-header": "thirdValue",
+ },
+ allowHeaders: "x-my-header, second-header, third-header",
+ },
+ {
+ pass: 1,
+ method: "GET",
+ headers: {
+ "x-my-header": "myValue",
+ "second-header": "secondValue",
+ "third-header": "thirdValue",
+ },
+ allowHeaders: "x-my-header,second-header,third-header",
+ },
+ {
+ pass: 1,
+ method: "GET",
+ headers: {
+ "x-my-header": "myValue",
+ "second-header": "secondValue",
+ "third-header": "thirdValue",
+ },
+ allowHeaders: "x-my-header ,second-header ,third-header",
+ },
+ {
+ pass: 1,
+ method: "GET",
+ headers: {
+ "x-my-header": "myValue",
+ "second-header": "secondValue",
+ "third-header": "thirdValue",
+ },
+ allowHeaders: "x-my-header , second-header , third-header",
+ },
+ {
+ pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue", "second-header": "secondValue" },
+ allowHeaders: ", x-my-header, , ,, second-header, , ",
+ },
+ {
+ pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue", "second-header": "secondValue" },
+ allowHeaders: "x-my-header, second-header, unused-header",
+ },
+ {
+ pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue", "y-my-header": "secondValue" },
+ allowHeaders: "x-my-header",
+ },
+ {
+ pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "", "y-my-header": "" },
+ allowHeaders: "x-my-header",
+ },
+
+ // HEAD requests
+ { pass: 1, method: "HEAD", noAllowPreflight: 1 },
+
+ // HEAD with safe headers
+ {
+ pass: 1,
+ method: "HEAD",
+ headers: {
+ "Content-Type": "text/plain",
+ Accept: "foo/bar",
+ "Accept-Language": "sv-SE",
+ },
+ noAllowPreflight: 1,
+ },
+ {
+ pass: 0,
+ method: "HEAD",
+ headers: {
+ "Content-Type": "foo/bar",
+ Accept: "foo/bar",
+ "Accept-Language": "sv-SE",
+ },
+ noAllowPreflight: 1,
+ },
+ {
+ pass: 0,
+ method: "HEAD",
+ headers: { "Content-Type": "foo/bar, text/plain" },
+ noAllowPreflight: 1,
+ },
+ {
+ pass: 0,
+ method: "HEAD",
+ headers: { "Content-Type": "foo/bar, text/plain, garbage" },
+ noAllowPreflight: 1,
+ },
+
+ // HEAD with custom headers
+ {
+ pass: 1,
+ method: "HEAD",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 0, method: "HEAD", headers: { "x-my-header": "myValue" } },
+ {
+ pass: 0,
+ method: "HEAD",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "",
+ },
+ {
+ pass: 0,
+ method: "HEAD",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "y-my-header",
+ },
+ {
+ pass: 0,
+ method: "HEAD",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header y-my-header",
+ },
+
+ // POST tests
+ { pass: 1, method: "POST", body: "hi there", noAllowPreflight: 1 },
+ { pass: 1, method: "POST" },
+ { pass: 1, method: "POST", noAllowPreflight: 1 },
+
+ // POST with standard headers
+ {
+ pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain" },
+ noAllowPreflight: 1,
+ },
+ {
+ pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "multipart/form-data" },
+ noAllowPreflight: 1,
+ },
+ {
+ pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
+ noAllowPreflight: 1,
+ },
+ {
+ pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar" },
+ },
+ { pass: 0, method: "POST", headers: { "Content-Type": "foo/bar" } },
+ {
+ pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: {
+ "Content-Type": "text/plain",
+ Accept: "foo/bar",
+ "Accept-Language": "sv-SE",
+ },
+ noAllowPreflight: 1,
+ },
+ {
+ pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar, text/plain" },
+ noAllowPreflight: 1,
+ },
+ {
+ pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar, text/plain, garbage" },
+ noAllowPreflight: 1,
+ },
+
+ // POST with custom headers
+ {
+ pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: {
+ Accept: "foo/bar",
+ "Accept-Language": "sv-SE",
+ "x-my-header": "myValue",
+ },
+ allowHeaders: "x-my-header",
+ },
+ {
+ pass: 1,
+ method: "POST",
+ headers: { "Content-Type": "text/plain", "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ {
+ pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain", "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ {
+ pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar", "x-my-header": "myValue" },
+ allowHeaders: "x-my-header, content-type",
+ },
+ {
+ pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar" },
+ noAllowPreflight: 1,
+ },
+ {
+ pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar", "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ {
+ pass: 1,
+ method: "POST",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ {
+ pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header, $_%",
+ },
+
+ // Other methods
+ { pass: 1, method: "DELETE", allowMethods: "DELETE" },
+ { pass: 0, method: "DELETE", allowHeaders: "DELETE" },
+ { pass: 0, method: "DELETE" },
+ { pass: 0, method: "DELETE", allowMethods: "" },
+ { pass: 1, method: "DELETE", allowMethods: "POST, PUT, DELETE" },
+ { pass: 1, method: "DELETE", allowMethods: "POST, DELETE, PUT" },
+ { pass: 1, method: "DELETE", allowMethods: "DELETE, POST, PUT" },
+ { pass: 1, method: "DELETE", allowMethods: "POST ,PUT ,DELETE" },
+ { pass: 1, method: "DELETE", allowMethods: "POST,PUT,DELETE" },
+ { pass: 1, method: "DELETE", allowMethods: "POST , PUT , DELETE" },
+ {
+ pass: 1,
+ method: "DELETE",
+ allowMethods: " ,, PUT ,, , , DELETE , ,",
+ },
+ { pass: 0, method: "DELETE", allowMethods: "PUT" },
+ { pass: 0, method: "DELETE", allowMethods: "DELETEZ" },
+ { pass: 0, method: "DELETE", allowMethods: "DELETE PUT" },
+ { pass: 0, method: "DELETE", allowMethods: "DELETE, PUT Z" },
+ { pass: 0, method: "DELETE", allowMethods: "DELETE, PU(T" },
+ { pass: 0, method: "DELETE", allowMethods: "PUT DELETE" },
+ { pass: 0, method: "DELETE", allowMethods: "PUT Z, DELETE" },
+ { pass: 0, method: "DELETE", allowMethods: "PU(T, DELETE" },
+ { pass: 0, method: "PUT", allowMethods: "put" },
+
+ // Status messages
+ {
+ pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ status: 404,
+ statusMessage: "nothin' here",
+ },
+ {
+ pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ status: 401,
+ statusMessage: "no can do",
+ },
+ {
+ pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar" },
+ allowHeaders: "content-type",
+ status: 500,
+ statusMessage: "server boo",
+ },
+ {
+ pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ status: 200,
+ statusMessage: "Yes!!",
+ },
+ {
+ pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "header value" },
+ allowHeaders: "x-my-header",
+ preflightStatus: 400,
+ },
+ {
+ pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "header value" },
+ allowHeaders: "x-my-header",
+ preflightStatus: 200,
+ },
+ {
+ pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "header value" },
+ allowHeaders: "x-my-header",
+ preflightStatus: 204,
+ },
+
+ // exposed headers
+ {
+ pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header",
+ expectedResponseHeaders: ["x-my-header"],
+ },
+ {
+ pass: 0,
+ method: "GET",
+ origin: "http://invalid",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header",
+ expectedResponseHeaders: [],
+ },
+ {
+ pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ expectedResponseHeaders: [],
+ },
+ {
+ pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header y",
+ expectedResponseHeaders: [],
+ },
+ {
+ pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "y x-my-header",
+ expectedResponseHeaders: [],
+ },
+ {
+ pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header, y-my-header z",
+ expectedResponseHeaders: [],
+ },
+ {
+ pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header, y-my-hea(er",
+ expectedResponseHeaders: [],
+ },
+ {
+ pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header", "y-my-header": "y header" },
+ exposeHeaders: " , ,,y-my-header,z-my-header, ",
+ expectedResponseHeaders: ["y-my-header"],
+ },
+ {
+ pass: 1,
+ method: "GET",
+ responseHeaders: {
+ "Cache-Control": "cacheControl header",
+ "Content-Language": "contentLanguage header",
+ Expires: "expires header",
+ "Last-Modified": "lastModified header",
+ Pragma: "pragma header",
+ Unexpected: "unexpected header",
+ },
+ expectedResponseHeaders: [
+ "Cache-Control",
+ "Content-Language",
+ "Content-Type",
+ "Expires",
+ "Last-Modified",
+ "Pragma",
+ ],
+ },
+ // Check that sending a body in the OPTIONS response works
+ {
+ pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ preflightBody: "I'm a preflight response body",
+ },
+ ];
+
+ var baseURL = "http://example.org" + corsServerPath;
+ var origin = "http://mochi.test:8888";
+ var fetches = [];
+ for (test of tests) {
+ var req = {
+ url: baseURL + "allowOrigin=" + escape(test.origin || origin),
+ method: test.method,
+ headers: test.headers,
+ uploadProgress: test.uploadProgress,
+ body: test.body,
+ responseHeaders: test.responseHeaders,
+ };
+
+ if (test.pass) {
+ req.url += "&origin=" + escape(origin) + "&requestMethod=" + test.method;
+ }
+
+ if ("username" in test) {
+ var u = new URL(req.url);
+ u.username = test.username || "";
+ req.url = u.href;
+ }
+
+ if ("password" in test) {
+ var u = new URL(req.url);
+ u.password = test.password || "";
+ req.url = u.href;
+ }
+
+ if (test.noAllowPreflight) {
+ req.url += "&noAllowPreflight";
+ }
+
+ if (test.pass && "headers" in test) {
+ function isUnsafeHeader(name) {
+ lName = name.toLowerCase();
+ return (
+ lName != "accept" &&
+ lName != "accept-language" &&
+ (lName != "content-type" ||
+ ![
+ "text/plain",
+ "multipart/form-data",
+ "application/x-www-form-urlencoded",
+ ].includes(test.headers[name].toLowerCase()))
+ );
+ }
+ req.url += "&headers=" + escape(JSON.stringify(test.headers));
+ reqHeaders = escape(
+ Object.keys(test.headers)
+ .filter(isUnsafeHeader)
+ .map(s => s.toLowerCase())
+ .sort()
+ .join(",")
+ );
+ req.url += reqHeaders ? "&requestHeaders=" + reqHeaders : "";
+ }
+ if ("allowHeaders" in test) {
+ req.url += "&allowHeaders=" + escape(test.allowHeaders);
+ }
+ if ("allowMethods" in test) {
+ req.url += "&allowMethods=" + escape(test.allowMethods);
+ }
+ if (test.body) {
+ req.url += "&body=" + escape(test.body);
+ }
+ if (test.status) {
+ req.url += "&status=" + test.status;
+ req.url += "&statusMessage=" + escape(test.statusMessage);
+ }
+ if (test.preflightStatus) {
+ req.url += "&preflightStatus=" + test.preflightStatus;
+ }
+ if (test.responseHeaders) {
+ req.url +=
+ "&responseHeaders=" + escape(JSON.stringify(test.responseHeaders));
+ }
+ if (test.exposeHeaders) {
+ req.url += "&exposeHeaders=" + escape(test.exposeHeaders);
+ }
+ if (test.preflightBody) {
+ req.url += "&preflightBody=" + escape(test.preflightBody);
+ }
+
+ fetches.push(
+ (function (test) {
+ return new Promise(function (resolve) {
+ resolve(
+ new Request(req.url, {
+ method: req.method,
+ mode: "cors",
+ headers: req.headers,
+ body: req.body,
+ })
+ );
+ })
+ .then(function (request) {
+ return fetch(request);
+ })
+ .then(function (res) {
+ ok(test.pass, "Expected test to pass for " + JSON.stringify(test));
+ if (test.status) {
+ is(
+ res.status,
+ test.status,
+ "wrong status in test for " + JSON.stringify(test)
+ );
+ is(
+ res.statusText,
+ test.statusMessage,
+ "wrong status text for " + JSON.stringify(test)
+ );
+ } else {
+ is(
+ res.status,
+ 200,
+ "wrong status in test for " + JSON.stringify(test)
+ );
+ is(
+ res.statusText,
+ "OK",
+ "wrong status text for " + JSON.stringify(test)
+ );
+ }
+ if (test.responseHeaders) {
+ for (header in test.responseHeaders) {
+ if (!test.expectedResponseHeaders.includes(header)) {
+ is(
+ res.headers.has(header),
+ false,
+ "|Headers.has()|wrong response header (" +
+ header +
+ ") in test for " +
+ JSON.stringify(test)
+ );
+ } else {
+ is(
+ res.headers.get(header),
+ test.responseHeaders[header],
+ "|Headers.get()|wrong response header (" +
+ header +
+ ") in test for " +
+ JSON.stringify(test)
+ );
+ }
+ }
+ }
+
+ return res.text();
+ })
+ .then(function (v) {
+ if (test.method !== "HEAD") {
+ is(
+ v,
+ "<res>hello pass</res>\n",
+ "wrong responseText in test for " + JSON.stringify(test)
+ );
+ } else {
+ is(
+ v,
+ "",
+ "wrong responseText in HEAD test for " + JSON.stringify(test)
+ );
+ }
+ })
+ .catch(function (e) {
+ ok(!test.pass, "Expected test failure for " + JSON.stringify(test));
+ ok(
+ e instanceof TypeError,
+ "Exception should be TypeError for " + JSON.stringify(test)
+ );
+ });
+ })(test)
+ );
+ }
+
+ return Promise.all(fetches);
+}
+
+function testCrossOriginCredentials() {
+ var origin = "http://mochi.test:8888";
+ var tests = [
+ { pass: 1, method: "GET", withCred: "include", allowCred: 1 },
+ { pass: 0, method: "GET", withCred: "include", allowCred: 0 },
+ { pass: 0, method: "GET", withCred: "include", allowCred: 1, origin: "*" },
+ { pass: 1, method: "GET", withCred: "omit", allowCred: 1, origin: "*" },
+ {
+ pass: 1,
+ method: "GET",
+ setCookie: "a=1",
+ withCred: "include",
+ allowCred: 1,
+ },
+ {
+ pass: 1,
+ method: "GET",
+ cookie: "a=1",
+ withCred: "include",
+ allowCred: 1,
+ },
+ { pass: 1, method: "GET", noCookie: 1, withCred: "omit", allowCred: 1 },
+ { pass: 0, method: "GET", noCookie: 1, withCred: "include", allowCred: 1 },
+ {
+ pass: 1,
+ method: "GET",
+ setCookie: "a=2",
+ withCred: "omit",
+ allowCred: 1,
+ },
+ {
+ pass: 1,
+ method: "GET",
+ cookie: "a=1",
+ withCred: "include",
+ allowCred: 1,
+ },
+ {
+ pass: 1,
+ method: "GET",
+ setCookie: "a=2",
+ withCred: "include",
+ allowCred: 1,
+ },
+ {
+ pass: 1,
+ method: "GET",
+ cookie: "a=2",
+ withCred: "include",
+ allowCred: 1,
+ },
+ {
+ // When credentials mode is same-origin, but mode is cors, no
+ // cookie should be sent cross origin.
+ pass: 0,
+ method: "GET",
+ cookie: "a=2",
+ withCred: "same-origin",
+ allowCred: 1,
+ },
+ {
+ // When credentials mode is same-origin, but mode is cors, no
+ // cookie should be sent cross origin. This test checks the same
+ // thing as above, but uses the noCookie check on the server
+ // instead, and expects a valid response.
+ pass: 1,
+ method: "GET",
+ noCookie: 1,
+ withCred: "same-origin",
+ },
+ {
+ // Initialize by setting a cookies for same- and cross- origins.
+ pass: 1,
+ hops: [
+ { server: origin, setCookie: escape("a=1") },
+ {
+ server: "http://example.com",
+ allowOrigin: origin,
+ allowCred: 1,
+ setCookie: escape("a=2"),
+ },
+ ],
+ withCred: "include",
+ },
+ {
+ pass: 1,
+ method: "GET",
+ hops: [
+ { server: origin, cookie: escape("a=1") },
+ { server: origin, cookie: escape("a=1") },
+ { server: "http://example.com", allowOrigin: origin, noCookie: 1 },
+ ],
+ withCred: "same-origin",
+ },
+ {
+ pass: 1,
+ method: "GET",
+ hops: [
+ { server: origin, cookie: escape("a=1") },
+ { server: origin, cookie: escape("a=1") },
+ {
+ server: "http://example.com",
+ allowOrigin: origin,
+ allowCred: 1,
+ cookie: escape("a=2"),
+ },
+ ],
+ withCred: "include",
+ },
+ {
+ pass: 1,
+ method: "GET",
+ hops: [
+ { server: origin, cookie: escape("a=1") },
+ { server: origin, cookie: escape("a=1") },
+ { server: "http://example.com", allowOrigin: "*", noCookie: 1 },
+ ],
+ withCred: "same-origin",
+ },
+ {
+ pass: 0,
+ method: "GET",
+ hops: [
+ { server: origin, cookie: escape("a=1") },
+ { server: origin, cookie: escape("a=1") },
+ {
+ server: "http://example.com",
+ allowOrigin: "*",
+ allowCred: 1,
+ cookie: escape("a=2"),
+ },
+ ],
+ withCred: "include",
+ },
+ // fails because allow-credentials CORS header is not set by server
+ {
+ pass: 0,
+ method: "GET",
+ hops: [
+ { server: origin, cookie: escape("a=1") },
+ { server: origin, cookie: escape("a=1") },
+ {
+ server: "http://example.com",
+ allowOrigin: origin,
+ cookie: escape("a=2"),
+ },
+ ],
+ withCred: "include",
+ },
+ {
+ pass: 1,
+ method: "GET",
+ hops: [
+ { server: origin, noCookie: 1 },
+ { server: origin, noCookie: 1 },
+ { server: "http://example.com", allowOrigin: origin, noCookie: 1 },
+ ],
+ withCred: "omit",
+ },
+ ];
+
+ var baseURL = "http://example.org" + corsServerPath;
+ var origin = "http://mochi.test:8888";
+
+ var finalPromiseResolve, finalPromiseReject;
+ var finalPromise = new Promise(function (res, rej) {
+ finalPromiseResolve = res;
+ finalPromiseReject = rej;
+ });
+
+ function makeRequest(test) {
+ var url;
+ if (test.hops) {
+ url =
+ test.hops[0].server +
+ corsServerPath +
+ "hop=1&hops=" +
+ escape(JSON.stringify(test.hops));
+ } else {
+ url = baseURL + "allowOrigin=" + escape(test.origin || origin);
+ }
+ req = {
+ url,
+ method: test.method,
+ headers: test.headers,
+ withCred: test.withCred,
+ };
+
+ if (test.allowCred) {
+ req.url += "&allowCred";
+ }
+
+ if (test.setCookie) {
+ req.url += "&setCookie=" + escape(test.setCookie);
+ }
+ if (test.cookie) {
+ req.url += "&cookie=" + escape(test.cookie);
+ }
+ if (test.noCookie) {
+ req.url += "&noCookie";
+ }
+
+ if ("allowHeaders" in test) {
+ req.url += "&allowHeaders=" + escape(test.allowHeaders);
+ }
+ if ("allowMethods" in test) {
+ req.url += "&allowMethods=" + escape(test.allowMethods);
+ }
+
+ return new Request(req.url, {
+ method: req.method,
+ headers: req.headers,
+ credentials: req.withCred,
+ });
+ }
+
+ function testResponse(res, test) {
+ ok(test.pass, "Expected test to pass for " + JSON.stringify(test));
+ is(res.status, 200, "wrong status in test for " + JSON.stringify(test));
+ is(res.statusText, "OK", "wrong status text for " + JSON.stringify(test));
+ return res.text().then(function (v) {
+ is(
+ v,
+ "<res>hello pass</res>\n",
+ "wrong text in test for " + JSON.stringify(test)
+ );
+ });
+ }
+
+ function runATest(tests, i) {
+ var test = tests[i];
+ var request = makeRequest(test);
+ fetch(request).then(
+ function (res) {
+ testResponse(res, test).then(function () {
+ if (i < tests.length - 1) {
+ runATest(tests, i + 1);
+ } else {
+ finalPromiseResolve();
+ }
+ });
+ },
+ function (e) {
+ ok(!test.pass, "Expected test failure for " + JSON.stringify(test));
+ ok(
+ e instanceof TypeError,
+ "Exception should be TypeError for " + JSON.stringify(test)
+ );
+ if (i < tests.length - 1) {
+ runATest(tests, i + 1);
+ } else {
+ finalPromiseResolve();
+ }
+ }
+ );
+ }
+
+ runATest(tests, 0);
+ return finalPromise;
+}
+
+function testModeNoCorsCredentials() {
+ var cookieStr = "type=chocolatechip";
+ var tests = [
+ {
+ // Initialize by setting a cookie.
+ pass: 1,
+ setCookie: cookieStr,
+ withCred: "include",
+ },
+ {
+ pass: 1,
+ noCookie: 1,
+ withCred: "omit",
+ },
+ {
+ pass: 1,
+ noCookie: 1,
+ withCred: "same-origin",
+ },
+ {
+ pass: 1,
+ cookie: cookieStr,
+ withCred: "include",
+ },
+ {
+ pass: 1,
+ cookie: cookieStr,
+ withCred: "omit",
+ status: 500,
+ },
+ {
+ pass: 1,
+ cookie: cookieStr,
+ withCred: "same-origin",
+ status: 500,
+ },
+ {
+ pass: 1,
+ noCookie: 1,
+ withCred: "include",
+ status: 500,
+ },
+ ];
+
+ var finalPromiseResolve, finalPromiseReject;
+ var finalPromise = new Promise(function (res, rej) {
+ finalPromiseResolve = res;
+ finalPromiseReject = rej;
+ });
+
+ function makeRequest(test) {
+ req = {
+ url: "http://example.org" + corsServerPath + "a+b",
+ withCred: test.withCred,
+ };
+
+ if (test.setCookie) {
+ req.url += "&setCookie=" + escape(test.setCookie);
+ }
+ if (test.cookie) {
+ req.url += "&cookie=" + escape(test.cookie);
+ }
+ if (test.noCookie) {
+ req.url += "&noCookie";
+ }
+
+ return new Request(req.url, {
+ method: "GET",
+ mode: "no-cors",
+ credentials: req.withCred,
+ });
+ }
+
+ function testResponse(res, test) {
+ is(res.type, "opaque", "wrong response type for " + JSON.stringify(test));
+
+ // Get unfiltered response
+ var chromeResponse = SpecialPowers.wrap(res);
+ var unfiltered = chromeResponse.cloneUnfiltered();
+
+ var status = test.status ? test.status : 200;
+ is(
+ unfiltered.status,
+ status,
+ "wrong status in test for " + JSON.stringify(test)
+ );
+
+ return unfiltered.text().then(function (v) {
+ if (test.status === 200) {
+ const expected =
+ SpecialPowers.getIntPref(
+ "browser.opaqueResponseBlocking.filterFetchResponse"
+ ) > 0
+ ? ""
+ : "<res>hello pass</res>\n";
+ is(v, expected, "wrong text in test for " + JSON.stringify(test));
+ }
+ });
+ }
+
+ function runATest(tests, i) {
+ if (typeof SpecialPowers !== "object") {
+ finalPromiseResolve();
+ return;
+ }
+
+ var test = tests[i];
+ var request = makeRequest(test);
+ fetch(request).then(
+ function (res) {
+ ok(test.pass, "Expected test to pass " + JSON.stringify(test));
+ testResponse(res, test).then(function () {
+ if (i < tests.length - 1) {
+ runATest(tests, i + 1);
+ } else {
+ finalPromiseResolve();
+ }
+ });
+ },
+ function (e) {
+ ok(!test.pass, "Expected test to fail " + JSON.stringify(test));
+ ok(e instanceof TypeError, "Test should fail " + JSON.stringify(test));
+ if (i < tests.length - 1) {
+ runATest(tests, i + 1);
+ } else {
+ finalPromiseResolve();
+ }
+ }
+ );
+ }
+
+ runATest(tests, 0);
+ return finalPromise;
+}
+
+function testCORSRedirects() {
+ var origin = "http://mochi.test:8888";
+
+ var tests = [
+ {
+ pass: 1,
+ method: "GET",
+ hops: [{ server: "http://example.com", allowOrigin: origin }],
+ },
+ {
+ pass: 0,
+ method: "GET",
+ hops: [
+ { server: "http://example.com", allowOrigin: origin },
+ { server: "http://mochi.test:8888", allowOrigin: origin },
+ ],
+ },
+ {
+ pass: 1,
+ method: "GET",
+ hops: [
+ { server: "http://example.com", allowOrigin: origin },
+ { server: "http://mochi.test:8888", allowOrigin: "*" },
+ ],
+ },
+ {
+ pass: 0,
+ method: "GET",
+ hops: [
+ { server: "http://example.com", allowOrigin: origin },
+ { server: "http://mochi.test:8888" },
+ ],
+ },
+ {
+ pass: 1,
+ method: "GET",
+ hops: [
+ { server: "http://mochi.test:8888" },
+ { server: "http://mochi.test:8888" },
+ { server: "http://example.com", allowOrigin: origin },
+ ],
+ },
+ {
+ pass: 0,
+ method: "GET",
+ hops: [
+ { server: "http://mochi.test:8888" },
+ { server: "http://mochi.test:8888" },
+ { server: "http://example.com", allowOrigin: origin },
+ { server: "http://mochi.test:8888" },
+ ],
+ },
+ {
+ pass: 0,
+ method: "GET",
+ hops: [
+ { server: "http://example.com", allowOrigin: origin },
+ { server: "http://test2.mochi.test:8888", allowOrigin: origin },
+ {
+ server: "http://sub2.xn--lt-uia.mochi.test:8888",
+ allowOrigin: origin,
+ },
+ { server: "http://sub1.test1.mochi.test:8888", allowOrigin: origin },
+ ],
+ },
+ {
+ pass: 0,
+ method: "GET",
+ hops: [
+ { server: "http://example.com", allowOrigin: origin },
+ { server: "http://test2.mochi.test:8888", allowOrigin: origin },
+ { server: "http://sub2.xn--lt-uia.mochi.test:8888", allowOrigin: "*" },
+ { server: "http://sub1.test1.mochi.test:8888", allowOrigin: "*" },
+ ],
+ },
+ {
+ pass: 1,
+ method: "GET",
+ hops: [
+ { server: "http://example.com", allowOrigin: origin },
+ { server: "http://test2.mochi.test:8888", allowOrigin: "*" },
+ { server: "http://sub2.xn--lt-uia.mochi.test:8888", allowOrigin: "*" },
+ { server: "http://sub1.test1.mochi.test:8888", allowOrigin: "*" },
+ ],
+ },
+ {
+ pass: 0,
+ method: "GET",
+ hops: [
+ { server: "http://example.com", allowOrigin: origin },
+ { server: "http://test2.mochi.test:8888", allowOrigin: origin },
+ { server: "http://sub2.xn--lt-uia.mochi.test:8888", allowOrigin: "x" },
+ { server: "http://sub1.test1.mochi.test:8888", allowOrigin: origin },
+ ],
+ },
+ {
+ pass: 0,
+ method: "GET",
+ hops: [
+ { server: "http://example.com", allowOrigin: origin },
+ { server: "http://test2.mochi.test:8888", allowOrigin: origin },
+ { server: "http://sub2.xn--lt-uia.mochi.test:8888", allowOrigin: "*" },
+ { server: "http://sub1.test1.mochi.test:8888", allowOrigin: origin },
+ ],
+ },
+ {
+ pass: 0,
+ method: "GET",
+ hops: [
+ { server: "http://example.com", allowOrigin: origin },
+ { server: "http://test2.mochi.test:8888", allowOrigin: origin },
+ { server: "http://sub2.xn--lt-uia.mochi.test:8888", allowOrigin: "*" },
+ { server: "http://sub1.test1.mochi.test:8888" },
+ ],
+ },
+ {
+ pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain" },
+ hops: [
+ { server: "http://mochi.test:8888" },
+ { server: "http://example.com", allowOrigin: origin },
+ ],
+ },
+ {
+ pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain", "my-header": "myValue" },
+ hops: [
+ { server: "http://mochi.test:8888" },
+ {
+ server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ {
+ pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain", "my-header": "myValue" },
+ hops: [
+ { server: "http://mochi.test:8888" },
+ {
+ server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ noAllowPreflight: 1,
+ },
+ ],
+ },
+ {
+ pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain", "my-header": "myValue" },
+ hops: [
+ { server: "http://mochi.test:8888" },
+ {
+ server: "http://test1.example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ {
+ server: "http://test2.example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ {
+ pass: 1,
+ method: "DELETE",
+ hops: [
+ { server: "http://mochi.test:8888" },
+ {
+ server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ ],
+ },
+ {
+ pass: 0,
+ method: "DELETE",
+ hops: [
+ { server: "http://mochi.test:8888" },
+ {
+ server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ noAllowPreflight: 1,
+ },
+ ],
+ },
+ {
+ pass: 0,
+ method: "DELETE",
+ hops: [
+ { server: "http://mochi.test:8888" },
+ {
+ server: "http://test1.example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ {
+ server: "http://test2.example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ ],
+ },
+ {
+ pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain", "my-header": "myValue" },
+ hops: [
+ { server: "http://example.com", allowOrigin: origin },
+ { server: "http://sub1.test1.mochi.test:8888", allowOrigin: origin },
+ ],
+ },
+ {
+ pass: 0,
+ method: "DELETE",
+ hops: [
+ {
+ server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ {
+ server: "http://sub1.test1.mochi.test:8888",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ ],
+ },
+ {
+ pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain", "my-header": "myValue" },
+ hops: [
+ { server: "http://example.com" },
+ {
+ server: "http://sub1.test1.mochi.test:8888",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ {
+ pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain" },
+ hops: [
+ { server: "http://mochi.test:8888" },
+ { server: "http://example.com", allowOrigin: origin },
+ ],
+ },
+ {
+ pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain", "my-header": "myValue" },
+ hops: [
+ {
+ server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ {
+ server: "http://mochi.test:8888",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ ];
+
+ var fetches = [];
+ for (test of tests) {
+ req = {
+ url:
+ test.hops[0].server +
+ corsServerPath +
+ "hop=1&hops=" +
+ escape(JSON.stringify(test.hops)),
+ method: test.method,
+ headers: test.headers,
+ body: test.body,
+ };
+
+ if (test.headers) {
+ req.url += "&headers=" + escape(JSON.stringify(test.headers));
+ }
+
+ if (test.pass) {
+ if (test.body) {
+ req.url += "&body=" + escape(test.body);
+ }
+ }
+
+ var request = new Request(req.url, {
+ method: req.method,
+ headers: req.headers,
+ body: req.body,
+ });
+ fetches.push(
+ (function (request, test) {
+ return fetch(request).then(
+ function (res) {
+ ok(test.pass, "Expected test to pass for " + JSON.stringify(test));
+ is(
+ res.status,
+ 200,
+ "wrong status in test for " + JSON.stringify(test)
+ );
+ is(
+ res.statusText,
+ "OK",
+ "wrong status text for " + JSON.stringify(test)
+ );
+ is(
+ res.type,
+ "cors",
+ "wrong response type for " + JSON.stringify(test)
+ );
+ var reqHost = new URL(req.url).host;
+ // If there is a service worker present, the redirections will be
+ // transparent, assuming that the original request is to the current
+ // site and would be intercepted.
+ if (isSWPresent) {
+ if (reqHost === location.host) {
+ is(
+ new URL(res.url).host,
+ reqHost,
+ "Response URL should be original URL with a SW present"
+ );
+ }
+ } else {
+ is(
+ new URL(res.url).host,
+ new URL(test.hops[test.hops.length - 1].server).host,
+ "Response URL should be redirected URL"
+ );
+ }
+ return res.text().then(function (v) {
+ is(
+ v,
+ "<res>hello pass</res>\n",
+ "wrong responseText in test for " + JSON.stringify(test)
+ );
+ });
+ },
+ function (e) {
+ ok(!test.pass, "Expected test failure for " + JSON.stringify(test));
+ ok(
+ e instanceof TypeError,
+ "Exception should be TypeError for " + JSON.stringify(test)
+ );
+ }
+ );
+ })(request, test)
+ );
+ }
+
+ return Promise.all(fetches);
+}
+
+function testNoCORSRedirects() {
+ var origin = "http://mochi.test:8888";
+
+ var tests = [
+ { pass: 1, method: "GET", hops: [{ server: "http://example.com" }] },
+ {
+ pass: 1,
+ method: "GET",
+ hops: [{ server: origin }, { server: "http://example.com" }],
+ },
+ {
+ pass: 1,
+ method: "GET",
+ // Must use a simple header due to no-cors header restrictions.
+ headers: { "accept-language": "en-us" },
+ hops: [{ server: origin }, { server: "http://example.com" }],
+ },
+ {
+ pass: 1,
+ method: "GET",
+ hops: [
+ { server: origin },
+ { server: "http://example.com" },
+ { server: origin },
+ ],
+ },
+ {
+ pass: 1,
+ method: "POST",
+ body: "upload body here",
+ hops: [{ server: origin }, { server: "http://example.com" }],
+ },
+ {
+ pass: 0,
+ method: "DELETE",
+ hops: [{ server: origin }, { server: "http://example.com" }],
+ },
+ ];
+
+ var fetches = [];
+ for (test of tests) {
+ req = {
+ url:
+ test.hops[0].server +
+ corsServerPath +
+ "hop=1&hops=" +
+ escape(JSON.stringify(test.hops)),
+ method: test.method,
+ headers: test.headers,
+ body: test.body,
+ };
+
+ if (test.headers) {
+ req.url += "&headers=" + escape(JSON.stringify(test.headers));
+ }
+
+ if (test.pass) {
+ if (test.body) {
+ req.url += "&body=" + escape(test.body);
+ }
+ }
+
+ fetches.push(
+ (function (req, test) {
+ return new Promise(function (resolve, reject) {
+ resolve(
+ new Request(req.url, {
+ mode: "no-cors",
+ method: req.method,
+ headers: req.headers,
+ body: req.body,
+ })
+ );
+ })
+ .then(function (request) {
+ return fetch(request);
+ })
+ .then(
+ function (res) {
+ ok(
+ test.pass,
+ "Expected test to pass for " + JSON.stringify(test)
+ );
+ // All requests are cross-origin no-cors, we should always have
+ // an opaque response here. All values on the opaque response
+ // should be hidden.
+ is(
+ res.type,
+ "opaque",
+ "wrong response type for " + JSON.stringify(test)
+ );
+ is(
+ res.status,
+ 0,
+ "wrong status in test for " + JSON.stringify(test)
+ );
+ is(
+ res.statusText,
+ "",
+ "wrong status text for " + JSON.stringify(test)
+ );
+ is(res.url, "", "wrong response url for " + JSON.stringify(test));
+ return res.text().then(function (v) {
+ is(
+ v,
+ "",
+ "wrong responseText in test for " + JSON.stringify(test)
+ );
+ });
+ },
+ function (e) {
+ ok(
+ !test.pass,
+ "Expected test failure for " + JSON.stringify(test)
+ );
+ ok(
+ e instanceof TypeError,
+ "Exception should be TypeError for " + JSON.stringify(test)
+ );
+ }
+ );
+ })(req, test)
+ );
+ }
+
+ return Promise.all(fetches);
+}
+
+function testReferrer() {
+ var referrer;
+ if (self && self.location) {
+ referrer = self.location.href;
+ } else {
+ referrer = document.documentURI;
+ }
+
+ var dict = {
+ Referer: referrer,
+ };
+ return fetch(
+ corsServerPath + "headers=" + encodeURIComponent(JSON.stringify(dict))
+ ).then(
+ function (res) {
+ is(res.status, 200, "expected correct referrer header to be sent");
+ dump(res.statusText);
+ },
+ function (e) {
+ ok(false, "expected correct referrer header to be sent");
+ }
+ );
+}
+
+function runTest() {
+ testNoCorsCtor();
+ let promise = Promise.resolve();
+ if (typeof SpecialPowers === "object") {
+ promise = SpecialPowers.pushPrefEnv({
+ // Bug 1617611: Fix all the tests broken by "cookies SameSite=lax by default"
+ set: [["network.cookie.sameSite.laxByDefault", false]],
+ });
+ }
+
+ return promise
+ .then(testModeSameOrigin)
+ .then(testModeNoCors)
+ .then(testModeCors)
+ .then(testSameOriginCredentials)
+ .then(testCrossOriginCredentials)
+ .then(testModeNoCorsCredentials)
+ .then(testCORSRedirects)
+ .then(testNoCORSRedirects)
+ .then(testReferrer);
+ // Put more promise based tests here.
+}