tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }