Title: [220497] trunk
Revision
220497
Author
cdu...@apple.com
Date
2017-08-09 16:23:58 -0700 (Wed, 09 Aug 2017)

Log Message

[Beacon][NetworkSession] Support CORS-preflighting on redirects
https://bugs.webkit.org/show_bug.cgi?id=175386
<rdar://problem/33801370>

Reviewed by Youenn Fablet.

Source/WebCore:

Export a couple of WebCore symbols so I can use them in WebKit2.

Tests: http/wpt/beacon/cors/cors-preflight-redirect-failure.html
       http/wpt/beacon/cors/cors-preflight-redirect-from-crossorigin-to-sameorigin.html
       http/wpt/beacon/cors/cors-preflight-redirect-success.html

* loader/CrossOriginAccessControl.h:
* page/SecurityOrigin.h:

Source/WebKit:

Add support to Beacon for doing CORS-preflighting upon redirect to a different
domain.

* NetworkProcess/NetworkCORSPreflightChecker.h:
* NetworkProcess/PingLoad.cpp:
(WebKit::PingLoad::PingLoad):
(WebKit::PingLoad::~PingLoad):
(WebKit::PingLoad::loadRequest):
(WebKit::PingLoad::securityOrigin const):
(WebKit::PingLoad::willPerformHTTPRedirection):
(WebKit::PingLoad::didReceiveResponseNetworkSession):
(WebKit::PingLoad::needsCORSPreflight const):
(WebKit::PingLoad::doCORSPreflight):
* NetworkProcess/PingLoad.h:

LayoutTests:

Add layout test coverage.

* http/wpt/beacon/cors/cors-preflight-redirect-failure-expected.txt: Added.
* http/wpt/beacon/cors/cors-preflight-redirect-failure.html: Added.
* http/wpt/beacon/cors/cors-preflight-redirect-from-crossorigin-to-sameorigin-expected.txt: Added.
* http/wpt/beacon/cors/cors-preflight-redirect-from-crossorigin-to-sameorigin.html: Added.
* http/wpt/beacon/cors/cors-preflight-redirect-success-expected.txt: Added.
* http/wpt/beacon/cors/cors-preflight-redirect-success.html: Added.
* http/wpt/beacon/resources/beacon-preflight.py:
(main):
* http/wpt/beacon/resources/redirect.py: Added.
(main):

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (220496 => 220497)


--- trunk/LayoutTests/ChangeLog	2017-08-09 23:18:13 UTC (rev 220496)
+++ trunk/LayoutTests/ChangeLog	2017-08-09 23:23:58 UTC (rev 220497)
@@ -1,5 +1,26 @@
 2017-08-09  Chris Dumez  <cdu...@apple.com>
 
+        [Beacon][NetworkSession] Support CORS-preflighting on redirects
+        https://bugs.webkit.org/show_bug.cgi?id=175386
+        <rdar://problem/33801370>
+
+        Reviewed by Youenn Fablet.
+
+        Add layout test coverage.
+
+        * http/wpt/beacon/cors/cors-preflight-redirect-failure-expected.txt: Added.
+        * http/wpt/beacon/cors/cors-preflight-redirect-failure.html: Added.
+        * http/wpt/beacon/cors/cors-preflight-redirect-from-crossorigin-to-sameorigin-expected.txt: Added.
+        * http/wpt/beacon/cors/cors-preflight-redirect-from-crossorigin-to-sameorigin.html: Added.
+        * http/wpt/beacon/cors/cors-preflight-redirect-success-expected.txt: Added.
+        * http/wpt/beacon/cors/cors-preflight-redirect-success.html: Added.
+        * http/wpt/beacon/resources/beacon-preflight.py:
+        (main):
+        * http/wpt/beacon/resources/redirect.py: Added.
+        (main):
+
+2017-08-09  Chris Dumez  <cdu...@apple.com>
+
         Unreviewed, deflake http/wpt/beacon/keepalive-after-navigation.html
 
         Temporarily restore previous timeout on this test to address flakiness on the

Modified: trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-arraybufferview-failure.html (220496 => 220497)


--- trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-arraybufferview-failure.html	2017-08-09 23:18:13 UTC (rev 220496)
+++ trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-arraybufferview-failure.html	2017-08-09 23:23:58 UTC (rev 220497)
@@ -18,7 +18,7 @@
   return new Promise(resolve => {
     step_timeout(test.step_func(() => {
       fetch(checkUrl).then(response => {
-        response.text().then(body => {
+        response.json().then(body => {
           resolve(body);
         });
       });
@@ -33,8 +33,7 @@
 
   promise_test(function(test) {
     assert_true(navigator.sendBeacon(testUrl, what), "SendBeacon Succeeded");
-    return pollResult(test, id) .then(json => {
-      result = JSON.parse(json);
+    return pollResult(test, id) .then(result => {
       assert_equals(result['preflight'], 1, "Received preflight")
       assert_equals(result['preflight_referer'], document.URL, "Preflight referer header")
       assert_equals(result['preflight_requested_method'], "POST", "Preflight requested method")

Modified: trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-arraybufferview-success.html (220496 => 220497)


--- trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-arraybufferview-success.html	2017-08-09 23:18:13 UTC (rev 220496)
+++ trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-arraybufferview-success.html	2017-08-09 23:23:58 UTC (rev 220497)
@@ -18,7 +18,7 @@
   return new Promise(resolve => {
     step_timeout(test.step_func(() => {
       fetch(checkUrl).then(response => {
-        response.text().then(body => {
+        response.json().then(body => {
           resolve(body);
         });
       });
@@ -33,8 +33,7 @@
 
   promise_test(function(test) {
     assert_true(navigator.sendBeacon(testUrl, what), "SendBeacon Succeeded");
-    return pollResult(test, id) .then(json => {
-      result = JSON.parse(json);
+    return pollResult(test, id) .then(result => {
       assert_equals(result['preflight'], 1, "Received preflight")
       assert_equals(result['preflight_referer'], document.URL, "Preflight referer header")
       assert_equals(result['preflight_requested_method'], "POST", "Preflight requested method")

Modified: trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-failure.html (220496 => 220497)


--- trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-failure.html	2017-08-09 23:18:13 UTC (rev 220496)
+++ trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-failure.html	2017-08-09 23:23:58 UTC (rev 220497)
@@ -18,7 +18,7 @@
   return new Promise(resolve => {
     step_timeout(test.step_func(() => {
       fetch(checkUrl).then(response => {
-        response.text().then(body => {
+        response.json().then(body => {
           resolve(body);
         });
       });
@@ -33,8 +33,7 @@
 
   promise_test(function(test) {
     assert_true(navigator.sendBeacon(testUrl, what), "SendBeacon Succeeded");
-    return pollResult(test, id) .then(json => {
-      result = JSON.parse(json);
+    return pollResult(test, id) .then(result => {
       assert_equals(result['preflight'], 1, "Received preflight")
       assert_equals(result['preflight_referer'], document.URL, "Preflight referer header")
       assert_equals(result['preflight_requested_method'], "POST", "Preflight requested method")

Modified: trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-success.html (220496 => 220497)


--- trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-success.html	2017-08-09 23:18:13 UTC (rev 220496)
+++ trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-success.html	2017-08-09 23:23:58 UTC (rev 220497)
@@ -18,7 +18,7 @@
   return new Promise(resolve => {
     step_timeout(test.step_func(() => {
       fetch(checkUrl).then(response => {
-        response.text().then(body => {
+        response.json().then(body => {
           resolve(body);
         });
       });
@@ -33,8 +33,7 @@
 
   promise_test(function(test) {
     assert_true(navigator.sendBeacon(testUrl, what), "SendBeacon Succeeded");
-    return pollResult(test, id) .then(json => {
-      result = JSON.parse(json);
+    return pollResult(test, id) .then(result => {
       assert_equals(result['preflight'], 1, "Received preflight")
       assert_equals(result['preflight_referer'], document.URL, "Preflight referer header")
       assert_equals(result['preflight_requested_method'], "POST", "Preflight requested method")

Modified: trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-cookie.html (220496 => 220497)


--- trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-cookie.html	2017-08-09 23:18:13 UTC (rev 220496)
+++ trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-cookie.html	2017-08-09 23:23:58 UTC (rev 220497)
@@ -21,7 +21,7 @@
   return new Promise(resolve => {
     step_timeout(test.step_func(() => {
       fetch(checkUrl).then(response => {
-        response.text().then(body => {
+        response.json().then(body => {
           resolve(body);
         });
       });
@@ -46,8 +46,7 @@
   promise_test(function(test) {
     return fetchCORSCookie(testBase, "testCookie", "/").then(() => {
       assert_true(navigator.sendBeacon(testUrl, what), "SendBeacon Succeeded");
-      return pollResult(test, id).then(json => {
-        result = JSON.parse(json);
+      return pollResult(test, id).then(result => {
         assert_equals(result['preflight'], 1, "Received preflight")
         assert_equals(result['preflight_cookie_header'], "", "Preflight cookie header")
         assert_equals(result['beacon'], 1, "Received beacon")

Added: trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-redirect-failure-expected.txt (0 => 220497)


--- trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-redirect-failure-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-redirect-failure-expected.txt	2017-08-09 23:23:58 UTC (rev 220497)
@@ -0,0 +1,3 @@
+
+PASS CORS preflight failure test 
+

Copied: trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-redirect-failure.html (from rev 220496, trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-failure.html) (0 => 220497)


--- trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-redirect-failure.html	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-redirect-failure.html	2017-08-09 23:23:58 UTC (rev 220497)
@@ -0,0 +1,52 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>SendBeacon CORS preflight on redirect test</title>
+    <script src=""
+    <script src=""
+  </head>
+  <body>
+    <script src=""
+    <script src=""
+    <script>
+var RESOURCES_DIR = "/WebKit/beacon/resources/";
+
+function pollResult(test, id) {
+  var checkUrl = RESOURCES_DIR + "beacon-preflight.py?cmd=get&id=" + id;
+
+  return new Promise(resolve => {
+    step_timeout(test.step_func(() => {
+      fetch(checkUrl).then(response => {
+        response.json().then(body => {
+          resolve(body);
+        });
+      });
+    }), 1000);
+  });
+}
+
+function testCORSPreflightRedirectSuccess(what) {
+  var testBase = get_host_info().HTTP_REMOTE_ORIGIN + RESOURCES_DIR;
+  var id = self.token();
+  var target = encodeURIComponent(testBase + "beacon-preflight.py?allowCors=0&cmd=put&id=" + id);
+
+  // 307 & 308 redirections are the only ones that maintain the POST method.
+  var testUrl = RESOURCES_DIR + "redirect.py?redirect_status=307&location=" + target;
+
+  promise_test(function(test) {
+    assert_true(navigator.sendBeacon(testUrl, what), "SendBeacon Succeeded");
+    return pollResult(test, id) .then(result => {
+      assert_equals(result['preflight'], 1, "Received preflight")
+      assert_equals(result['preflight_referer'], document.URL, "Preflight referer header")
+      assert_equals(result['preflight_requested_method'], "POST", "Preflight requested method")
+      assert_equals(result['beacon'], 0, "Did not receive beacon")
+    });
+  }, "CORS preflight failure test");
+}
+
+let blob = new Blob(["123"], {type: "application/octet-stream"});
+testCORSPreflightRedirectSuccess(blob);
+    </script>
+  </body>
+</html>

Added: trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-redirect-from-crossorigin-to-sameorigin-expected.txt (0 => 220497)


--- trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-redirect-from-crossorigin-to-sameorigin-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-redirect-from-crossorigin-to-sameorigin-expected.txt	2017-08-09 23:23:58 UTC (rev 220497)
@@ -0,0 +1,3 @@
+
+PASS CORS preflight success test 
+

Copied: trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-redirect-from-crossorigin-to-sameorigin.html (from rev 220496, trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-success.html) (0 => 220497)


--- trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-redirect-from-crossorigin-to-sameorigin.html	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-redirect-from-crossorigin-to-sameorigin.html	2017-08-09 23:23:58 UTC (rev 220497)
@@ -0,0 +1,54 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>SendBeacon CORS preflight on redirect test from cross origin to same origin</title>
+    <script src=""
+    <script src=""
+  </head>
+  <body>
+    <script src=""
+    <script src=""
+    <script>
+var RESOURCES_DIR = "/WebKit/beacon/resources/";
+
+function pollResult(test, id) {
+  var checkUrl = RESOURCES_DIR + "beacon-preflight.py?cmd=get&id=" + id;
+
+  return new Promise(resolve => {
+    step_timeout(test.step_func(() => {
+      fetch(checkUrl).then(response => {
+        response.json().then(body => {
+          resolve(body);
+        });
+      });
+    }), 1000);
+  });
+}
+
+function testCORSPreflightRedirectSuccess(what) {
+  var testBase = get_host_info().HTTP_REMOTE_ORIGIN + RESOURCES_DIR;
+  var id = self.token();
+  var target = encodeURIComponent(get_host_info().HTTP_ORIGIN + RESOURCES_DIR + "beacon-preflight.py?allowCors=1&cmd=put&id=" + id);
+
+  // 307 & 308 redirections are the only ones that maintain the POST method.
+  var testUrl = testBase + "redirect.py?redirect_status=307&location=" + target;
+
+  promise_test(function(test) {
+    assert_true(navigator.sendBeacon(testUrl, what), "SendBeacon Succeeded");
+    return pollResult(test, id) .then(result => {
+      assert_equals(result['preflight'], 1, "Received preflight")
+      assert_equals(result['preflight_referer'], document.URL, "Preflight referer header")
+      assert_equals(result['preflight_requested_method'], "POST", "Preflight requested method")
+      assert_equals(result['preflight_origin'], "null", "Received beacon")
+      assert_equals(result['beacon'], 1, "Received beacon")
+      assert_equals(result['beacon_origin'], "null", "Received beacon")
+    });
+  }, "CORS preflight success test");
+}
+
+let blob = new Blob(["123"], {type: "application/octet-stream"});
+testCORSPreflightRedirectSuccess(blob);
+    </script>
+  </body>
+</html>

Added: trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-redirect-success-expected.txt (0 => 220497)


--- trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-redirect-success-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-redirect-success-expected.txt	2017-08-09 23:23:58 UTC (rev 220497)
@@ -0,0 +1,3 @@
+
+PASS CORS preflight success test 
+

Copied: trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-redirect-success.html (from rev 220496, trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-success.html) (0 => 220497)


--- trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-redirect-success.html	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/beacon/cors/cors-preflight-redirect-success.html	2017-08-09 23:23:58 UTC (rev 220497)
@@ -0,0 +1,52 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>SendBeacon CORS preflight on redirect test</title>
+    <script src=""
+    <script src=""
+  </head>
+  <body>
+    <script src=""
+    <script src=""
+    <script>
+var RESOURCES_DIR = "/WebKit/beacon/resources/";
+
+function pollResult(test, id) {
+  var checkUrl = RESOURCES_DIR + "beacon-preflight.py?cmd=get&id=" + id;
+
+  return new Promise(resolve => {
+    step_timeout(test.step_func(() => {
+      fetch(checkUrl).then(response => {
+        response.json().then(body => {
+          resolve(body);
+        });
+      });
+    }), 1000);
+  });
+}
+
+function testCORSPreflightRedirectSuccess(what) {
+  var testBase = get_host_info().HTTP_REMOTE_ORIGIN + RESOURCES_DIR;
+  var id = self.token();
+  var target = encodeURIComponent(testBase + "beacon-preflight.py?allowCors=1&cmd=put&id=" + id);
+
+  // 307 & 308 redirections are the only ones that maintain the POST method.
+  var testUrl = RESOURCES_DIR + "redirect.py?redirect_status=307&location=" + target;
+
+  promise_test(function(test) {
+    assert_true(navigator.sendBeacon(testUrl, what), "SendBeacon Succeeded");
+    return pollResult(test, id) .then(result => {
+      assert_equals(result['preflight'], 1, "Received preflight")
+      assert_equals(result['preflight_referer'], document.URL, "Preflight referer header")
+      assert_equals(result['preflight_requested_method'], "POST", "Preflight requested method")
+      assert_equals(result['beacon'], 1, "Received beacon")
+    });
+  }, "CORS preflight success test");
+}
+
+let blob = new Blob(["123"], {type: "application/octet-stream"});
+testCORSPreflightRedirectSuccess(blob);
+    </script>
+  </body>
+</html>

Modified: trunk/LayoutTests/http/wpt/beacon/resources/beacon-preflight.py (220496 => 220497)


--- trunk/LayoutTests/http/wpt/beacon/resources/beacon-preflight.py	2017-08-09 23:18:13 UTC (rev 220496)
+++ trunk/LayoutTests/http/wpt/beacon/resources/beacon-preflight.py	2017-08-09 23:23:58 UTC (rev 220497)
@@ -35,11 +35,13 @@
       stashed_data['preflight_requested_headers'] = request.headers.get("Access-Control-Request-Headers", "")
       stashed_data['preflight_cookie_header'] = request.headers.get("Cookie", "");
       stashed_data['preflight_referer'] = request.headers.get("Referer", "")
+      stashed_data['preflight_origin'] = request.headers.get("Origin", "")
       request.server.stash.put(test_id, stashed_data)
       return respondToCORSPreflight(request, response)
     elif request.method == "POST":
       stashed_data['beacon'] = 1;
       stashed_data['beacon_cookie_header'] = request.headers.get("Cookie", "")
+      stashed_data['beacon_origin'] = request.headers.get("Origin", "")
       request.server.stash.put(test_id, stashed_data)
     return [("Content-Type", "text/plain")], ""
   

Added: trunk/LayoutTests/http/wpt/beacon/resources/redirect.py (0 => 220497)


--- trunk/LayoutTests/http/wpt/beacon/resources/redirect.py	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/beacon/resources/redirect.py	2017-08-09 23:23:58 UTC (rev 220497)
@@ -0,0 +1,63 @@
+from urllib import urlencode
+from urlparse import urlparse
+
+def main(request, response):
+    stashed_data = {'count': 0, 'preflight': "0"}
+    status = 302
+    headers = [("Content-Type", "text/plain"),
+               ("Cache-Control", "no-cache"),
+               ("Pragma", "no-cache"),
+               ("Access-Control-Allow-Credentials", "true")]
+    headers.append(("Access-Control-Allow-Origin", request.headers.get("Origin", "*")))
+    token = None
+
+    if "token" in request.GET:
+        token = request.GET.first("token")
+        data = ""
+        if data:
+            stashed_data = data
+
+    if request.method == "OPTIONS":
+        requested_method = request.headers.get("Access-Control-Request-Method", None)
+        headers.append(("Access-Control-Allow-Methods", requested_method))
+        requested_headers = request.headers.get("Access-Control-Request-Headers", None)
+        headers.append(("Access-Control-Allow-Headers", requested_headers))
+        stashed_data['preflight'] = "1"
+        #Preflight is not redirected: return 200
+        if not "redirect_preflight" in request.GET:
+            if token:
+              request.server.stash.put(request.GET.first("token"), stashed_data)
+            return 200, headers, ""
+
+    if "redirect_status" in request.GET:
+        status = int(request.GET['redirect_status'])
+
+    stashed_data['count'] += 1
+
+    if "location" in request.GET:
+        url = ""
+        scheme = urlparse(url).scheme
+        if scheme == "" or scheme == "http" or scheme == "https":
+            url += "&" if '?' in url else "?"
+            #keep url parameters in location
+            url_parameters = {}
+            for item in request.GET.items():
+                url_parameters[item[0]] = item[1][0]
+            url += urlencode(url_parameters)
+            #make sure location changes during redirection loop
+            url += "&count=" + str(stashed_data['count'])
+        headers.append(("Location", url))
+
+    if "redirect_referrerpolicy" in request.GET:
+        headers.append(("Referrer-Policy", request.GET['redirect_referrerpolicy']))
+
+    if token:
+        request.server.stash.put(request.GET.first("token"), stashed_data)
+        if "max_count" in request.GET:
+            max_count =  int(request.GET['max_count'])
+            #stop redirecting and return count
+            if stashed_data['count'] > max_count:
+                # -1 because the last is not a redirection
+                return str(stashed_data['count'] - 1)
+
+    return status, headers, ""

Modified: trunk/Source/WebCore/ChangeLog (220496 => 220497)


--- trunk/Source/WebCore/ChangeLog	2017-08-09 23:18:13 UTC (rev 220496)
+++ trunk/Source/WebCore/ChangeLog	2017-08-09 23:23:58 UTC (rev 220497)
@@ -1,3 +1,20 @@
+2017-08-09  Chris Dumez  <cdu...@apple.com>
+
+        [Beacon][NetworkSession] Support CORS-preflighting on redirects
+        https://bugs.webkit.org/show_bug.cgi?id=175386
+        <rdar://problem/33801370>
+
+        Reviewed by Youenn Fablet.
+
+        Export a couple of WebCore symbols so I can use them in WebKit2.
+
+        Tests: http/wpt/beacon/cors/cors-preflight-redirect-failure.html
+               http/wpt/beacon/cors/cors-preflight-redirect-from-crossorigin-to-sameorigin.html
+               http/wpt/beacon/cors/cors-preflight-redirect-success.html
+
+        * loader/CrossOriginAccessControl.h:
+        * page/SecurityOrigin.h:
+
 2017-08-09  Jeremy Jones  <jere...@apple.com>
 
         Use MPAVRoutingController instead of deprecated versions.

Modified: trunk/Source/WebCore/loader/CrossOriginAccessControl.h (220496 => 220497)


--- trunk/Source/WebCore/loader/CrossOriginAccessControl.h	2017-08-09 23:18:13 UTC (rev 220496)
+++ trunk/Source/WebCore/loader/CrossOriginAccessControl.h	2017-08-09 23:23:58 UTC (rev 220497)
@@ -40,7 +40,7 @@
 bool isSimpleCrossOriginAccessRequest(const String& method, const HTTPHeaderMap&);
 bool isOnAccessControlSimpleRequestMethodWhitelist(const String&);
 
-void updateRequestForAccessControl(ResourceRequest&, SecurityOrigin&, StoredCredentials);
+WEBCORE_EXPORT void updateRequestForAccessControl(ResourceRequest&, SecurityOrigin&, StoredCredentials);
 WEBCORE_EXPORT ResourceRequest createAccessControlPreflightRequest(const ResourceRequest&, SecurityOrigin&, const String&);
 
 bool isValidCrossOriginRedirectionURL(const URL&);

Modified: trunk/Source/WebCore/page/SecurityOrigin.h (220496 => 220497)


--- trunk/Source/WebCore/page/SecurityOrigin.h	2017-08-09 23:18:13 UTC (rev 220496)
+++ trunk/Source/WebCore/page/SecurityOrigin.h	2017-08-09 23:23:58 UTC (rev 220497)
@@ -50,7 +50,7 @@
     };
 
     WEBCORE_EXPORT static Ref<SecurityOrigin> create(const URL&);
-    static Ref<SecurityOrigin> createUnique();
+    WEBCORE_EXPORT static Ref<SecurityOrigin> createUnique();
 
     WEBCORE_EXPORT static Ref<SecurityOrigin> createFromString(const String&);
     WEBCORE_EXPORT static Ref<SecurityOrigin> create(const String& protocol, const String& host, std::optional<uint16_t> port);

Modified: trunk/Source/WebKit/ChangeLog (220496 => 220497)


--- trunk/Source/WebKit/ChangeLog	2017-08-09 23:18:13 UTC (rev 220496)
+++ trunk/Source/WebKit/ChangeLog	2017-08-09 23:23:58 UTC (rev 220497)
@@ -1,3 +1,26 @@
+2017-08-09  Chris Dumez  <cdu...@apple.com>
+
+        [Beacon][NetworkSession] Support CORS-preflighting on redirects
+        https://bugs.webkit.org/show_bug.cgi?id=175386
+        <rdar://problem/33801370>
+
+        Reviewed by Youenn Fablet.
+
+        Add support to Beacon for doing CORS-preflighting upon redirect to a different
+        domain.
+
+        * NetworkProcess/NetworkCORSPreflightChecker.h:
+        * NetworkProcess/PingLoad.cpp:
+        (WebKit::PingLoad::PingLoad):
+        (WebKit::PingLoad::~PingLoad):
+        (WebKit::PingLoad::loadRequest):
+        (WebKit::PingLoad::securityOrigin const):
+        (WebKit::PingLoad::willPerformHTTPRedirection):
+        (WebKit::PingLoad::didReceiveResponseNetworkSession):
+        (WebKit::PingLoad::needsCORSPreflight const):
+        (WebKit::PingLoad::doCORSPreflight):
+        * NetworkProcess/PingLoad.h:
+
 2017-08-09  Jeremy Jones  <jere...@apple.com>
 
         Use MPAVRoutingController instead of deprecated versions.

Modified: trunk/Source/WebKit/NetworkProcess/NetworkCORSPreflightChecker.h (220496 => 220497)


--- trunk/Source/WebKit/NetworkProcess/NetworkCORSPreflightChecker.h	2017-08-09 23:18:13 UTC (rev 220496)
+++ trunk/Source/WebKit/NetworkProcess/NetworkCORSPreflightChecker.h	2017-08-09 23:23:58 UTC (rev 220497)
@@ -54,6 +54,7 @@
 
     NetworkCORSPreflightChecker(Parameters&&, CompletionCallback&&);
     ~NetworkCORSPreflightChecker();
+    const WebCore::ResourceRequest& originalRequest() const { return m_parameters.originalRequest; }
 
     void startPreflight();
 

Modified: trunk/Source/WebKit/NetworkProcess/PingLoad.cpp (220496 => 220497)


--- trunk/Source/WebKit/NetworkProcess/PingLoad.cpp	2017-08-09 23:18:13 UTC (rev 220496)
+++ trunk/Source/WebKit/NetworkProcess/PingLoad.cpp	2017-08-09 23:23:58 UTC (rev 220497)
@@ -32,6 +32,7 @@
 #include "Logging.h"
 #include "NetworkCORSPreflightChecker.h"
 #include "SessionTracker.h"
+#include <WebCore/CrossOriginAccessControl.h>
 
 #define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(m_parameters.sessionID.isAlwaysOnLoggingAllowed(), Network, "%p - PingLoad::" fmt, this, ##__VA_ARGS__)
 
@@ -42,6 +43,7 @@
 PingLoad::PingLoad(NetworkResourceLoadParameters&& parameters)
     : m_parameters(WTFMove(parameters))
     , m_timeoutTimer(*this, &PingLoad::timeoutTimerFired)
+    , m_isSameOriginRequest(securityOrigin().canRequest(m_parameters.request.url()))
 {
     // If the server never responds, this object will hang around forever.
     // Set a very generous timeout, just in case.
@@ -50,11 +52,14 @@
     if (needsCORSPreflight(m_parameters.request))
         doCORSPreflight(m_parameters.request);
     else
-        startNetworkLoad();
+        loadRequest(m_parameters.request);
 }
 
 PingLoad::~PingLoad()
 {
+    if (m_redirectHandler)
+        m_redirectHandler({ });
+
     if (m_task) {
         ASSERT(m_task->client() == this);
         m_task->clearClient();
@@ -62,22 +67,53 @@
     }
 }
 
-void PingLoad::startNetworkLoad()
+void PingLoad::loadRequest(const ResourceRequest& request)
 {
     RELEASE_LOG_IF_ALLOWED("startNetworkLoad");
     if (auto* networkSession = SessionTracker::networkSession(m_parameters.sessionID)) {
-        m_task = NetworkDataTask::create(*networkSession, *this, m_parameters);
+        auto loadParameters = m_parameters;
+        loadParameters.request = request;
+        m_task = NetworkDataTask::create(*networkSession, *this, WTFMove(loadParameters));
         m_task->resume();
     } else
         ASSERT_NOT_REACHED();
 }
 
-void PingLoad::willPerformHTTPRedirection(ResourceResponse&&, ResourceRequest&& request, RedirectCompletionHandler&& completionHandler)
+SecurityOrigin& PingLoad::securityOrigin() const
 {
-    RELEASE_LOG_IF_ALLOWED("willPerformHTTPRedirection");
-    // FIXME: Do a CORS preflight if necessary.
+    return m_origin ? *m_origin : *m_parameters.sourceOrigin;
+}
+
+void PingLoad::willPerformHTTPRedirection(ResourceResponse&& redirectResponse, ResourceRequest&& request, RedirectCompletionHandler&& completionHandler)
+{
+    RELEASE_LOG_IF_ALLOWED("willPerformHTTPRedirection - shouldFollowRedirects? %d", m_parameters.shouldFollowRedirects);
+    if (!m_parameters.shouldFollowRedirects) {
+        completionHandler({ });
+        return;
+    }
+    // FIXME: Do CSP check.
     // FIXME: We should ensure the number of redirects does not exceed 20.
-    completionHandler(m_parameters.shouldFollowRedirects ? request : ResourceRequest());
+    if (!needsCORSPreflight(request)) {
+        completionHandler(request);
+        return;
+    }
+    RELEASE_LOG_IF_ALLOWED("willPerformHTTPRedirection - Redirect requires a CORS preflight");
+
+    // Use a unique origin for subsequent loads if needed.
+    // https://fetch.spec.whatwg.org/#concept-http-redirect-fetch (Step 10).
+    ASSERT(m_parameters.mode == FetchOptions::Mode::Cors);
+    if (!securityOrigin().canRequest(redirectResponse.url()) && !protocolHostAndPortAreEqual(redirectResponse.url(), request.url())) {
+        if (!m_origin || !m_origin->isUnique())
+            m_origin = SecurityOrigin::createUnique();
+    }
+
+    m_isSameOriginRequest = false;
+    m_redirectHandler = WTFMove(completionHandler);
+
+    // Let's fetch the request with the original headers (equivalent to request cloning specified by fetch algorithm).
+    request.setHTTPHeaderFields(m_parameters.request.httpHeaderFields());
+
+    doCORSPreflight(request);
 }
 
 void PingLoad::didReceiveChallenge(const AuthenticationChallenge&, ChallengeCompletionHandler&& completionHandler)
@@ -87,9 +123,9 @@
     delete this;
 }
 
-void PingLoad::didReceiveResponseNetworkSession(ResourceResponse&&, ResponseCompletionHandler&& completionHandler)
+void PingLoad::didReceiveResponseNetworkSession(ResourceResponse&& response, ResponseCompletionHandler&& completionHandler)
 {
-    RELEASE_LOG_IF_ALLOWED("didReceiveResponseNetworkSession");
+    RELEASE_LOG_IF_ALLOWED("didReceiveResponseNetworkSession - httpStatusCode: %d", response.httpStatusCode());
     completionHandler(PolicyAction::PolicyIgnore);
     delete this;
 }
@@ -133,11 +169,10 @@
 
 bool PingLoad::needsCORSPreflight(const ResourceRequest& request) const
 {
-    if (m_parameters.mode == FetchOptions::Mode::Cors) {
-        ASSERT(m_parameters.sourceOrigin);
-        return !m_parameters.sourceOrigin->canRequest(request.url());
-    }
-    return false;
+    if (m_parameters.mode == FetchOptions::Mode::NoCors)
+        return false;
+
+    return !m_isSameOriginRequest || !securityOrigin().canRequest(request.url());
 }
 
 void PingLoad::doCORSPreflight(const ResourceRequest& request)
@@ -144,19 +179,23 @@
 {
     RELEASE_LOG_IF_ALLOWED("doCORSPreflight");
     ASSERT(!m_corsPreflightChecker);
-    ASSERT(m_parameters.sourceOrigin);
 
     NetworkCORSPreflightChecker::Parameters parameters = {
         request,
-        *m_parameters.sourceOrigin,
+        securityOrigin(),
         m_parameters.sessionID,
         m_parameters.allowStoredCredentials
     };
     m_corsPreflightChecker = std::make_unique<NetworkCORSPreflightChecker>(WTFMove(parameters), [this](NetworkCORSPreflightChecker::Result result) {
-        RELEASE_LOG_IF_ALLOWED("doCORSPreflight complete, success: %d", result == NetworkCORSPreflightChecker::Result::Success);
+        RELEASE_LOG_IF_ALLOWED("doCORSPreflight complete, success: %d forRedirect? %d", result == NetworkCORSPreflightChecker::Result::Success, !!m_redirectHandler);
+        auto corsPreflightChecker = WTFMove(m_corsPreflightChecker);
         if (result == NetworkCORSPreflightChecker::Result::Success) {
-            m_corsPreflightChecker = nullptr;
-            startNetworkLoad();
+            ResourceRequest actualRequest = corsPreflightChecker->originalRequest();
+            updateRequestForAccessControl(actualRequest, securityOrigin(), m_parameters.allowStoredCredentials);
+            if (auto redirectHandler = std::exchange(m_redirectHandler, nullptr))
+                redirectHandler(actualRequest);
+            else
+                loadRequest(actualRequest);
         } else
             delete this;
     });

Modified: trunk/Source/WebKit/NetworkProcess/PingLoad.h (220496 => 220497)


--- trunk/Source/WebKit/NetworkProcess/PingLoad.h	2017-08-09 23:18:13 UTC (rev 220496)
+++ trunk/Source/WebKit/NetworkProcess/PingLoad.h	2017-08-09 23:23:58 UTC (rev 220497)
@@ -51,14 +51,19 @@
     void cannotShowURL() final;
     void timeoutTimerFired();
 
-    void startNetworkLoad();
+    void loadRequest(const WebCore::ResourceRequest&);
     bool needsCORSPreflight(const WebCore::ResourceRequest&) const;
     void doCORSPreflight(const WebCore::ResourceRequest&);
+
+    WebCore::SecurityOrigin& securityOrigin() const;
     
     NetworkResourceLoadParameters m_parameters;
     RefPtr<NetworkDataTask> m_task;
     WebCore::Timer m_timeoutTimer;
     std::unique_ptr<NetworkCORSPreflightChecker> m_corsPreflightChecker;
+    RefPtr<WebCore::SecurityOrigin> m_origin;
+    bool m_isSameOriginRequest;
+    RedirectCompletionHandler m_redirectHandler;
 };
 
 }
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to