From f88ac14ebaf9206cc08af481c2332b8dbef11a59 Mon Sep 17 00:00:00 2001 From: Kenichi Ishibashi Date: Sun, 27 Mar 2022 23:50:19 -0700 Subject: [PATCH] Add WPTs for different Early Hints preload finish timings The test scenarios are: * 103 -> start preload -> preload finished -> 200 -> response body. * 103 -> start preload -> (preload inflight) -> 200 -> preload finished -> response body. The purpose of these tests is to make sure the resource is added to the preload map of the document [1]. Also refer to [2] for the motivation. [1] https://github.com/whatwg/html/pull/7675 [2] https://github.com/web-platform-tests/wpt/issues/33076#issuecomment-1077575167 Bug: 1305896 Change-Id: Id0ba4748f6493bc2c78be6de5769b4d16ce9092f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3552176 Reviewed-by: Yutaka Hirano Commit-Queue: Kenichi Ishibashi Cr-Commit-Position: refs/heads/main@{#985847} --- ...inished-before-final-response.h2.window.js | 11 +++++++ ...receiving-final-response-body.h2.window.js | 11 +++++++ .../early-hints/resources/delayed-js.h2.py | 10 +++--- .../resources/fetch-and-record-js.h2.py | 3 ++ ...eload-finished-before-final-response.h2.py | 31 +++++++++++++++++++ ...reload-finished-before-final-response.html | 15 +++++++++ ...-while-receiving-final-response-body.h2.py | 31 +++++++++++++++++++ ...d-while-receiving-final-response-body.html | 15 +++++++++ loading/early-hints/resources/utils.py | 20 +++++++++--- 9 files changed, 137 insertions(+), 10 deletions(-) create mode 100644 loading/early-hints/preload-finished-before-final-response.h2.window.js create mode 100644 loading/early-hints/preload-finished-while-receiving-final-response-body.h2.window.js create mode 100644 loading/early-hints/resources/preload-finished-before-final-response.h2.py create mode 100644 loading/early-hints/resources/preload-finished-before-final-response.html create mode 100644 loading/early-hints/resources/preload-finished-while-receiving-final-response-body.h2.py create mode 100644 loading/early-hints/resources/preload-finished-while-receiving-final-response-body.html diff --git a/loading/early-hints/preload-finished-before-final-response.h2.window.js b/loading/early-hints/preload-finished-before-final-response.h2.window.js new file mode 100644 index 00000000000000..c63239be1f5942 --- /dev/null +++ b/loading/early-hints/preload-finished-before-final-response.h2.window.js @@ -0,0 +1,11 @@ +// META: script=/common/utils.js +// META: script=resources/early-hints-helpers.sub.js + +test(() => { + const params = new URLSearchParams(); + const id = token(); + params.set("resource-url", SAME_ORIGIN_RESOURCES_URL + "/fetch-and-record-js.h2.py?id=" + id); + params.set("resource-id", id); + const test_url = "resources/preload-finished-before-final-response.h2.py?" + params.toString(); + window.location.replace(new URL(test_url, window.location)); +}); \ No newline at end of file diff --git a/loading/early-hints/preload-finished-while-receiving-final-response-body.h2.window.js b/loading/early-hints/preload-finished-while-receiving-final-response-body.h2.window.js new file mode 100644 index 00000000000000..bb03902bd14492 --- /dev/null +++ b/loading/early-hints/preload-finished-while-receiving-final-response-body.h2.window.js @@ -0,0 +1,11 @@ +// META: script=/common/utils.js +// META: script=resources/early-hints-helpers.sub.js + +test(() => { + const params = new URLSearchParams(); + const id = token(); + params.set("resource-url", SAME_ORIGIN_RESOURCES_URL + "/fetch-and-record-js.h2.py?id=" + id); + params.set("resource-id", id); + const test_url = "resources/preload-finished-while-receiving-final-response-body.h2.py?" + params.toString(); + window.location.replace(new URL(test_url, window.location)); +}); \ No newline at end of file diff --git a/loading/early-hints/resources/delayed-js.h2.py b/loading/early-hints/resources/delayed-js.h2.py index b3d28e86da353a..93865b930da392 100644 --- a/loading/early-hints/resources/delayed-js.h2.py +++ b/loading/early-hints/resources/delayed-js.h2.py @@ -1,14 +1,12 @@ -import time +import importlib + +utils = importlib.import_module("loading.early-hints.resources.utils") def main(request, response): id = request.GET.first(b"id") - url_dir = u'/'.join(request.url_parts.path.split(u'/')[:-1]) + u'/' # Wait until the id is set via resume-delayed-js.h2.py. - while True: - if request.server.stash.take(id, url_dir): - break - time.sleep(0.1) + utils.wait_for_preload_to_finish(request, id) headers = [ ("Content-Type", "text/javascript"), diff --git a/loading/early-hints/resources/fetch-and-record-js.h2.py b/loading/early-hints/resources/fetch-and-record-js.h2.py index d1b5d0e1cb0284..169f70c0450c79 100644 --- a/loading/early-hints/resources/fetch-and-record-js.h2.py +++ b/loading/early-hints/resources/fetch-and-record-js.h2.py @@ -1,4 +1,5 @@ import importlib +import time utils = importlib.import_module("loading.early-hints.resources.utils") @@ -10,4 +11,6 @@ def main(request, response): ("Cache-Control", "max-age=600"), ] body = "/*empty script*/" + # Sleep to simulate loading time. + time.sleep(0.05) return (200, "OK"), headers, body diff --git a/loading/early-hints/resources/preload-finished-before-final-response.h2.py b/loading/early-hints/resources/preload-finished-before-final-response.h2.py new file mode 100644 index 00000000000000..d0b12408d9cff0 --- /dev/null +++ b/loading/early-hints/resources/preload-finished-before-final-response.h2.py @@ -0,0 +1,31 @@ +import importlib +import os + +utils = importlib.import_module("loading.early-hints.resources.utils") + + +def handle_headers(frame, request, response): + resource_url = request.GET.first(b"resource-url").decode() + link_header_value = "<{}>; rel=preload; as=script".format(resource_url) + early_hints = [ + (b":status", b"103"), + (b"link", link_header_value), + ] + response.writer.write_raw_header_frame(headers=early_hints, + end_headers=True) + + # Wait for preload to finish before sending the final response headers. + resource_id = request.GET.first(b"resource-id").decode() + utils.wait_for_preload_to_finish(request, resource_id) + + response.status = 200 + response.headers[b"content-type"] = "text/html" + response.write_status_headers() + + +def main(request, response): + current_dir = os.path.dirname(os.path.realpath(__file__)) + file_path = os.path.join(current_dir, "preload-finished-before-final-response.html") + with open(file_path, "r") as f: + test_content = f.read() + response.writer.write_data(item=test_content, last=True) diff --git a/loading/early-hints/resources/preload-finished-before-final-response.html b/loading/early-hints/resources/preload-finished-before-final-response.html new file mode 100644 index 00000000000000..d965b4042039db --- /dev/null +++ b/loading/early-hints/resources/preload-finished-before-final-response.html @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/loading/early-hints/resources/preload-finished-while-receiving-final-response-body.h2.py b/loading/early-hints/resources/preload-finished-while-receiving-final-response-body.h2.py new file mode 100644 index 00000000000000..1ba486002c0acd --- /dev/null +++ b/loading/early-hints/resources/preload-finished-while-receiving-final-response-body.h2.py @@ -0,0 +1,31 @@ +import importlib +import os + +utils = importlib.import_module("loading.early-hints.resources.utils") + + +def handle_headers(frame, request, response): + resource_url = request.GET.first(b"resource-url").decode() + link_header_value = "<{}>; rel=preload; as=script".format(resource_url) + early_hints = [ + (b":status", b"103"), + (b"link", link_header_value), + ] + response.writer.write_raw_header_frame(headers=early_hints, + end_headers=True) + + response.status = 200 + response.headers[b"content-type"] = "text/html" + response.write_status_headers() + + +def main(request, response): + # Wait for preload to finish before sending the response body. + resource_id = request.GET.first(b"resource-id").decode() + utils.wait_for_preload_to_finish(request, resource_id) + + current_dir = os.path.dirname(os.path.realpath(__file__)) + file_path = os.path.join(current_dir, "preload-finished-while-receiving-final-response-body.html") + with open(file_path, "r") as f: + test_content = f.read() + response.writer.write_data(item=test_content, last=True) diff --git a/loading/early-hints/resources/preload-finished-while-receiving-final-response-body.html b/loading/early-hints/resources/preload-finished-while-receiving-final-response-body.html new file mode 100644 index 00000000000000..5a233612b27d68 --- /dev/null +++ b/loading/early-hints/resources/preload-finished-while-receiving-final-response-body.html @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/loading/early-hints/resources/utils.py b/loading/early-hints/resources/utils.py index aa6f331b4196cf..f24638ab3ac1d9 100644 --- a/loading/early-hints/resources/utils.py +++ b/loading/early-hints/resources/utils.py @@ -1,5 +1,6 @@ import datetime import json +import time def _url_dir(request): @@ -22,12 +23,23 @@ def store_request_timing_and_headers(request): request.server.stash.put(id, value, url_dir) -def get_request_timing_and_headers(request): +def get_request_timing_and_headers(request, id=None): """Get previously stored timestamp and request headers associated with the - given request. The request must be a GET request and must have the "id" - parameter. + given "id". When "id" is not given the id is retrieved from "request". """ - id = request.GET.first(b"id") + if id is None: + id = request.GET.first(b"id") url_dir = _url_dir(request) item = request.server.stash.take(id, url_dir) + if not item: + return None return json.dumps(item) + + +def wait_for_preload_to_finish(request, id): + """Wait until a preload associated with "id" is sent.""" + while True: + if get_request_timing_and_headers(request, id): + break + time.sleep(0.1) + time.sleep(0.1)