[ResourceTiming] Refactor connection-reuse tests

This CL migrates the connection reuse WPTs to the new style. See
wpt/resource-timing/CodingConventions.md for details.

These tests exist to verify that the Resource Timing API yields correct
PerformanceResourceTiming values when the underlying TCP connection is
reused across multiple subresources.

Bug: 1171767
Change-Id: I5acdb6bf8a5e71bb34c7d2236cdb9d0209d3d04d
GithubIssue: https://github.com/w3c/resource-timing/issues/254
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2950880
Commit-Queue: Tom McKee <tommckee@chromium.org>
Reviewed-by: Yoav Weiss <yoavweiss@chromium.org>
Cr-Commit-Position: refs/heads/master@{#891368}
diff --git a/resource-timing/connection-reuse.html b/resource-timing/connection-reuse.html
index e82b7bc..a1bc927 100644
--- a/resource-timing/connection-reuse.html
+++ b/resource-timing/connection-reuse.html
@@ -7,8 +7,46 @@
 <link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
 <script src="resources/entry-invariants.js"></script>
 <script src="resources/connection-reuse-test.js"></script>
+<script>
+  const {HTTPS_ORIGIN} = get_host_info();
+
+  // Fetches the given subresource a couple times with the same connection.
+  const http_path = "resources/fake_responses.py";
+  connection_reuse_test(http_path,
+    {
+      'on_200': invariants.assert_tao_pass_no_redirect_http,
+      'on_304': invariants.assert_tao_pass_304_not_modified_http,
+    }, "Reuse HTTP connection");
+
+  // Like above, but the subresource is fetched over HTTPS while this page is
+  // fetched over HTTP.
+  const https_url = `${HTTPS_ORIGIN}/resource-timing/${http_path}`;
+  connection_reuse_test(https_url,
+    {
+      'on_200': invariants.assert_tao_pass_no_redirect_https,
+      'on_304': invariants.assert_tao_pass_304_not_modified_https,
+    }, "Reuse HTTPS connection from HTTP page");
+
+  // Like the above mixed-content test but the final resource is behind an HTTP
+  // redirect response.
+  const redirect_path = (() => {
+    // The resource behind the redirect is the same fake_responses.py handler
+    // on the HTTPS origin. Pass it through encodeURIComponent so that it can
+    // be passed through a query-parameter.
+    const redirect_url = encodeURIComponent(https_url)
+    // The request is made to the HTTPS origin with a query parameter that will
+    // cause a 302 response.
+    return `${https_url}?redirect=${redirect_url}`;
+  })();
+  connection_reuse_test(redirect_path,
+    {
+      'on_200': invariants.assert_tao_enabled_cross_origin_redirected_resource,
+      'on_304': invariants.assert_tao_enabled_cross_origin_redirected_resource,
+    }, "Reuse HTTPS connection with redirects from an HTTP page");
+</script>
 </head>
 <body>
 <h1>Description</h1>
diff --git a/resource-timing/connection-reuse.https.html b/resource-timing/connection-reuse.https.html
index e82b7bc..3461eed 100644
--- a/resource-timing/connection-reuse.https.html
+++ b/resource-timing/connection-reuse.https.html
@@ -9,6 +9,13 @@
 <script src="/resources/testharnessreport.js"></script>
 <script src="resources/entry-invariants.js"></script>
 <script src="resources/connection-reuse-test.js"></script>
+<script>
+  connection_reuse_test("resources/fake_responses.py",
+    {
+      'on_200': invariants.assert_tao_pass_no_redirect_https,
+      'on_304': invariants.assert_tao_pass_304_not_modified_https,
+    }, "Reuse an HTTPS connection");
+</script>
 </head>
 <body>
 <h1>Description</h1>
diff --git a/resource-timing/resources/connection-reuse-test.js b/resource-timing/resources/connection-reuse-test.js
index 4def16c..453fbd3 100644
--- a/resource-timing/resources/connection-reuse-test.js
+++ b/resource-timing/resources/connection-reuse-test.js
@@ -1,70 +1,63 @@
 // This script is loaded in HTTP and HTTPS contexts to validate
 // PerformanceResourceTiming entries' attributes when reusing connections.
-
-// Make the first request before calling 'attribute_test' so that only the
-// second request's PerformanceResourceTiming entry will be interrogated.
-// We don't check the first request's PerformanceResourceTiming entry because
-// that's not what this test is trying to validate.
 //
-// Note: to ensure that we reuse the connection to fetch multiple resources,
-// we use the same XMLHttpRequest object for each request. Although it doesn't
-// seem to be specified, each browser tested by WPT will reuse the underlying
-// TCP connection with this approach. Pre-establishing the XHR's connection
-// helps us to test connection reuse also in browsers that may key their
-// connections on the related request's credentials mode.
-const client = new XMLHttpRequest();
-const identifier = Math.random();
-const path = `resources/fake_responses.py?tag=${identifier}`;
-client.open("GET", path, false);
-client.send();
+// Note: to ensure that we reuse the connection to fetch multiple resources, we
+// use the same XMLHttpRequest object throughout an individual test. Although
+// it doesn't seem to be specified, each browser tested by WPT will reuse the
+// underlying TCP connection with this approach. Pre-establishing the XHR's
+// connection helps us to test connection reuse also in browsers that may key
+// their connections on the related request's credentials mode.
 
-attribute_test(
-  async () => {
-    client.open("GET", path + "&same_resource=false", false);
-    client.send();
+const connection_reuse_test = (path, follow_on_assertions, test_label) => {
+  const {on_200, on_304} = follow_on_assertions;
 
-    // We expect to get a 200 Ok response because we've requested a different
-    // resource than previous requests.
-    if (client.status != 200) {
-      throw new Error(`Got something other than a 200 response. ` +
-                      `client.status: ${client.status}`);
-    }
-  }, path, entry => {
-    invariants.assert_connection_reused(entry);
+  // Make the first request before calling 'attribute_test' so that only the
+  // second request's PerformanceResourceTiming entry will be interrogated. We
+  // don't check the first request's PerformanceResourceTiming entry because
+  // that's not what this test is trying to validate.
+  const client = new XMLHttpRequest();
+  const identifier = Math.random();
+  path = `${path}?tag=${identifier}`;
+  client.open("GET", path, false);
+  client.send();
 
-    // The entry must also follow the specification for any entry corresponding
-    // to a 'typical' 200 Ok response.
-    if (self.location.protocol == 'https:') {
-      invariants.assert_tao_pass_no_redirect_https(entry);
-    } else {
-      invariants.assert_tao_pass_no_redirect_http(entry);
-    }
-  },
-  "PerformanceResrouceTiming entries need to conform to the spec when a " +
-  "distinct resource is fetched over a persistent connection");
+  attribute_test(
+    async () => {
+      client.open("GET", path + "&same_resource=false", false);
+      client.send();
 
-attribute_test(
-  async () => {
-    client.open("GET", path, false);
-    client.setRequestHeader("If-None-Match", identifier);
-    client.send();
+      // We expect to get a 200 Ok response because we've requested a different
+      // resource than previous requests.
+      if (client.status != 200) {
+        throw new Error(`Got something other than a 200 response. ` +
+                        `client.status: ${client.status}`);
+      }
+    }, path, entry => {
+      invariants.assert_connection_reused(entry);
+      on_200(entry);
+    },
+    `PerformanceResrouceTiming entries need to conform to the spec when a ` +
+    `distinct resource is fetched over a persistent connection ` +
+    `(${test_label})`);
 
-    // We expect to get a 304 Not Modified response because we've used a
-    // matching 'identifier' for the If-None-Match header.
-    if (client.status != 304) {
-      throw new Error(`Got something other than a 304 response. ` +
-                      `client.status: ${client.status}`);
-    }
-  }, path, entry => {
-    invariants.assert_connection_reused(entry);
+  attribute_test(
+    async () => {
+      client.open("GET", path, false);
+      client.setRequestHeader("If-None-Match", identifier);
+      client.send();
 
-    // The entry must also follow the specification for any entry corresponding
-    // to a 'typical' 304 Not Modified response.
-    if (self.location.protocol == 'https:') {
-      invariants.assert_tao_pass_304_not_modified_https(entry);
-    } else {
-      invariants.assert_tao_pass_304_not_modified_http(entry);
-    }
-  },
-  "PerformanceResrouceTiming entries need to conform to the spec when the " +
-  "resource is cache-revalidated over a persistent connection");
+      // We expect to get a 304 Not Modified response because we've used a
+      // matching 'identifier' for the If-None-Match header.
+      if (client.status != 304) {
+        throw new Error(`Got something other than a 304 response. ` +
+                        `client.status: ${client.status}, response: ` +
+                        `'${client.responseText}'`);
+      }
+    }, path, entry => {
+      invariants.assert_connection_reused(entry);
+      on_304(entry);
+    },
+    `PerformanceResrouceTiming entries need to conform to the spec when the ` +
+    `resource is cache-revalidated over a persistent connection ` +
+    `(${test_label})`);
+}
diff --git a/resource-timing/resources/fake_responses.py b/resource-timing/resources/fake_responses.py
index 828748a..e33adbf 100644
--- a/resource-timing/resources/fake_responses.py
+++ b/resource-timing/resources/fake_responses.py
@@ -1,6 +1,12 @@
 # /xhr/resources/conditional.py -- to fake a 304 response
 
 def main(request, response):
+    if request.method == "OPTIONS":
+        # Assume this is a CORS preflight
+        response.headers.set(b"Access-Control-Allow-Headers", "*")
+        response.headers.set(b"Access-Control-Allow-Origin", "*")
+        response.status = (204, "No Content")
+        return b""
     tag = request.GET.first(b"tag", None)
     redirect = request.GET.first(b"redirect", None)
     match = request.headers.get(b"If-None-Match", None)