tor-browser

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

test_synthesized_response.js (8477B)


      1 "use strict";
      2 
      3 const { HttpServer } = ChromeUtils.importESModule(
      4  "resource://testing-common/httpd.sys.mjs"
      5 );
      6 
      7 ChromeUtils.defineLazyGetter(this, "URL", function () {
      8  return "http://localhost:" + httpServer.identity.primaryPort;
      9 });
     10 
     11 var httpServer = null;
     12 
     13 function isParentProcess() {
     14  let appInfo = Cc["@mozilla.org/xre/app-info;1"];
     15  return (
     16    !appInfo ||
     17    Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT
     18  );
     19 }
     20 
     21 if (isParentProcess()) {
     22  // ensure the cache service is prepped when running the test
     23  // We only do this in the main process, as the cache storage service leaks
     24  // when instantiated in the content process.
     25  Services.cache2;
     26 }
     27 
     28 var gotOnProgress;
     29 var gotOnStatus;
     30 
     31 function make_channel(url, body, cb) {
     32  gotOnProgress = false;
     33  gotOnStatus = false;
     34  var chan = NetUtil.newChannel({
     35    uri: url,
     36    loadUsingSystemPrincipal: true,
     37  }).QueryInterface(Ci.nsIHttpChannel);
     38  chan.notificationCallbacks = {
     39    numChecks: 0,
     40    QueryInterface: ChromeUtils.generateQI([
     41      "nsINetworkInterceptController",
     42      "nsIInterfaceRequestor",
     43      "nsIProgressEventSink",
     44    ]),
     45    getInterface(iid) {
     46      return this.QueryInterface(iid);
     47    },
     48    onProgress() {
     49      gotOnProgress = true;
     50    },
     51    onStatus() {
     52      gotOnStatus = true;
     53    },
     54    shouldPrepareForIntercept() {
     55      Assert.equal(this.numChecks, 0);
     56      this.numChecks++;
     57      return true;
     58    },
     59    channelIntercepted(channel) {
     60      channel.QueryInterface(Ci.nsIInterceptedChannel);
     61      if (body) {
     62        var synthesized = Cc[
     63          "@mozilla.org/io/string-input-stream;1"
     64        ].createInstance(Ci.nsIStringInputStream);
     65        synthesized.setByteStringData(body);
     66 
     67        channel.startSynthesizedResponse(synthesized, null, null, "", false);
     68        channel.finishSynthesizedResponse();
     69      }
     70      if (cb) {
     71        cb(channel);
     72      }
     73      return {
     74        dispatch() {},
     75      };
     76    },
     77  };
     78  return chan;
     79 }
     80 
     81 const REMOTE_BODY = "http handler body";
     82 const NON_REMOTE_BODY = "synthesized body";
     83 const NON_REMOTE_BODY_2 = "synthesized body #2";
     84 
     85 function bodyHandler(metadata, response) {
     86  response.setHeader("Content-Type", "text/plain");
     87  response.write(REMOTE_BODY);
     88 }
     89 
     90 function run_test() {
     91  httpServer = new HttpServer();
     92  httpServer.registerPathHandler("/body", bodyHandler);
     93  httpServer.start(-1);
     94 
     95  run_next_test();
     96 }
     97 
     98 function handle_synthesized_response(request, buffer) {
     99  Assert.equal(buffer, NON_REMOTE_BODY);
    100  Assert.ok(gotOnStatus);
    101  Assert.ok(gotOnProgress);
    102  run_next_test();
    103 }
    104 
    105 function handle_synthesized_response_2(request, buffer) {
    106  Assert.equal(buffer, NON_REMOTE_BODY_2);
    107  Assert.ok(gotOnStatus);
    108  Assert.ok(gotOnProgress);
    109  run_next_test();
    110 }
    111 
    112 function handle_remote_response(request, buffer) {
    113  Assert.equal(buffer, REMOTE_BODY);
    114  Assert.ok(gotOnStatus);
    115  Assert.ok(gotOnProgress);
    116  run_next_test();
    117 }
    118 
    119 // hit the network instead of synthesizing
    120 add_test(function () {
    121  var chan = make_channel(URL + "/body", null, function (channel) {
    122    channel.resetInterception(false);
    123  });
    124  chan.asyncOpen(new ChannelListener(handle_remote_response, null));
    125 });
    126 
    127 // synthesize a response
    128 add_test(function () {
    129  var chan = make_channel(URL + "/body", NON_REMOTE_BODY);
    130  chan.asyncOpen(
    131    new ChannelListener(handle_synthesized_response, null, CL_ALLOW_UNKNOWN_CL)
    132  );
    133 });
    134 
    135 // hit the network instead of synthesizing, to test that no previous synthesized
    136 // cache entry is used.
    137 add_test(function () {
    138  var chan = make_channel(URL + "/body", null, function (channel) {
    139    channel.resetInterception(false);
    140  });
    141  chan.asyncOpen(new ChannelListener(handle_remote_response, null));
    142 });
    143 
    144 // synthesize a different response to ensure no previous response is cached
    145 add_test(function () {
    146  var chan = make_channel(URL + "/body", NON_REMOTE_BODY_2);
    147  chan.asyncOpen(
    148    new ChannelListener(
    149      handle_synthesized_response_2,
    150      null,
    151      CL_ALLOW_UNKNOWN_CL
    152    )
    153  );
    154 });
    155 
    156 // ensure that the channel waits for a decision and synthesizes headers correctly
    157 add_test(function () {
    158  var chan = make_channel(URL + "/body", null, function (channel) {
    159    do_timeout(100, function () {
    160      var synthesized = Cc[
    161        "@mozilla.org/io/string-input-stream;1"
    162      ].createInstance(Ci.nsIStringInputStream);
    163      synthesized.setByteStringData(NON_REMOTE_BODY);
    164      channel.synthesizeHeader("Content-Length", NON_REMOTE_BODY.length);
    165      channel.startSynthesizedResponse(synthesized, null, null, "", false);
    166      channel.finishSynthesizedResponse();
    167    });
    168  });
    169  chan.asyncOpen(new ChannelListener(handle_synthesized_response, null));
    170 });
    171 
    172 // ensure that the channel waits for a decision
    173 add_test(function () {
    174  var chan = make_channel(URL + "/body", null, function (channel) {
    175    do_timeout(100, function () {
    176      channel.resetInterception(false);
    177    });
    178  });
    179  chan.asyncOpen(new ChannelListener(handle_remote_response, null));
    180 });
    181 
    182 // ensure that the intercepted channel supports suspend/resume
    183 add_test(function () {
    184  var chan = make_channel(URL + "/body", null, function (intercepted) {
    185    var synthesized = Cc[
    186      "@mozilla.org/io/string-input-stream;1"
    187    ].createInstance(Ci.nsIStringInputStream);
    188    synthesized.setByteStringData(NON_REMOTE_BODY);
    189 
    190    // set the content-type to ensure that the stream converter doesn't hold up notifications
    191    // and cause the test to fail
    192    intercepted.synthesizeHeader("Content-Type", "text/plain");
    193    intercepted.startSynthesizedResponse(synthesized, null, null, "", false);
    194    intercepted.finishSynthesizedResponse();
    195  });
    196  chan.asyncOpen(
    197    new ChannelListener(
    198      handle_synthesized_response,
    199      null,
    200      CL_ALLOW_UNKNOWN_CL | CL_SUSPEND | CL_EXPECT_3S_DELAY
    201    )
    202  );
    203 });
    204 
    205 // ensure that the intercepted channel can be cancelled
    206 add_test(function () {
    207  var chan = make_channel(URL + "/body", null, function (intercepted) {
    208    intercepted.cancelInterception(Cr.NS_BINDING_ABORTED);
    209  });
    210  chan.asyncOpen(new ChannelListener(run_next_test, null, CL_EXPECT_FAILURE));
    211 });
    212 
    213 // ensure that the channel can't be cancelled via nsIInterceptedChannel after making a decision
    214 add_test(function () {
    215  var chan = make_channel(URL + "/body", null, function (channel) {
    216    channel.resetInterception(false);
    217    do_timeout(0, function () {
    218      var gotexception = false;
    219      try {
    220        channel.cancelInterception();
    221      } catch (x) {
    222        gotexception = true;
    223      }
    224      Assert.ok(gotexception);
    225    });
    226  });
    227  chan.asyncOpen(new ChannelListener(handle_remote_response, null));
    228 });
    229 
    230 // ensure that the intercepted channel can be canceled during the response
    231 add_test(function () {
    232  var chan = make_channel(URL + "/body", null, function (intercepted) {
    233    var synthesized = Cc[
    234      "@mozilla.org/io/string-input-stream;1"
    235    ].createInstance(Ci.nsIStringInputStream);
    236    synthesized.setByteStringData(NON_REMOTE_BODY);
    237 
    238    let channel = intercepted.channel;
    239    intercepted.startSynthesizedResponse(synthesized, null, null, "", false);
    240    intercepted.finishSynthesizedResponse();
    241    channel.cancel(Cr.NS_BINDING_ABORTED);
    242  });
    243  chan.asyncOpen(
    244    new ChannelListener(
    245      run_next_test,
    246      null,
    247      CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL
    248    )
    249  );
    250 });
    251 
    252 // ensure that the intercepted channel can be canceled before the response
    253 add_test(function () {
    254  var chan = make_channel(URL + "/body", null, function (intercepted) {
    255    var synthesized = Cc[
    256      "@mozilla.org/io/string-input-stream;1"
    257    ].createInstance(Ci.nsIStringInputStream);
    258    synthesized.setByteStringData(NON_REMOTE_BODY);
    259 
    260    intercepted.channel.cancel(Cr.NS_BINDING_ABORTED);
    261 
    262    // This should not throw, but result in the channel firing callbacks
    263    // with an error status.
    264    intercepted.startSynthesizedResponse(synthesized, null, null, "", false);
    265    intercepted.finishSynthesizedResponse();
    266  });
    267  chan.asyncOpen(
    268    new ChannelListener(
    269      run_next_test,
    270      null,
    271      CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL
    272    )
    273  );
    274 });
    275 
    276 // Ensure that nsIInterceptedChannel.channelIntercepted() can return an error.
    277 // In this case we should automatically ResetInterception() and complete the
    278 // network request.
    279 add_test(function () {
    280  var chan = make_channel(URL + "/body", null, function () {
    281    throw new Error("boom");
    282  });
    283  chan.asyncOpen(new ChannelListener(handle_remote_response, null));
    284 });
    285 
    286 add_test(function () {
    287  httpServer.stop(run_next_test);
    288 });