tor-browser

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

test_suspend_channel_on_authRetry.js (6658B)


      1 // This file tests async handling of a channel suspended in DoAuthRetry
      2 // notifying http-on-modify-request and http-on-before-connect observers.
      3 "use strict";
      4 
      5 const { HttpServer } = ChromeUtils.importESModule(
      6  "resource://testing-common/httpd.sys.mjs"
      7 );
      8 
      9 ChromeUtils.defineLazyGetter(this, "URL", function () {
     10  return "http://localhost:" + httpserv.identity.primaryPort;
     11 });
     12 
     13 var obs = Services.obs;
     14 
     15 var requestObserver = null;
     16 
     17 function AuthPrompt() {}
     18 
     19 AuthPrompt.prototype = {
     20  user: "guest",
     21  pass: "guest",
     22 
     23  QueryInterface: ChromeUtils.generateQI(["nsIAuthPrompt"]),
     24 
     25  prompt: function ap1_prompt() {
     26    do_throw("unexpected prompt call");
     27  },
     28 
     29  promptUsernameAndPassword: function promptUP(
     30    title,
     31    text,
     32    realm,
     33    savePW,
     34    user,
     35    pw
     36  ) {
     37    user.value = this.user;
     38    pw.value = this.pass;
     39 
     40    obs.addObserver(requestObserver, "http-on-before-connect");
     41    obs.addObserver(requestObserver, "http-on-modify-request");
     42    return true;
     43  },
     44 
     45  promptPassword: function promptPW() {
     46    do_throw("unexpected promptPassword call");
     47  },
     48 };
     49 
     50 function requestListenerObserver(
     51  suspendOnBeforeConnect,
     52  suspendOnModifyRequest
     53 ) {
     54  this.suspendOnModifyRequest = suspendOnModifyRequest;
     55  this.suspendOnBeforeConnect = suspendOnBeforeConnect;
     56 }
     57 
     58 requestListenerObserver.prototype = {
     59  suspendOnModifyRequest: false,
     60  suspendOnBeforeConnect: false,
     61  gotOnBeforeConnect: false,
     62  resumeOnBeforeConnect: false,
     63  gotOnModifyRequest: false,
     64  resumeOnModifyRequest: false,
     65  QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
     66 
     67  observe(subject, topic) {
     68    if (
     69      topic === "http-on-before-connect" &&
     70      subject instanceof Ci.nsIHttpChannel
     71    ) {
     72      if (this.suspendOnBeforeConnect) {
     73        let chan = subject.QueryInterface(Ci.nsIHttpChannel);
     74        executeSoon(() => {
     75          this.resumeOnBeforeConnect = true;
     76          chan.resume();
     77        });
     78        this.gotOnBeforeConnect = true;
     79        chan.suspend();
     80      }
     81    } else if (
     82      topic === "http-on-modify-request" &&
     83      subject instanceof Ci.nsIHttpChannel
     84    ) {
     85      if (this.suspendOnModifyRequest) {
     86        let chan = subject.QueryInterface(Ci.nsIHttpChannel);
     87        executeSoon(() => {
     88          this.resumeOnModifyRequest = true;
     89          chan.resume();
     90        });
     91        this.gotOnModifyRequest = true;
     92        chan.suspend();
     93      }
     94    }
     95  },
     96 };
     97 
     98 function Requestor() {}
     99 
    100 Requestor.prototype = {
    101  QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor"]),
    102 
    103  getInterface: function requestor_gi(iid) {
    104    if (iid.equals(Ci.nsIAuthPrompt)) {
    105      // Allow the prompt to store state by caching it here
    106      if (!this.prompt) {
    107        this.prompt = new AuthPrompt();
    108      }
    109      return this.prompt;
    110    }
    111 
    112    throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
    113  },
    114 
    115  prompt: null,
    116 };
    117 
    118 var listener = {
    119  expectedCode: -1, // Uninitialized
    120 
    121  onStartRequest: function test_onStartR(request) {
    122    try {
    123      if (!Components.isSuccessCode(request.status)) {
    124        do_throw("Channel should have a success code!");
    125      }
    126 
    127      if (!(request instanceof Ci.nsIHttpChannel)) {
    128        do_throw("Expecting an HTTP channel");
    129      }
    130 
    131      Assert.equal(request.responseStatus, this.expectedCode);
    132      // The request should be succeeded iff we expect 200
    133      Assert.equal(request.requestSucceeded, this.expectedCode == 200);
    134    } catch (e) {
    135      do_throw("Unexpected exception: " + e);
    136    }
    137    throw Components.Exception("", Cr.NS_ERROR_ABORT);
    138  },
    139 
    140  onDataAvailable: function test_ODA() {
    141    do_throw("Should not get any data!");
    142  },
    143 
    144  onStopRequest: function test_onStopR(request, status) {
    145    Assert.equal(status, Cr.NS_ERROR_ABORT);
    146    if (requestObserver.suspendOnBeforeConnect) {
    147      Assert.ok(
    148        requestObserver.gotOnBeforeConnect &&
    149          requestObserver.resumeOnBeforeConnect
    150      );
    151    }
    152    if (requestObserver.suspendOnModifyRequest) {
    153      Assert.ok(
    154        requestObserver.gotOnModifyRequest &&
    155          requestObserver.resumeOnModifyRequest
    156      );
    157    }
    158    obs.removeObserver(requestObserver, "http-on-before-connect");
    159    obs.removeObserver(requestObserver, "http-on-modify-request");
    160    moveToNextTest();
    161  },
    162 };
    163 
    164 function makeChan(url, loadingUrl) {
    165  var principal = Services.scriptSecurityManager.createContentPrincipal(
    166    Services.io.newURI(loadingUrl),
    167    {}
    168  );
    169  return NetUtil.newChannel({
    170    uri: url,
    171    loadingPrincipal: principal,
    172    securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
    173    contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
    174  });
    175 }
    176 
    177 var tests = [
    178  test_suspend_on_before_connect,
    179  test_suspend_on_modify_request,
    180  test_suspend_all,
    181 ];
    182 
    183 var current_test = 0;
    184 
    185 var httpserv = null;
    186 
    187 function moveToNextTest() {
    188  if (current_test < tests.length - 1) {
    189    // First, gotta clear the auth cache
    190    Cc["@mozilla.org/network/http-auth-manager;1"]
    191      .getService(Ci.nsIHttpAuthManager)
    192      .clearAll();
    193 
    194    current_test++;
    195    tests[current_test]();
    196  } else {
    197    do_test_pending();
    198    httpserv.stop(do_test_finished);
    199  }
    200 
    201  do_test_finished();
    202 }
    203 
    204 function run_test() {
    205  httpserv = new HttpServer();
    206 
    207  httpserv.registerPathHandler("/auth", authHandler);
    208 
    209  httpserv.start(-1);
    210 
    211  tests[0]();
    212 }
    213 
    214 function test_suspend_on_auth(suspendOnBeforeConnect, suspendOnModifyRequest) {
    215  var chan = makeChan(URL + "/auth", URL);
    216  requestObserver = new requestListenerObserver(
    217    suspendOnBeforeConnect,
    218    suspendOnModifyRequest
    219  );
    220  chan.notificationCallbacks = new Requestor();
    221  listener.expectedCode = 200; // OK
    222  chan.asyncOpen(listener);
    223 
    224  do_test_pending();
    225 }
    226 
    227 function test_suspend_on_before_connect() {
    228  test_suspend_on_auth(true, false);
    229 }
    230 
    231 function test_suspend_on_modify_request() {
    232  test_suspend_on_auth(false, true);
    233 }
    234 
    235 function test_suspend_all() {
    236  test_suspend_on_auth(true, true);
    237 }
    238 
    239 // PATH HANDLERS
    240 
    241 // /auth
    242 function authHandler(metadata, response) {
    243  // btoa("guest:guest"), but that function is not available here
    244  var expectedHeader = "Basic Z3Vlc3Q6Z3Vlc3Q=";
    245 
    246  var body;
    247  if (
    248    metadata.hasHeader("Authorization") &&
    249    metadata.getHeader("Authorization") == expectedHeader
    250  ) {
    251    response.setStatusLine(metadata.httpVersion, 200, "OK, authorized");
    252    response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
    253 
    254    body = "success";
    255  } else {
    256    // didn't know guest:guest, failure
    257    response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
    258    response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
    259 
    260    body = "failed";
    261  }
    262 
    263  response.bodyOutputStream.write(body, body.length);
    264 }