/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// This test makes sure that the authorization header can get deleted e.g. by
// extensions if they are observing "http-on-modify-request". In a first step
// the auth cache is filled with credentials which then get added to the
// following request. On "http-on-modify-request" it is tested whether the
// authorization header got added at all and if so it gets removed. This test
// passes iff both succeeds.

Components.utils.import("resource://testing-common/httpd.js");
Components.utils.import("resource://gre/modules/NetUtil.jsm");

var notification = "http-on-modify-request";

var httpServer = null;

var authCredentials = "guest:guest";
var authPath = "/authTest";
var authCredsURL = "http://" + authCredentials + "@localhost:8888" + authPath;
var authURL = "http://localhost:8888" + authPath;

function authHandler(metadata, response) {
  if (metadata.hasHeader("Test")) {
    // Lets see if the auth header got deleted.
    var noAuthHeader = false;
    if (!metadata.hasHeader("Authorization")) {
      noAuthHeader = true;
    }
    Assert.ok(noAuthHeader);
  } else {
    // Not our test request yet.
    if (!metadata.hasHeader("Authorization")) {
      response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
      response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
    }
  }
}

function RequestObserver() {
  this.register();
}

RequestObserver.prototype = {
  register: function() {
    info("Registering " + notification);
    Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService).
      addObserver(this, notification, true);
  },

  QueryInterface: function(iid) {
    if (iid.equals(Ci.nsIObserver) || iid.equals(Ci.nsISupportsWeakReference) ||
        iid.equals(Ci.nsISupports)) {
      return this;
    }
    throw Cr.NS_ERROR_NO_INTERFACE;
  },

  observe: function(subject, topic, data) {
    if (topic == notification) {
      if (!(subject instanceof Ci.nsIHttpChannel)) {
        do_throw(notification + " observed a non-HTTP channel.");
      }
      try {
        let authHeader = subject.getRequestHeader("Authorization");
      } catch (e) {
        // Throw if there is no header to delete. We should get one iff caching
        // the auth credentials is working and the header gets added _before_
        // "http-on-modify-request" gets called.
        httpServer.stop(do_test_finished);
        do_throw("No authorization header found, aborting!");
      }
      // We are still here. Let's remove the authorization header now.
      subject.setRequestHeader("Authorization", null, false);
    }
  }
}

var listener = {
  onStartRequest: function test_onStartR(request, ctx) {},

  onDataAvailable: function test_ODA() {
    do_throw("Should not get any data!");
  },

  onStopRequest: function test_onStopR(request, ctx, status) {
    if (current_test < (tests.length - 1)) {
      current_test++;
      tests[current_test]();
    } else {
      do_test_pending();
      httpServer.stop(do_test_finished);
    }
    do_test_finished();
  }
};

function makeChan(url) {
  return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true})
                .QueryInterface(Ci.nsIHttpChannel);
}

var tests = [startAuthHeaderTest, removeAuthHeaderTest];

var current_test = 0;

var requestObserver = null;

function run_test() {
  httpServer = new HttpServer();
  httpServer.registerPathHandler(authPath, authHandler);
  httpServer.start(8888);

  tests[0]();
}

function startAuthHeaderTest() {
  var chan = makeChan(authCredsURL);
  chan.asyncOpen2(listener);

  do_test_pending();
}

function removeAuthHeaderTest() {
  // After caching the auth credentials in the first test, lets try to remove
  // the authorization header now...
  requestObserver = new RequestObserver();
  var chan = makeChan(authURL);
  // Indicating that the request is coming from the second test.
  chan.setRequestHeader("Test", "1", false);
  chan.asyncOpen2(listener);

  do_test_pending();
}
