// 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(
"resource://testing-common/httpd.sys.mjs"
);
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);
}