Files
sousa-gecko/testing/web-platform/tests/fetch/compression-dictionary/dictionary-match.tentative.https.html
T
Patrick Meenan cd6d515c99 Bug 2046345 [wpt PR 60518] - Compression dictionary: Prioritize match-dest over pattern length, a=testonly
Automatic update from web-platform-tests
Compression dictionary: Prioritize match-dest over pattern length

This fixes an edge case where we weren't aligned with the RFC and added
more web platform test coverage. The RFC explicitly states that
dictionaries with a matching match-dest take priority over those that
are wildcard matches.

Web Platform Test updates:
  - Improved the test cases that have unknown values in match-dest.
  - Improved the testing around valid and invalid characters in "id".
  - Tested more edge cases for dictionary match prioritization.

Bug: 521299806, 40255884
Change-Id: If8f544bfd5df70f6d2ffdd8870696ff1006361d8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7909546
Reviewed-by: Frédéric Wang Nélar <fwang@igalia.com>
Reviewed-by: Tsuyoshi Horo <horo@chromium.org>
Commit-Queue: Patrick Meenan <pmeenan@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1645339}

--

wpt-commits: 21c3d7bf2c741fa4ef461f6a72ac6e8a63063fb7
wpt-pr: 60518
2026-06-16 11:35:29 +00:00

182 lines
8.0 KiB
HTML

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="/common/utils.js"></script>
<script src="./resources/compression-dictionary-util.sub.js"></script>
</head>
<body>
<script>
compression_dictionary_promise_test(async (t) => {
const match_dest = encodeURIComponent('("asdf")');
const pattern = encodeURIComponent(
'/fetch/compression-dictionary/resources/echo-headers.py');
await fetch(
`${kRegisterDictionaryPath}?id=id1&match-dest=${match_dest}&match=${pattern}`);
await registerAltDictionaryAndWait(t);
const headers = await (await fetch('./resources/echo-headers.py')).json();
assert_false("available-dictionary" in headers);
}, 'Dictionary with unknown match-dest is not used for fetch() API');
compression_dictionary_promise_test(async (t) => {
const match_dest = encodeURIComponent('()');
const dict = await (await fetch(`${kRegisterDictionaryPath}?id=test&match-dest=${match_dest}`)).text();
assert_equals(
await waitUntilAvailableDictionaryHeader(t, {}),
kDefaultDictionaryHashBase64);
assert_equals(await checkHeader('dictionary-id', {}), '"test"');
}, 'Dictionary registration with empty match-dest list acts as wildcard');
compression_dictionary_promise_test(async (t) => {
const match_dest = encodeURIComponent('("")');
const dict = await (await fetch(`${kRegisterDictionaryPath}?id=test&match-dest=${match_dest}`)).text();
assert_equals(
await waitUntilAvailableDictionaryHeader(t, {}),
kDefaultDictionaryHashBase64);
assert_equals(await checkHeader('dictionary-id', {}), '"test"');
}, 'Dictionary registration with matching fetch destination');
compression_dictionary_promise_test(async (t) => {
const match_dest = encodeURIComponent('("script")');
const pattern = encodeURIComponent(
'/fetch/compression-dictionary/resources/echo-headers.py');
await fetch(
`${kRegisterDictionaryPath}?id=id1&match-dest=${match_dest}&match=${pattern}`);
await registerAltDictionaryAndWait(t);
const headers = await (await fetch('./resources/echo-headers.py')).json();
assert_false("available-dictionary" in headers);
}, 'Dictionary with non-matching match-dest is not used');
compression_dictionary_promise_test(async (t) => {
const match_dest = encodeURIComponent('("asdf" "")');
const dict = await (await fetch(`${kRegisterDictionaryPath}?id=test&match-dest=${match_dest}`)).text();
assert_equals(
await waitUntilAvailableDictionaryHeader(t, {}),
kDefaultDictionaryHashBase64);
assert_equals(await checkHeader('dictionary-id', {}), '"test"');
}, 'Dictionary registration with unknown match-dest and empty string matches fetch destination');
compression_dictionary_promise_test(async (t) => {
const match_specific = encodeURIComponent(
'/fetch/compression-dictionary/resources/echo-headers.py');
const match_medium = encodeURIComponent(
'/fetch/compression-dictionary/resources/*');
const match_general = encodeURIComponent(
'/fetch/compression-dictionary/*');
const dictionary_path_specific =
`${kRegisterDictionaryPath}?id=specific&content=specific&` +
`match=${match_specific}`;
const dictionary_path_medium =
`${kRegisterDictionaryPath}?id=medium&content=medium&` +
`match=${match_medium}`;
const dictionary_path_general =
`${kRegisterDictionaryPath}?id=general&content=general&` +
`match=${match_general}`;
const expected_specific_hash = await calculateDictionaryHash('specific');
const expected_medium_hash = await calculateDictionaryHash('medium');
await fetch(dictionary_path_specific);
await fetch(dictionary_path_medium);
await fetch(dictionary_path_general);
// echo-headers.py matches all 3, specific (length 55) is the most specific.
const hash_specific = await waitUntilAvailableDictionaryHeader(t, {
expected_header: expected_specific_hash
});
assert_equals(hash_specific, expected_specific_hash);
// echo-headers2.py matches medium (length 41) and general (length 31),
// medium is more specific.
const hash_medium = await waitUntilAvailableDictionaryHeader(t, {
expected_header: expected_medium_hash,
use_alt_path: true
});
assert_equals(hash_medium, expected_medium_hash);
}, 'Best match algorithm prioritizes dictionaries with longer match patterns');
compression_dictionary_promise_test(async (t) => {
const match_longer_nodest = encodeURIComponent(
'/fetch/compression-dictionary/resources/echo-headers*.py');
const match_medium_dest = encodeURIComponent(
'/fetch/compression-dictionary/*/echo-headers2.py');
const match_shorter_dest = encodeURIComponent(
'/fetch/compression-dictionary/*');
const match_dest = encodeURIComponent('("")');
const dictionary_path_longer_nodest =
`${kRegisterDictionaryPath}?id=longer-nodest&content=longer-nodest&` +
`match=${match_longer_nodest}`;
const dictionary_path_medium_dest =
`${kRegisterDictionaryPath}?id=medium-dest&content=medium-dest&` +
`match-dest=${match_dest}&match=${match_medium_dest}`;
const dictionary_path_shorter_dest =
`${kRegisterDictionaryPath}?id=shorter-dest&content=shorter-dest&` +
`match-dest=${match_dest}&match=${match_shorter_dest}`;
const expected_longer_hash = await calculateDictionaryHash('longer-nodest');
const expected_medium_hash = await calculateDictionaryHash('medium-dest');
const expected_shorter_hash = await calculateDictionaryHash('shorter-dest');
await fetch(dictionary_path_longer_nodest);
await fetch(dictionary_path_medium_dest);
await fetch(dictionary_path_shorter_dest);
// echo-headers.py matches longer-nodest (no dest) and shorter-dest (with dest).
// shorter-dest wins because it has match-dest.
const hash_selected = await waitUntilAvailableDictionaryHeader(t, {
expected_header: expected_shorter_hash
});
assert_equals(hash_selected, expected_shorter_hash);
// echo-headers2.py matches all three. medium-dest and shorter-dest have dest.
// medium-dest wins because it has a longer match pattern.
const hash_medium = await waitUntilAvailableDictionaryHeader(t, {
expected_header: expected_medium_hash,
use_alt_path: true
});
assert_equals(hash_medium, expected_medium_hash);
}, 'match-dest presence takes priority over longer match patterns');
compression_dictionary_promise_test(async (t) => {
const match_specific_1 = encodeURIComponent(
'/fetch/compression-dictionary/resources/echo-headers*.py');
const match_specific_2 = encodeURIComponent(
'/fetch/compression-dictionary/resources/echo-headers.py*');
const dictionary_path_first =
`${kRegisterDictionaryPath}?id=first-fetched&content=first-fetched-content&` +
`match=${match_specific_1}`;
const dictionary_path_second =
`${kRegisterDictionaryPath}?id=second-fetched&content=second-fetched-content&` +
`match=${match_specific_2}`;
const expected_first_hash = await calculateDictionaryHash('first-fetched-content');
const expected_second_hash = await calculateDictionaryHash('second-fetched-content');
await (await fetch(dictionary_path_first)).text();
assert_equals(
await waitUntilAvailableDictionaryHeader(t, {}), expected_first_hash);
// Wait a short delay to ensure different fetch times.
await new Promise(resolve => t.step_timeout(resolve, 50));
await (await fetch(dictionary_path_second)).text();
// We must wait until second-fetched is registered.
const hash_selected = await waitUntilAvailableDictionaryHeader(t, {
expected_header: expected_second_hash
});
assert_equals(hash_selected, expected_second_hash);
// The first dictionary should still be available for paths that only match it.
assert_equals(
await waitUntilAvailableDictionaryHeader(t, {
expected_header: expected_first_hash,
use_alt_path: true
}),
expected_first_hash);
}, 'Most recently fetched dictionary takes priority when match lengths are equal');
</script>
</body>