tor-browser

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

browser_net_resend.js (12369B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 *  http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 /**
      7 * Tests if resending a request works.
      8 */
      9 
     10 add_task(async function () {
     11  if (
     12    Services.prefs.getBoolPref(
     13      "devtools.netmonitor.features.newEditAndResend",
     14      true
     15    )
     16  ) {
     17    await testResendRequest();
     18  } else {
     19    await testOldEditAndResendPanel();
     20  }
     21 });
     22 
     23 // This tests resending a request without editing using
     24 // the resend context menu item. This particularly covering
     25 // the new resend functionality.
     26 async function testResendRequest() {
     27  const { tab, monitor } = await initNetMonitor(POST_DATA_URL, {
     28    requestCount: 1,
     29  });
     30  info("Starting test... ");
     31 
     32  const { document, store, windowRequire } = monitor.panelWin;
     33 
     34  // Action should be processed synchronously in tests.
     35  const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
     36  store.dispatch(Actions.batchEnable(false));
     37 
     38  await performRequests(monitor, tab, 2);
     39 
     40  is(
     41    document.querySelectorAll(".request-list-item").length,
     42    2,
     43    "There are currently two requests"
     44  );
     45 
     46  const firstResend = await resendRequestAndWaitForNewRequest(
     47    monitor,
     48    document.querySelectorAll(".request-list-item")[0]
     49  );
     50 
     51  Assert.notStrictEqual(
     52    firstResend.originalResource.resourceId,
     53    firstResend.newResource.resourceId,
     54    "The resent request is different resource from the first request"
     55  );
     56 
     57  is(
     58    firstResend.originalResource.url,
     59    firstResend.newResource.url,
     60    "The resent request has the same url and query parameters and the first request"
     61  );
     62 
     63  // The priority header only appears when the urgency and incremental values
     64  // are not both default values (u=3 and i=false). In this case the original
     65  // request has no priority header and the resent request does, hence we subtract one.
     66  is(
     67    firstResend.originalResource.requestHeaders.headers.length,
     68    firstResend.newResource.requestHeaders.headers.length - 1,
     69    "The no of headers are the same"
     70  );
     71 
     72  // Because a resent request has a different purpose and principal it will
     73  // also have a different CoS flag (meaning a different priority header).
     74  // So we can't compare the original and resent request's priority and skip it.
     75  firstResend.originalResource.requestHeaders.headers.forEach(
     76    ({ name, value }) => {
     77      if (name === "Priority") {
     78        return;
     79      }
     80      const foundHeader = firstResend.newResource.requestHeaders.headers.find(
     81        header => header.name == name
     82      );
     83      is(
     84        value,
     85        foundHeader.value,
     86        `The '${name}' header for the request and the resent request match`
     87      );
     88    }
     89  );
     90 
     91  info("Check that the custom headers and form data are resent correctly");
     92  const secondResend = await resendRequestAndWaitForNewRequest(
     93    monitor,
     94    document.querySelectorAll(".request-list-item")[1]
     95  );
     96 
     97  Assert.notStrictEqual(
     98    secondResend.originalResource.resourceId,
     99    secondResend.newResource.resourceId,
    100    "The resent request is different resource from the second request"
    101  );
    102 
    103  const customHeader =
    104    secondResend.originalResource.requestHeaders.headers.find(
    105      header => header.name == "custom-header-xxx"
    106    );
    107 
    108  const customHeaderInResentRequest =
    109    secondResend.newResource.requestHeaders.headers.find(
    110      header => header.name == "custom-header-xxx"
    111    );
    112 
    113  is(
    114    customHeader.value,
    115    customHeaderInResentRequest.value,
    116    "The custom header in the resent request is the same as the second request"
    117  );
    118 
    119  is(
    120    customHeaderInResentRequest.value,
    121    "custom-value-xxx",
    122    "The custom header in the resent request is correct"
    123  );
    124 
    125  is(
    126    secondResend.originalResource.requestPostData.postData.text,
    127    secondResend.newResource.requestPostData.postData.text,
    128    "The form data in the resent is the same as the second request"
    129  );
    130 }
    131 
    132 async function resendRequestAndWaitForNewRequest(monitor, originalRequestItem) {
    133  const { document, store, windowRequire, connector } = monitor.panelWin;
    134  const { getSelectedRequest, getDisplayedRequests } = windowRequire(
    135    "devtools/client/netmonitor/src/selectors/index"
    136  );
    137 
    138  info("Select the request to resend");
    139  const expectedNoOfRequestsAfterResend =
    140    getDisplayedRequests(store.getState()).length + 1;
    141 
    142  const waitForHeaders = waitUntil(() =>
    143    document.querySelector(".headers-overview")
    144  );
    145  EventUtils.sendMouseEvent({ type: "mousedown" }, originalRequestItem);
    146  await waitForHeaders;
    147 
    148  const originalResourceId = getSelectedRequest(store.getState()).id;
    149 
    150  const waitForNewRequest = waitUntil(
    151    () =>
    152      getDisplayedRequests(store.getState()).length ==
    153        expectedNoOfRequestsAfterResend &&
    154      getSelectedRequest(store.getState()).id !== originalResourceId
    155  );
    156 
    157  info("Open the context menu and select the resend for the request");
    158  EventUtils.sendMouseEvent({ type: "contextmenu" }, originalRequestItem);
    159  const wait = waitForNetworkEvents(monitor, 1);
    160  await selectContextMenuItem(monitor, "request-list-context-resend-only");
    161  await waitForNewRequest;
    162  await wait;
    163 
    164  const newResourceId = getSelectedRequest(store.getState()).id;
    165 
    166  // Make sure we fetch the request headers and post data for the
    167  // new request so we can assert them.
    168  await connector.requestData(newResourceId, "requestHeaders");
    169  await connector.requestData(newResourceId, "requestPostData");
    170 
    171  return {
    172    originalResource: getRequestById(store.getState(), originalResourceId),
    173    newResource: getRequestById(store.getState(), newResourceId),
    174  };
    175 }
    176 
    177 // This is a basic test for the old edit and resend panel
    178 // This should be removed soon in Bug 1745416 when we remove
    179 // the old panel functionality.
    180 async function testOldEditAndResendPanel() {
    181  const ADD_QUERY = "t1=t2";
    182  const ADD_HEADER = "Test-header: true";
    183  const ADD_UA_HEADER = "User-Agent: Custom-Agent";
    184  const ADD_POSTDATA = "&t3=t4";
    185 
    186  const { tab, monitor } = await initNetMonitor(POST_DATA_URL, {
    187    requestCount: 1,
    188  });
    189  info("Starting test... ");
    190 
    191  const { document, store, windowRequire } = monitor.panelWin;
    192  const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
    193  const { getSelectedRequest, getSortedRequests } = windowRequire(
    194    "devtools/client/netmonitor/src/selectors/index"
    195  );
    196 
    197  store.dispatch(Actions.batchEnable(false));
    198 
    199  // Execute requests.
    200  await performRequests(monitor, tab, 2);
    201 
    202  const origItemId = getSortedRequests(store.getState())[0].id;
    203 
    204  store.dispatch(Actions.selectRequest(origItemId));
    205  await waitForRequestData(
    206    store,
    207    ["requestHeaders", "requestPostData"],
    208    origItemId
    209  );
    210 
    211  let origItem = getSortedRequests(store.getState())[0];
    212 
    213  // add a new custom request cloned from selected request
    214 
    215  store.dispatch(Actions.cloneSelectedRequest());
    216  await testCustomForm(origItem);
    217 
    218  let customItem = getSelectedRequest(store.getState());
    219  testCustomItem(customItem, origItem);
    220 
    221  // edit the custom request
    222  await editCustomForm();
    223 
    224  // FIXME: reread the customItem, it's been replaced by a new object (immutable!)
    225  customItem = getSelectedRequest(store.getState());
    226  testCustomItemChanged(customItem, origItem);
    227 
    228  // send the new request
    229  const wait = waitForNetworkEvents(monitor, 1);
    230  store.dispatch(Actions.sendCustomRequest());
    231  await wait;
    232 
    233  let sentItem;
    234  // Testing sent request will require updated requestHeaders and requestPostData,
    235  // we must wait for both properties get updated before starting test.
    236  await waitUntil(() => {
    237    sentItem = getSelectedRequest(store.getState());
    238    origItem = getSortedRequests(store.getState())[0];
    239    return (
    240      sentItem &&
    241      sentItem.requestHeaders &&
    242      sentItem.requestPostData &&
    243      origItem &&
    244      origItem.requestHeaders &&
    245      origItem.requestPostData
    246    );
    247  });
    248 
    249  await testSentRequest(sentItem, origItem);
    250 
    251  // Ensure the UI shows the new request, selected, and that the detail panel was closed.
    252  is(
    253    getSortedRequests(store.getState()).length,
    254    3,
    255    "There are 3 requests shown"
    256  );
    257  is(
    258    document
    259      .querySelector(".request-list-item.selected")
    260      .getAttribute("data-id"),
    261    sentItem.id,
    262    "The sent request is selected"
    263  );
    264  is(
    265    document.querySelector(".network-details-bar"),
    266    null,
    267    "The detail panel is hidden"
    268  );
    269 
    270  await teardown(monitor);
    271 
    272  function testCustomItem(item, orig) {
    273    is(
    274      item.method,
    275      orig.method,
    276      "item is showing the same method as original request"
    277    );
    278    is(item.url, orig.url, "item is showing the same URL as original request");
    279  }
    280 
    281  function testCustomItemChanged(item, orig) {
    282    const { url } = item;
    283    const expectedUrl = orig.url + "&" + ADD_QUERY;
    284 
    285    is(url, expectedUrl, "menu item is updated to reflect url entered in form");
    286  }
    287 
    288  /*
    289   * Test that the New Request form was populated correctly
    290   */
    291  async function testCustomForm(data) {
    292    await waitUntil(() => document.querySelector(".custom-request-panel"));
    293    is(
    294      document.getElementById("custom-method-value").value,
    295      data.method,
    296      "new request form showing correct method"
    297    );
    298 
    299    is(
    300      document.getElementById("custom-url-value").value,
    301      data.url,
    302      "new request form showing correct url"
    303    );
    304 
    305    const query = document.getElementById("custom-query-value");
    306    is(
    307      query.value,
    308      "foo=bar\nbaz=42\ntype=urlencoded",
    309      "new request form showing correct query string"
    310    );
    311 
    312    const headers = document
    313      .getElementById("custom-headers-value")
    314      .value.split("\n");
    315    for (const { name, value } of data.requestHeaders.headers) {
    316      ok(
    317        headers.includes(name + ": " + value),
    318        "form contains header from request"
    319      );
    320    }
    321 
    322    const postData = document.getElementById("custom-postdata-value");
    323    is(
    324      postData.value,
    325      data.requestPostData.postData.text,
    326      "new request form showing correct post data"
    327    );
    328  }
    329 
    330  /*
    331   * Add some params and headers to the request form
    332   */
    333  async function editCustomForm() {
    334    monitor.panelWin.focus();
    335 
    336    const query = document.getElementById("custom-query-value");
    337    const queryFocus = once(query, "focus", false);
    338    // Bug 1195825: Due to some unexplained dark-matter with promise,
    339    // focus only works if delayed by one tick.
    340    query.setSelectionRange(query.value.length, query.value.length);
    341    executeSoon(() => query.focus());
    342    await queryFocus;
    343 
    344    // add params to url query string field
    345    typeInNetmonitor(["VK_RETURN"], monitor);
    346    typeInNetmonitor(ADD_QUERY, monitor);
    347 
    348    const headers = document.getElementById("custom-headers-value");
    349    const headersFocus = once(headers, "focus", false);
    350    headers.setSelectionRange(headers.value.length, headers.value.length);
    351    headers.focus();
    352    await headersFocus;
    353 
    354    // add a header
    355    typeInNetmonitor(["VK_RETURN"], monitor);
    356    typeInNetmonitor(ADD_HEADER, monitor);
    357 
    358    // add a User-Agent header, to check if default headers can be modified
    359    // (there will be two of them, first gets overwritten by the second)
    360    typeInNetmonitor(["VK_RETURN"], monitor);
    361    typeInNetmonitor(ADD_UA_HEADER, monitor);
    362 
    363    const postData = document.getElementById("custom-postdata-value");
    364    const postFocus = once(postData, "focus", false);
    365    postData.setSelectionRange(postData.value.length, postData.value.length);
    366    postData.focus();
    367    await postFocus;
    368 
    369    // add to POST data once textarea has updated
    370    await waitUntil(() => postData.textContent !== "");
    371    typeInNetmonitor(ADD_POSTDATA, monitor);
    372  }
    373 
    374  /*
    375   * Make sure newly created event matches expected request
    376   */
    377  async function testSentRequest(data, origData) {
    378    is(data.method, origData.method, "correct method in sent request");
    379    is(data.url, origData.url + "&" + ADD_QUERY, "correct url in sent request");
    380 
    381    const { headers } = data.requestHeaders;
    382    const hasHeader = headers.some(h => `${h.name}: ${h.value}` == ADD_HEADER);
    383    ok(hasHeader, "new header added to sent request");
    384 
    385    const hasUAHeader = headers.some(
    386      h => `${h.name}: ${h.value}` == ADD_UA_HEADER
    387    );
    388    ok(hasUAHeader, "User-Agent header added to sent request");
    389 
    390    is(
    391      data.requestPostData.postData.text,
    392      origData.requestPostData.postData.text + ADD_POSTDATA,
    393      "post data added to sent request"
    394    );
    395  }
    396 }