Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

// This file tests async handling of a channel suspended in DoAuthRetry
// notifying http-on-modify-request and http-on-before-connect observers.
"use strict";
const { HttpServer } = ChromeUtils.importESModule(
);
ChromeUtils.defineLazyGetter(this, "URL", function () {
return "http://localhost:" + httpserv.identity.primaryPort;
});
var obs = Services.obs;
var requestObserver = null;
function AuthPrompt() {}
AuthPrompt.prototype = {
user: "guest",
pass: "guest",
QueryInterface: ChromeUtils.generateQI(["nsIAuthPrompt"]),
prompt: function ap1_prompt() {
do_throw("unexpected prompt call");
},
promptUsernameAndPassword: function promptUP(
title,
text,
realm,
savePW,
user,
pw
) {
user.value = this.user;
pw.value = this.pass;
obs.addObserver(requestObserver, "http-on-before-connect");
obs.addObserver(requestObserver, "http-on-modify-request");
return true;
},
promptPassword: function promptPW() {
do_throw("unexpected promptPassword call");
},
};
function requestListenerObserver(
suspendOnBeforeConnect,
suspendOnModifyRequest
) {
this.suspendOnModifyRequest = suspendOnModifyRequest;
this.suspendOnBeforeConnect = suspendOnBeforeConnect;
}
requestListenerObserver.prototype = {
suspendOnModifyRequest: false,
suspendOnBeforeConnect: false,
gotOnBeforeConnect: false,
resumeOnBeforeConnect: false,
gotOnModifyRequest: false,
resumeOnModifyRequest: false,
QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
observe(subject, topic) {
if (
topic === "http-on-before-connect" &&
subject instanceof Ci.nsIHttpChannel
) {
if (this.suspendOnBeforeConnect) {
let chan = subject.QueryInterface(Ci.nsIHttpChannel);
executeSoon(() => {
this.resumeOnBeforeConnect = true;
chan.resume();
});
this.gotOnBeforeConnect = true;
chan.suspend();
}
} else if (
topic === "http-on-modify-request" &&
subject instanceof Ci.nsIHttpChannel
) {
if (this.suspendOnModifyRequest) {
let chan = subject.QueryInterface(Ci.nsIHttpChannel);
executeSoon(() => {
this.resumeOnModifyRequest = true;
chan.resume();
});
this.gotOnModifyRequest = true;
chan.suspend();
}
}
},
};
function Requestor() {}
Requestor.prototype = {
QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor"]),
getInterface: function requestor_gi(iid) {
if (iid.equals(Ci.nsIAuthPrompt)) {
// Allow the prompt to store state by caching it here
if (!this.prompt) {
this.prompt = new AuthPrompt();
}
return this.prompt;
}
throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
},
prompt: null,
};
var listener = {
expectedCode: -1, // Uninitialized
onStartRequest: function test_onStartR(request) {
try {
if (!Components.isSuccessCode(request.status)) {
do_throw("Channel should have a success code!");
}
if (!(request instanceof Ci.nsIHttpChannel)) {
do_throw("Expecting an HTTP channel");
}
Assert.equal(request.responseStatus, this.expectedCode);
// The request should be succeeded iff we expect 200
Assert.equal(request.requestSucceeded, this.expectedCode == 200);
} catch (e) {
do_throw("Unexpected exception: " + e);
}
throw Components.Exception("", Cr.NS_ERROR_ABORT);
},
onDataAvailable: function test_ODA() {
do_throw("Should not get any data!");
},
onStopRequest: function test_onStopR(request, status) {
Assert.equal(status, Cr.NS_ERROR_ABORT);
if (requestObserver.suspendOnBeforeConnect) {
Assert.ok(
requestObserver.gotOnBeforeConnect &&
requestObserver.resumeOnBeforeConnect
);
}
if (requestObserver.suspendOnModifyRequest) {
Assert.ok(
requestObserver.gotOnModifyRequest &&
requestObserver.resumeOnModifyRequest
);
}
obs.removeObserver(requestObserver, "http-on-before-connect");
obs.removeObserver(requestObserver, "http-on-modify-request");
moveToNextTest();
},
};
function makeChan(url, loadingUrl) {
var principal = Services.scriptSecurityManager.createContentPrincipal(
Services.io.newURI(loadingUrl),
{}
);
return NetUtil.newChannel({
uri: url,
loadingPrincipal: principal,
securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
});
}
var tests = [
test_suspend_on_before_connect,
test_suspend_on_modify_request,
test_suspend_all,
];
var current_test = 0;
var httpserv = null;
function moveToNextTest() {
if (current_test < tests.length - 1) {
// First, gotta clear the auth cache
Cc["@mozilla.org/network/http-auth-manager;1"]
.getService(Ci.nsIHttpAuthManager)
.clearAll();
current_test++;
tests[current_test]();
} else {
do_test_pending();
httpserv.stop(do_test_finished);
}
do_test_finished();
}
function run_test() {
httpserv = new HttpServer();
httpserv.registerPathHandler("/auth", authHandler);
httpserv.start(-1);
tests[0]();
}
function test_suspend_on_auth(suspendOnBeforeConnect, suspendOnModifyRequest) {
var chan = makeChan(URL + "/auth", URL);
requestObserver = new requestListenerObserver(
suspendOnBeforeConnect,
suspendOnModifyRequest
);
chan.notificationCallbacks = new Requestor();
listener.expectedCode = 200; // OK
chan.asyncOpen(listener);
do_test_pending();
}
function test_suspend_on_before_connect() {
test_suspend_on_auth(true, false);
}
function test_suspend_on_modify_request() {
test_suspend_on_auth(false, true);
}
function test_suspend_all() {
test_suspend_on_auth(true, true);
}
// PATH HANDLERS
// /auth
function authHandler(metadata, response) {
// btoa("guest:guest"), but that function is not available here
var expectedHeader = "Basic Z3Vlc3Q6Z3Vlc3Q=";
var body;
if (
metadata.hasHeader("Authorization") &&
metadata.getHeader("Authorization") == expectedHeader
) {
response.setStatusLine(metadata.httpVersion, 200, "OK, authorized");
response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
body = "success";
} else {
// didn't know guest:guest, failure
response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
body = "failed";
}
response.bodyOutputStream.write(body, body.length);
}