author | Nikhil Marathe <nsm.nikhil@gmail.com> |
Thu, 15 Jan 2015 14:28:14 -0800 | |
changeset 225497 | 1a680c6110633ba0aac904568584114df9720539 |
parent 225496 | a1791566f721f1a14f515e7aec5f45890fe22a8f |
child 225498 | a76ced5f328cbe36bae13f80f4ad4659d40a8e6b |
push id | 28163 |
push user | philringnalda@gmail.com |
push date | Sat, 24 Jan 2015 16:27:39 +0000 |
treeherder | mozilla-central@1cf171c1a177 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 1122258 |
milestone | 38.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
dom/fetch/FetchDriver.cpp | file | annotate | diff | comparison | revisions | |
dom/tests/mochitest/fetch/worker_test_fetch_cors.js | file | annotate | diff | comparison | revisions |
--- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -311,40 +311,16 @@ FetchDriver::HttpFetch(bool aCORSFlag, b return rv; } // Step 2 deals with letting ServiceWorkers intercept requests. This is // handled by Necko after the channel is opened. // FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be // set based on the Request's flag. - // From here on we create a channel and set its properties with the - // information from the InternalRequest. This is an implementation detail. - MOZ_ASSERT(mLoadGroup); - nsCOMPtr<nsIChannel> chan; - rv = NS_NewChannel(getter_AddRefs(chan), - uri, - mPrincipal, - nsILoadInfo::SEC_NORMAL, - mRequest->GetContext(), - mLoadGroup, - nullptr, /* aCallbacks */ - nsIRequest::LOAD_NORMAL, - ios); - mLoadGroup = nullptr; - if (NS_WARN_IF(NS_FAILED(rv))) { - FailWithNetworkError(); - return rv; - } - - // Insert ourselves into the notification callbacks chain so we can handle - // cross-origin redirects. - chan->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks)); - chan->SetNotificationCallbacks(this); - // Step 3.1 "If the CORS preflight flag is set and one of these conditions is // true..." is handled by the CORS proxy. // // Step 3.2 "Set request's skip service worker flag." This isn't required // since Necko will fall back to the network if the ServiceWorker does not // respond with a valid Response. // // NS_StartCORSPreflight() will automatically kick off the original request @@ -355,16 +331,46 @@ FetchDriver::HttpFetch(bool aCORSFlag, b // is include, or request's credentials mode is same-origin and the CORS flag // is unset, and unset otherwise." bool useCredentials = false; if (mRequest->GetCredentialsMode() == RequestCredentials::Include || (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin && !aCORSFlag)) { useCredentials = true; } + // This is effectivetly the opposite of the use credentials flag in "HTTP + // network or cache fetch" in the spec and decides whether to transmit + // cookies and other identifying information. LOAD_ANONYMOUS also prevents + // new cookies sent by the server from being stored. + const nsLoadFlags credentialsFlag = useCredentials ? 0 : nsIRequest::LOAD_ANONYMOUS; + + // From here on we create a channel and set its properties with the + // information from the InternalRequest. This is an implementation detail. + MOZ_ASSERT(mLoadGroup); + nsCOMPtr<nsIChannel> chan; + rv = NS_NewChannel(getter_AddRefs(chan), + uri, + mPrincipal, + nsILoadInfo::SEC_NORMAL, + mRequest->GetContext(), + mLoadGroup, + nullptr, /* aCallbacks */ + nsIRequest::LOAD_NORMAL | credentialsFlag, + ios); + mLoadGroup = nullptr; + if (NS_WARN_IF(NS_FAILED(rv))) { + FailWithNetworkError(); + return rv; + } + + // Insert ourselves into the notification callbacks chain so we can handle + // cross-origin redirects. + chan->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks)); + chan->SetNotificationCallbacks(this); + // FIXME(nsm): Bug 1120715. // Step 3.4 "If request's cache mode is default and request's header list // contains a header named `If-Modified-Since`, `If-None-Match`, // `If-Unmodified-Since`, `If-Match`, or `If-Range`, set request's cache mode // to no-store." // Step 3.5 begins "HTTP network or cache fetch". // HTTP network or cache fetch
--- a/dom/tests/mochitest/fetch/worker_test_fetch_cors.js +++ b/dom/tests/mochitest/fetch/worker_test_fetch_cors.js @@ -7,20 +7,16 @@ if (typeof ok !== "function") { if (typeof is !== "function") { function is(a, b, msg) { postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg }); } } var path = "/tests/dom/base/test/"; -function isNetworkError(response) { - return response.type == "error" && response.status === 0 && response.statusText === ""; -} - 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) { @@ -63,16 +59,111 @@ 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&allowOrigin=*", { mode: "no-cors" }); return fetch(r).then(function(res) { ok(isOpaqueResponse(res), "no-cors Request fetch should result in opaque response"); }); } +function testSameOriginCredentials() { + var cookieStr = "type=chocolatechip"; + var tests = [ + { + // Initialize by setting a cookie. + pass: 1, + setCookie: cookieStr, + withCred: "same-origin", + }, + { + // Default mode is "omit". + pass: 1, + noCookie: 1, + }, + { + 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 " + test.toSource()); + is(res.status, 200, "wrong status in test for " + test.toSource()); + is(res.statusText, "OK", "wrong status text for " + test.toSource()); + return res.text().then(function(v) { + is(v, "<res>hello pass</res>\n", + "wrong text in test for " + test.toSource()); + }); + } + + function runATest(tests, i) { + var test = tests[i]; + var request = makeRequest(test); + console.log(request.url); + fetch(request).then(function(res) { + testResponse(res, test); + if (i < tests.length-1) { + runATest(tests, i+1); + } else { + finalPromiseResolve(); + } + }, function(e) { + ok(!test.pass, "Expected test to fail " + test.toSource()); + ok(e instanceof TypeError, "Test should fail " + test.toSource()); + 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 @@ -701,90 +792,108 @@ function testModeCors() { ok(e instanceof TypeError, "Exception should be TypeError for " + test.toSource()); }); })(test, request)); } return Promise.all(fetches); } -function testCredentials() { +function testCrossOriginCredentials() { var tests = [ { pass: 1, method: "GET", - withCred: 1, + withCred: "include", allowCred: 1, }, { pass: 0, method: "GET", - withCred: 1, + withCred: "include", allowCred: 0, }, { pass: 0, method: "GET", - withCred: 1, + withCred: "include", allowCred: 1, origin: "*", }, { pass: 1, method: "GET", - withCred: 0, + withCred: "omit", allowCred: 1, origin: "*", }, { pass: 1, method: "GET", setCookie: "a=1", - withCred: 1, + withCred: "include", allowCred: 1, }, { pass: 1, method: "GET", cookie: "a=1", - withCred: 1, + withCred: "include", allowCred: 1, }, { pass: 1, method: "GET", noCookie: 1, - withCred: 0, + withCred: "omit", allowCred: 1, }, { pass: 0, method: "GET", noCookie: 1, - withCred: 1, + withCred: "include", allowCred: 1, }, { pass: 1, method: "GET", setCookie: "a=2", - withCred: 0, + withCred: "omit", allowCred: 1, }, { pass: 1, method: "GET", cookie: "a=1", - withCred: 1, + withCred: "include", allowCred: 1, }, { pass: 1, method: "GET", setCookie: "a=2", - withCred: 1, + withCred: "include", allowCred: 1, }, { pass: 1, method: "GET", cookie: "a=2", - withCred: 1, + 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", + }, ]; - // FIXME(nsm): Add "same-origin" credentials test 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; @@ -810,51 +919,51 @@ function testCredentials() { 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 ? "include" : "omit" }); + credentials: req.withCred }); } function testResponse(res, test) { ok(test.pass, "Expected test to pass for " + test.toSource()); is(res.status, 200, "wrong status in test for " + test.toSource()); is(res.statusText, "OK", "wrong status text for " + test.toSource()); return res.text().then(function(v) { is(v, "<res>hello pass</res>\n", "wrong text in test for " + test.toSource()); }); } - function runATest(i) { + function runATest(tests, i) { var test = tests[i]; var request = makeRequest(test); fetch(request).then(function(res) { testResponse(res, test); if (i < tests.length-1) { - runATest(i+1); + runATest(tests, i+1); } else { finalPromiseResolve(); } }, function(e) { ok(!test.pass, "Expected test failure for " + test.toSource()); ok(e instanceof TypeError, "Exception should be TypeError for " + test.toSource()); if (i < tests.length-1) { - runATest(i+1); + runATest(tests, i+1); } else { finalPromiseResolve(); } }); } - runATest(0); + runATest(tests, 0); return finalPromise; } function testRedirects() { var origin = "http://mochi.test:8888"; var tests = [ { pass: 1, @@ -1129,18 +1238,16 @@ function testRedirects() { } 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 " + test.toSource()); - is(isNetworkError(res), false, - "shouldn't have failed in test for " + test.toSource()); is(res.status, 200, "wrong status in test for " + test.toSource()); is(res.statusText, "OK", "wrong status text for " + test.toSource()); 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 " + test.toSource()); }); }, function(e) { @@ -1163,17 +1270,18 @@ function runTest() { } testNoCorsCtor(); Promise.resolve() .then(testModeSameOrigin) .then(testModeNoCors) .then(testModeCors) - .then(testCredentials) + .then(testSameOriginCredentials) + .then(testCrossOriginCredentials) .then(testRedirects) // Put more promise based tests here. .then(done) .catch(function(e) { ok(false, "Some test failed " + e); done(); }); }