tor-browser

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

test_http3_large_post.js (5065B)


      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 registerCleanupFunction(async () => {
      6  http3_clear_prefs();
      7 });
      8 
      9 add_task(async function setup() {
     10  await http3_setup_tests("h3");
     11 });
     12 
     13 let Http3Listener = function (amount) {
     14  this.amount = amount;
     15 };
     16 
     17 Http3Listener.prototype = {
     18  expectedStatus: Cr.NS_OK,
     19  amount: 0,
     20  onProgressMaxNotificationCount: 0,
     21  onProgressNotificationCount: 0,
     22 
     23  QueryInterface: ChromeUtils.generateQI(["nsIProgressEventSink"]),
     24 
     25  getInterface(iid) {
     26    if (iid.equals(Ci.nsIProgressEventSink)) {
     27      return this;
     28    }
     29    throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
     30  },
     31 
     32  onProgress(request, progress, progressMax) {
     33    // we will get notifications for the request and the response.
     34    if (progress === progressMax) {
     35      this.onProgressMaxNotificationCount += 1;
     36    }
     37    // For a large upload there should be a multiple notifications.
     38    this.onProgressNotificationCount += 1;
     39  },
     40 
     41  onStatus() {},
     42 
     43  onStartRequest: function testOnStartRequest(request) {
     44    Assert.equal(request.status, this.expectedStatus);
     45    if (Components.isSuccessCode(this.expectedStatus)) {
     46      Assert.equal(request.responseStatus, 200);
     47    }
     48    Assert.equal(
     49      this.amount,
     50      request.getResponseHeader("x-data-received-length")
     51    );
     52  },
     53 
     54  onDataAvailable: function testOnDataAvailable(request, stream, off, cnt) {
     55    read_stream(stream, cnt);
     56  },
     57 
     58  onStopRequest: function testOnStopRequest(request) {
     59    let httpVersion = "";
     60    try {
     61      httpVersion = request.protocolVersion;
     62    } catch (e) {}
     63    Assert.equal(httpVersion, "h3");
     64    // We should get 2 correctOnProgress, i.e. one for request and one for the response.
     65    Assert.equal(this.onProgressMaxNotificationCount, 2);
     66    if (this.amount > 500000) {
     67      // 10 is an arbitrary number, but we cannot calculate exact number
     68      // because it depends on timing.
     69      Assert.greater(this.onProgressNotificationCount, 10);
     70    }
     71    this.finish();
     72  },
     73 };
     74 
     75 function chanPromise(chan, listener) {
     76  return new Promise(resolve => {
     77    function finish(result) {
     78      resolve(result);
     79    }
     80    listener.finish = finish;
     81    chan.asyncOpen(listener);
     82  });
     83 }
     84 
     85 function makeChan(uri, amount) {
     86  let chan = NetUtil.newChannel({
     87    uri,
     88    loadUsingSystemPrincipal: true,
     89  }).QueryInterface(Ci.nsIHttpChannel);
     90  chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
     91 
     92  let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
     93    Ci.nsIStringInputStream
     94  );
     95  stream.setByteStringData(generateContent(amount));
     96  let uchan = chan.QueryInterface(Ci.nsIUploadChannel);
     97  uchan.setUploadStream(stream, "text/plain", stream.available());
     98  chan.requestMethod = "POST";
     99  return chan;
    100 }
    101 
    102 // Generate a post.
    103 function generateContent(size) {
    104  let content = "";
    105  for (let i = 0; i < size; i++) {
    106    content += "0";
    107  }
    108  return content;
    109 }
    110 
    111 // Send a large post that can fit into a neqo stream buffer at once.
    112 // But the amount of data is larger than the necko's default stream
    113 // buffer side, therefore ReadSegments will be called multiple times.
    114 add_task(async function test_large_post() {
    115  let amount = 1 << 16;
    116  let listener = new Http3Listener(amount);
    117  let chan = makeChan("https://foo.example.com/post", amount);
    118  chan.notificationCallbacks = listener;
    119  await chanPromise(chan, listener);
    120 });
    121 
    122 // Send a large post that cannot fit into a neqo stream buffer at once.
    123 // StreamWritable events will trigger sending more data when the buffer
    124 // space is freed
    125 add_task(async function test_large_post2() {
    126  let amount = 1 << 23;
    127  let listener = new Http3Listener(amount);
    128  let chan = makeChan("https://foo.example.com/post", amount);
    129  chan.notificationCallbacks = listener;
    130  await chanPromise(chan, listener);
    131 });
    132 
    133 // Send a post in the same way viaduct does in bug 1749957.
    134 add_task(async function test_bug1749957_bug1750056() {
    135  let amount = 200; // Tests the bug as long as it's non-zero.
    136  let uri = "https://foo.example.com/post";
    137  let listener = new Http3Listener(amount);
    138 
    139  let chan = NetUtil.newChannel({
    140    uri,
    141    loadUsingSystemPrincipal: true,
    142  }).QueryInterface(Ci.nsIHttpChannel);
    143 
    144  // https://searchfox.org/mozilla-central/rev/1920b17ac5988fcfec4e45e2a94478ebfbfc6f88/toolkit/components/viaduct/ViaductRequest.cpp#120-152
    145  {
    146    chan.requestMethod = "POST";
    147    chan.setRequestHeader("content-length", "" + amount, /* aMerge = */ false);
    148 
    149    let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
    150      Ci.nsIStringInputStream
    151    );
    152    stream.setByteStringData(generateContent(amount));
    153    let uchan = chan.QueryInterface(Ci.nsIUploadChannel2);
    154    uchan.explicitSetUploadStream(
    155      stream,
    156      /* aContentType = */ "",
    157      /* aContentLength = */ -1,
    158      "POST",
    159      /* aStreamHasHeaders = */ false
    160    );
    161  }
    162 
    163  chan.notificationCallbacks = listener;
    164  await chanPromise(chan, listener);
    165 });