compression-dictionary-util.sub.js (5698B)
1 const SAME_ORIGIN = "https://{{host}}:{{ports[https][0]}}"; 2 const CROSS_ORIGIN = "https://{{hosts[alt][www]}}:{{ports[https][0]}}"; 3 4 const RESOURCES_PATH = "/fetch/compression-dictionary/resources"; 5 const SAME_ORIGIN_RESOURCES_URL = SAME_ORIGIN + RESOURCES_PATH; 6 const CROSS_ORIGIN_RESOURCES_URL = CROSS_ORIGIN + RESOURCES_PATH; 7 8 const kDefaultDictionaryContent = 'This is a test dictionary.\n'; 9 const kDefaultDictionaryHashBase64 = 10 ':U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:'; 11 const kRegisterDictionaryPath = './resources/register-dictionary.py'; 12 const kCompressedDataPath = './resources/compressed-data.py'; 13 const kExpectedCompressedData = 14 `This is compressed test data using a test dictionary`; 15 const kCheckHeaderMaxRetry = 10; 16 const kCheckHeaderRetryTimeout = 200; 17 const kCheckPreviousRequestHeadersMaxRetry = 5; 18 const kCheckPreviousRequestHeadersRetryTimeout = 250; 19 20 // Gets the remote URL corresponding to `relative_path`. 21 function getRemoteHostUrl(relative_path) { 22 const remote_origin = new URL(get_host_info().HTTPS_REMOTE_ORIGIN); 23 let result = new URL(relative_path, location.href); 24 result.protocol = remote_origin.protocol; 25 result.hostname = remote_origin.hostname; 26 result.port = remote_origin.port; 27 return result.href; 28 } 29 30 // Calculates the Structured Field Byte Sequence containing the SHA-256 hash of 31 // the contents of the dictionary text. 32 async function calculateDictionaryHash(dictionary_text) { 33 const encoded = (new TextEncoder()).encode(dictionary_text); 34 const digest = await crypto.subtle.digest('SHA-256', encoded) 35 return ':' + btoa(String.fromCharCode(...new Uint8Array(digest))) + ':'; 36 } 37 38 // Checks the HTTP request headers which is sent to the server. 39 async function checkHeaders({check_remote = false, use_alt_path = false}) { 40 let url = use_alt_path ? './resources/echo-headers2.py' : 41 './resources/echo-headers.py'; 42 if (check_remote) { 43 url = getRemoteHostUrl(url); 44 } 45 return await (await fetch(url)).json(); 46 } 47 48 // Checks the specified header in the HTTP request headers. 49 async function checkHeader(header, { 50 check_remote = false, 51 use_alt_path = false}) { 52 return (await checkHeaders({check_remote: check_remote, use_alt_path: use_alt_path}))[header]; 53 } 54 55 // Waits until the specified header is available in the HTTP 56 // request headers, and returns the header. If the header is not available after 57 // the specified number of retries, returns an error message. If the 58 // `expected_header` is specified, this method waits until the header is 59 // available and matches the `expected_header`. 60 async function waitUntilHeader(test, header, { 61 max_retry = kCheckHeaderMaxRetry, 62 expected_header = undefined, 63 check_remote = false, 64 use_alt_path = false 65 }) { 66 for (let retry_count = 0; retry_count <= max_retry; retry_count++) { 67 const response_header = await checkHeader(header, {check_remote: check_remote, 68 use_alt_path: use_alt_path}); 69 if (response_header) { 70 if (expected_header === undefined || response_header == expected_header) { 71 return response_header; 72 } 73 } 74 await new Promise( 75 (resolve) => test.step_timeout( 76 resolve, kCheckHeaderRetryTimeout)); 77 } 78 return `"${header}" header is not available`; 79 } 80 81 async function waitUntilAvailableDictionaryHeader(test, { 82 max_retry = kCheckHeaderMaxRetry, 83 expected_header = undefined, 84 check_remote = false, 85 use_alt_path = false 86 }) { 87 return waitUntilHeader(test, 'available-dictionary', { 88 max_retry: max_retry, 89 expected_header: expected_header, 90 check_remote: check_remote, 91 use_alt_path: use_alt_path 92 }); 93 } 94 95 // Checks the HTTP request headers which was sent to the server with `token` 96 // to register a dictionary. 97 async function checkPreviousRequestHeaders(token, check_remote = false) { 98 let url = `./resources/register-dictionary.py?get_previous_header=${token}`; 99 if (check_remote) { 100 url = getRemoteHostUrl(url); 101 } 102 return await (await fetch(url)).json(); 103 } 104 105 // Waits until the HTTP request headers which was sent to the server with 106 // `token` to register a dictionary is available, and returns the header. If the 107 // header is not available after the specified number of retries, returns 108 // `undefined`. 109 async function waitUntilPreviousRequestHeaders( 110 test, token, check_remote = false) { 111 for (let retry_count = 0; retry_count <= kCheckPreviousRequestHeadersMaxRetry; 112 retry_count++) { 113 const header = 114 (await checkPreviousRequestHeaders(token, check_remote))['headers']; 115 if (header) { 116 return header; 117 } 118 await new Promise( 119 (resolve) => test.step_timeout( 120 resolve, kCheckPreviousRequestHeadersRetryTimeout)); 121 } 122 return undefined; 123 } 124 125 // Clears the site data for the specified directive by sending a request to 126 // `./resources/clear-site-data.py` which returns `Clear-Site-Data` response 127 // header. 128 // Note: When `directive` is 'cache' or 'cookies' is specified, registered 129 // compression dictionaries should be also cleared. 130 async function clearSiteData(directive = 'cache') { 131 return await (await fetch( 132 `./resources/clear-site-data.py?directive=${directive}`)) 133 .text(); 134 } 135 136 // A utility test method that adds the `clearSiteData()` method to the 137 // testharness cleanup function. This is intended to ensure that registered 138 // dictionaries are cleared in tests and that registered dictionaries do not 139 // interfere with subsequent tests. 140 function compression_dictionary_promise_test(func, name, properties) { 141 promise_test(async (test) => { 142 test.add_cleanup(clearSiteData); 143 await func(test); 144 }, name, properties); 145 }