Skip to content

Commit 27d2ee7

Browse files
committed
Test link rel=compression-dictionary header as early hints
Spec PR: whatwg/html#11620 This is currently only implemented in Firefox but not covered by any test: https://searchfox.org/firefox-main/rev/5e465cbe324a291bb69aa85c5c4231e052cabe9a/netwerk/protocol/http/EarlyHintsService.cpp#106
1 parent aa26903 commit 27d2ee7

5 files changed

Lines changed: 90 additions & 6 deletions

fetch/compression-dictionary/dictionary-fetch-with-link-element.tentative.https.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
getRemoteHostUrl(`${kRegisterDictionaryPath}?save_header=${dict_token}`);
5050
addLinkRelCompressionDictionaryElement(url, 'anonymous');
5151
const headers = await waitUntilPreviousRequestHeaders(
52-
t, dict_token, /*check_remote=*/ true);
52+
t, dict_token, {check_remote: true});
5353
assert_true(headers !== undefined, 'Headers should be available');
5454
assert_equals(headers['sec-fetch-mode'], 'cors');
5555

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<!DOCTYPE html>
2+
<script src="/common/utils.js"></script>
3+
<script src="./resources/compression-dictionary-util.sub.js"></script>
4+
<script>
5+
const dict_token = token();
6+
const url = new URL(
7+
`${kRegisterDictionaryHttp2Path}?save_header=${dict_token}`, location.href);
8+
navigateToTestWithCompressionDictionaryEarlyHints("resources/dictionary-fetch-with-link-header-in-early-hints.tentative.https.h2.html", url.href);
9+
</script>

fetch/compression-dictionary/resources/compression-dictionary-util.sub.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,9 @@ async function waitUntilAvailableDictionaryHeader(test, {
9696

9797
// Checks the HTTP request headers which was sent to the server with `token`
9898
// to register a dictionary.
99-
async function checkPreviousRequestHeaders(token, check_remote = false) {
100-
let url = `./resources/register-dictionary.py?get_previous_header=${token}`;
99+
async function checkPreviousRequestHeaders(token, options = {}) {
100+
const { check_remote = false, use_http2 = false } = options;
101+
let url = `${use_http2 ? kRegisterDictionaryHttp2Path : kRegisterDictionaryPath}?get_previous_header=${token}`;
101102
if (check_remote) {
102103
url = getRemoteHostUrl(url);
103104
}
@@ -108,12 +109,12 @@ async function checkPreviousRequestHeaders(token, check_remote = false) {
108109
// `token` to register a dictionary is available, and returns the header. If the
109110
// header is not available after the specified number of retries, returns
110111
// `undefined`.
111-
async function waitUntilPreviousRequestHeaders(
112-
test, token, check_remote = false) {
112+
async function waitUntilPreviousRequestHeaders(test, token, options = {}) {
113+
const { check_remote = false, use_http2 = false } = options;
113114
for (let retry_count = 0; retry_count <= kCheckPreviousRequestHeadersMaxRetry;
114115
retry_count++) {
115116
const header =
116-
(await checkPreviousRequestHeaders(token, check_remote))['headers'];
117+
(await checkPreviousRequestHeaders(token, {check_remote, use_http2}))['headers'];
117118
if (header) {
118119
return header;
119120
}
@@ -156,3 +157,11 @@ async function registerAltDictionaryAndWait(t) {
156157
await waitUntilAvailableDictionaryHeader(t, {use_alt_path: true}),
157158
kDefaultDictionaryHashBase64);
158159
}
160+
161+
function navigateToTestWithCompressionDictionaryEarlyHints(test_url, dictionary_url) {
162+
const params = new URLSearchParams();
163+
params.set("test_url", test_url);
164+
params.set("dictionary_url", dictionary_url);
165+
const url = `${RESOURCES_PATH}/early-hint-for-compression-dictionary-test-loader.h2.py?${params.toString()}`;
166+
window.location.replace(new URL(url, window.location));
167+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<!DOCTYPE html>
2+
<head>
3+
<meta charset="utf-8">
4+
<meta name="timeout" content="long"/>
5+
<script src="/resources/testharness.js"></script>
6+
<script src="/resources/testharnessreport.js"></script>
7+
<script src="/common/get-host-info.sub.js"></script>
8+
<script src="/common/utils.js"></script>
9+
<!-- resources/early-hint-for-compression-dictionary-test-loader.h2.py loads
10+
this file but it really uses links relative to the parent directory. -->
11+
<base href="../">
12+
<script src="./resources/compression-dictionary-util.sub.js"></script>
13+
</head>
14+
<body>
15+
<script>
16+
17+
compression_dictionary_promise_test(async (t) => {
18+
const searchParams = new URLSearchParams(location.search);
19+
const dictionary_url = searchParams.get("dictionary_url");
20+
const dict_token = (new URL(dictionary_url)).searchParams.get("save_header");
21+
const headers = await waitUntilPreviousRequestHeaders(t, dict_token, {use_http2: true});
22+
assert_true(headers !== undefined, 'Headers should be available');
23+
// Wait until `available-dictionary` header is available.
24+
assert_equals(
25+
await waitUntilAvailableDictionaryHeader(t, {}),
26+
kDefaultDictionaryHashBase64);
27+
// Check if the data compressed using Brotli with the dictionary can be
28+
// decompressed.
29+
const data_url = `${kCompressedDataHttp2Path}?content_encoding=dcb`;
30+
assert_equals(await (await fetch(data_url)).text(), kExpectedCompressedData);
31+
}, 'Fetch dictionary using link header in early hint');
32+
33+
</script>
34+
</body>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import os
2+
import time
3+
4+
def _remove_relative_resources_prefix(path):
5+
if path.startswith("resources/"):
6+
return path[len("resources/"):]
7+
return path
8+
9+
def handle_headers(frame, request, response):
10+
# Send a 103 response.
11+
dictionary_url = request.GET.first(b"dictionary_url").decode()
12+
link_header_value = "<{}>; rel=\"compression-dictionary\"".format(dictionary_url).encode()
13+
early_hints = [
14+
(b":status", b"103"),
15+
(b"link", link_header_value),
16+
]
17+
response.writer.write_raw_header_frame(headers=early_hints,
18+
end_headers=True)
19+
20+
# Delay before sending the 200 response.
21+
time.sleep(0.2)
22+
response.status = 200
23+
response.headers[b"content-type"] = "text/html"
24+
response.write_status_headers()
25+
26+
def main(request, response):
27+
test_path = _remove_relative_resources_prefix(
28+
request.GET[b"test_url"].decode("utf-8"))
29+
current_dir = os.path.dirname(os.path.realpath(__file__))
30+
file_path = os.path.join(current_dir, test_path)
31+
test_content = open(file_path, "r").read()
32+
response.writer.write_data(item=test_content, last=True)

0 commit comments

Comments
 (0)