tor-browser

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

network_bench.js (8956B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /* eslint-env node */
      6 
      7 const { logTest, logTask } = require("./utils/profiling");
      8 
      9 const fs = require("fs");
     10 const path = require("path");
     11 
     12 async function waitForH3(maxRetries, browser, url, commands, context) {
     13  let attempt = 0;
     14  while (attempt < maxRetries) {
     15    await commands.navigate(url);
     16 
     17    // Get performance entries to check the protocol
     18    let protocolInfo = await commands.js.run(
     19      `
     20      // Get all performance entries
     21      const entries = performance.getEntries();
     22 
     23      // Create an array to store the results
     24      const protocolInfo = entries.map(entry => ({
     25          name: entry.name,
     26          protocol: entry.nextHopProtocol,
     27      }));
     28 
     29      return protocolInfo;
     30      `
     31    );
     32 
     33    // Log the protocol information
     34    context.log.info("protocolInfo: " + JSON.stringify(protocolInfo));
     35 
     36    // Check if the main document is using h3 protocol
     37    const normalizeUrl = url => url.replace(/\/+$/, "");
     38    const isH3 = protocolInfo.some(
     39      entry =>
     40        normalizeUrl(entry.name) === normalizeUrl(url) &&
     41        entry.protocol === "h3"
     42    );
     43 
     44    if (isH3) {
     45      context.log.info("Protocol h3 detected!");
     46      return; // Exit the function if h3 is found
     47    }
     48 
     49    // Increment the attempt counter and retry
     50    attempt++;
     51    context.log.info(
     52      `Retry attempt ${attempt} - Protocol is not h3. Retrying...`
     53    );
     54    if (browser === "firefox") {
     55      const script = `
     56      Services.obs.notifyObservers(null, "net:cancel-all-connections");
     57      Services.obs.notifyObservers(null, "network:reset-http3-excluded-list");
     58    `;
     59      commands.js.runPrivileged(script);
     60    }
     61    context.log.info("Waiting 3s to close connection...");
     62    await commands.wait.byTime(3000);
     63  }
     64 }
     65 
     66 async function waitForComplete(timeout, commands, context, id) {
     67  let starttime = performance.now();
     68  let status = "";
     69  let goodput = 0;
     70 
     71  while (performance.now() - starttime < timeout) {
     72    status = await commands.js.run(
     73      `return document.getElementById('${id}').innerHTML;`
     74    );
     75 
     76    if (status.startsWith("success")) {
     77      goodput = parseFloat(status.split(":")[1]);
     78      status = "success";
     79      break;
     80    } else if (status.startsWith("error")) {
     81      break;
     82    }
     83 
     84    await commands.wait.byTime(1000);
     85  }
     86 
     87  let endtime = performance.now();
     88 
     89  return {
     90    start: starttime,
     91    end: endtime,
     92    status,
     93    goodput,
     94  };
     95 }
     96 
     97 module.exports = logTest(
     98  "download/upload test",
     99  async function (context, commands) {
    100    let serverUrl = `${context.options.browsertime.server_url}`;
    101    let iterations = `${context.options.browsertime.iterations}`;
    102    const [protocol, testType] =
    103      `${context.options.browsertime.test_type}`.split("_");
    104 
    105    await commands.measure.start(serverUrl);
    106    let accumulatedResults = [];
    107    let browserName = "";
    108    let file_size = parseInt(context.options.browsertime.test_file_size, 10);
    109    if (Number.isNaN(file_size)) {
    110      // default is 32MB
    111      file_size = 32000000;
    112    }
    113    for (let iteration = 0; iteration < iterations; iteration++) {
    114      await logTask(context, "cycle " + iteration, async function () {
    115        const driver = context.selenium.driver;
    116        const webdriver = context.selenium.webdriver;
    117        let capabilities = await driver.getCapabilities();
    118        browserName = capabilities.get("browserName");
    119 
    120        if (protocol === "h3") {
    121          await waitForH3(10, browserName, serverUrl, commands, context);
    122        } else {
    123          await commands.navigate(serverUrl);
    124        }
    125 
    126        if (testType === "download") {
    127          const downloadItem = await driver.findElement(
    128            webdriver.By.id("downloadBtn")
    129          );
    130 
    131          const actions = driver.actions({ async: true });
    132          await actions.move({ origin: downloadItem }).click().perform();
    133 
    134          // Start the test and wait for the upload to complete
    135          let results = await waitForComplete(
    136            1200000,
    137            commands,
    138            context,
    139            "download_status"
    140          );
    141          let downloadTime = results.end - results.start;
    142          // Store result in megabit/seconds
    143          let downloadGoodput = (file_size * 8) / ((downloadTime / 1000) * 1e6);
    144          context.log.info(
    145            "download results: " +
    146              results.status +
    147              " duration: " +
    148              downloadTime +
    149              "ms, downloadGoodput: " +
    150              downloadGoodput +
    151              "Mbit/s"
    152          );
    153          accumulatedResults.push(downloadGoodput);
    154        } else if (testType === "upload") {
    155          const uploadItem = await driver.findElement(
    156            webdriver.By.id("fileUpload")
    157          );
    158 
    159          let tagName = await uploadItem.getTagName();
    160          if (tagName === "input") {
    161            if (context.options.browsertime.moz_fetch_dir == "None") {
    162              context.log.error(
    163                "This test depends on the fetch task. Download the file, 'https://github.com/mozilla/perf-automation/raw/master/test_files/upload-test-32MB.dat' and set the os environment variable MOZ_FETCHES_DIR to that directory."
    164              );
    165            }
    166 
    167            let localFilePath = path.join(
    168              `${context.options.browsertime.moz_fetch_dir}`,
    169              `${context.options.browsertime.test_file_name}`
    170            );
    171            if (!fs.existsSync(localFilePath)) {
    172              localFilePath = path.join(
    173                `${context.options.browsertime.moz_fetch_dir}`,
    174                "upload-test-32MB.dat"
    175              );
    176            }
    177 
    178            context.log.info("Sending file path: " + localFilePath);
    179            await uploadItem.sendKeys(localFilePath);
    180          } else {
    181            const actions = driver.actions({ async: true });
    182            await actions.move({ origin: uploadItem }).click().perform();
    183          }
    184 
    185          // Start the test and wait for the upload to complete
    186          let results = await waitForComplete(
    187            1200000,
    188            commands,
    189            context,
    190            "upload_status"
    191          );
    192          let uploadTime = results.end - results.start;
    193 
    194          // Store result in megabit/seconds
    195          let uploadGoodput = Number.isNaN(results.goodput)
    196            ? (file_size * 8) / ((uploadTime / 1000) * 1e6)
    197            : results.goodput;
    198          context.log.info(
    199            "upload results: " +
    200              results.status +
    201              " duration: " +
    202              uploadTime +
    203              "ms, uploadGoodput: " +
    204              uploadGoodput +
    205              "Mbit/s"
    206          );
    207          if (results.status === "success") {
    208            accumulatedResults.push(uploadGoodput);
    209          }
    210        } else {
    211          context.log.error("Unsupported test type:" + testType);
    212        }
    213      });
    214 
    215      if (browserName === "firefox" && protocol === "h3") {
    216        const statsScript = `
    217        const gDashboard = Cc["@mozilla.org/network/dashboard;1"].getService(
    218          Ci.nsIDashboard
    219        );
    220        let promise = new Promise((resolve, reject) => {
    221          gDashboard.requestHttp3ConnectionStats((data) => {
    222            resolve(data);
    223          });
    224        });
    225        let done = false;
    226        let result = null;
    227        let error = null;
    228        promise
    229          .catch(e => {
    230            error = e;
    231          })
    232          .then(r => {
    233            result = r;
    234            done = true;
    235          });
    236        // Spin the event loop until done becomes true.
    237        Services.tm.spinEventLoopUntil(
    238          "requestHttpConnections",
    239          () => done
    240        );
    241        return result;
    242        `;
    243        let res = await commands.js.runPrivileged(statsScript);
    244        context.log.info("HTTP/3 Connection Stats" + JSON.stringify(res));
    245      }
    246 
    247      // No need to close the connection at the last run.
    248      if (iteration != iterations - 1) {
    249        await commands.navigate("about:blank");
    250        if (browserName === "firefox") {
    251          const script = `
    252      Services.obs.notifyObservers(null, "net:cancel-all-connections");
    253    `;
    254          commands.js.runPrivileged(script);
    255          context.log.info("Waiting 3s to close connection...");
    256          await commands.wait.byTime(3000);
    257        } else {
    258          // TODO: configiure a shorter idle timeout at server side, so we
    259          // don't have to wait that long.
    260          context.log.info("Waiting 35s to close connection...");
    261          await commands.wait.byTime(35000);
    262        }
    263      }
    264    }
    265 
    266    if (testType === "download") {
    267      commands.measure.addObject({
    268        custom_data: { "download-goodput": accumulatedResults },
    269      });
    270    } else if (testType === "upload") {
    271      commands.measure.addObject({
    272        custom_data: { "upload-goodput": accumulatedResults },
    273      });
    274    }
    275  }
    276 );