183 lines
5.1 KiB
JavaScript
183 lines
5.1 KiB
JavaScript
/* 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/. */
|
|
|
|
"use strict";
|
|
|
|
/* import-globals-from head_cache.js */
|
|
/* import-globals-from head_cookies.js */
|
|
/* import-globals-from head_channels.js */
|
|
/* import-globals-from head_servers.js */
|
|
|
|
const { MockRegistrar } = ChromeUtils.importESModule(
|
|
"resource://testing-common/MockRegistrar.sys.mjs"
|
|
);
|
|
|
|
const certOverrideService = Cc[
|
|
"@mozilla.org/security/certoverride;1"
|
|
].getService(Ci.nsICertOverrideService);
|
|
|
|
// We don't normally allow localhost channels to be proxied, but this
|
|
// is easier than updating all the certs and/or domains.
|
|
Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true);
|
|
registerCleanupFunction(() => {
|
|
Services.prefs.clearUserPref("network.proxy.allow_hijacking_localhost");
|
|
});
|
|
|
|
function makeChan(uri) {
|
|
let chan = NetUtil.newChannel({
|
|
uri,
|
|
loadUsingSystemPrincipal: true,
|
|
}).QueryInterface(Ci.nsIHttpChannel);
|
|
chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
|
|
return chan;
|
|
}
|
|
|
|
function channelOpenPromise(chan, flags) {
|
|
return new Promise(resolve => {
|
|
function finish(req, buffer) {
|
|
resolve([req, buffer]);
|
|
}
|
|
chan.asyncOpen(new ChannelListener(finish, null, flags));
|
|
});
|
|
}
|
|
|
|
class SecurityObserver {
|
|
constructor(input, output) {
|
|
this.input = input;
|
|
this.output = output;
|
|
}
|
|
|
|
onHandshakeDone() {
|
|
info("TLS handshake done");
|
|
|
|
let output = this.output;
|
|
this.input.asyncWait(
|
|
{
|
|
onInputStreamReady(readyInput) {
|
|
let request = NetUtil.readInputStreamToString(
|
|
readyInput,
|
|
readyInput.available()
|
|
);
|
|
ok(
|
|
request.startsWith("GET /") && request.includes("HTTP/1.1"),
|
|
"expecting an HTTP/1.1 GET request"
|
|
);
|
|
let response =
|
|
"HTTP/1.1 200 OK\r\nContent-Type:text/plain\r\n" +
|
|
"Connection:Close\r\nContent-Length:2\r\n\r\nOK";
|
|
output.write(response, response.length);
|
|
},
|
|
},
|
|
0,
|
|
0,
|
|
Services.tm.currentThread
|
|
);
|
|
}
|
|
}
|
|
|
|
function startServer(cert) {
|
|
let tlsServer = Cc["@mozilla.org/network/tls-server-socket;1"].createInstance(
|
|
Ci.nsITLSServerSocket
|
|
);
|
|
tlsServer.init(-1, true, -1);
|
|
tlsServer.serverCert = cert;
|
|
|
|
let securityObservers = [];
|
|
|
|
let listener = {
|
|
onSocketAccepted(socket, transport) {
|
|
info("Accepted TLS client connection");
|
|
let connectionInfo = transport.securityCallbacks.getInterface(
|
|
Ci.nsITLSServerConnectionInfo
|
|
);
|
|
let input = transport.openInputStream(0, 0, 0);
|
|
let output = transport.openOutputStream(0, 0, 0);
|
|
connectionInfo.setSecurityObserver(new SecurityObserver(input, output));
|
|
},
|
|
|
|
onStopListening() {
|
|
info("onStopListening");
|
|
for (let securityObserver of securityObservers) {
|
|
securityObserver.input.close();
|
|
securityObserver.output.close();
|
|
}
|
|
},
|
|
};
|
|
|
|
tlsServer.setSessionTickets(false);
|
|
tlsServer.setRequestClientCertificate(Ci.nsITLSServerSocket.REQUEST_ALWAYS);
|
|
|
|
tlsServer.asyncListen(listener);
|
|
|
|
return tlsServer;
|
|
}
|
|
|
|
// Replace the UI dialog that prompts the user to pick a client certificate.
|
|
const clientAuthDialogService = {
|
|
chooseCertificate(hostname, certArray, loadContext, caNames, callback) {
|
|
callback.certificateChosen(certArray[0], false);
|
|
},
|
|
QueryInterface: ChromeUtils.generateQI(["nsIClientAuthDialogService"]),
|
|
};
|
|
|
|
let server;
|
|
add_setup(async function setup() {
|
|
do_get_profile();
|
|
|
|
let clientAuthDialogServiceCID = MockRegistrar.register(
|
|
"@mozilla.org/security/ClientAuthDialogService;1",
|
|
clientAuthDialogService
|
|
);
|
|
|
|
let cert = getTestServerCertificate();
|
|
ok(!!cert, "Got self-signed cert");
|
|
server = startServer(cert);
|
|
|
|
certOverrideService.rememberValidityOverride(
|
|
"localhost",
|
|
server.port,
|
|
{},
|
|
cert,
|
|
true
|
|
);
|
|
|
|
registerCleanupFunction(async function () {
|
|
MockRegistrar.unregister(clientAuthDialogServiceCID);
|
|
certOverrideService.clearValidityOverride("localhost", server.port, {});
|
|
server.close();
|
|
});
|
|
});
|
|
|
|
add_task(async function test_client_auth_with_proxy() {
|
|
let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
|
|
Ci.nsIX509CertDB
|
|
);
|
|
addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u");
|
|
addCertFromFile(certdb, "proxy-ca.pem", "CTu,u,u");
|
|
|
|
let proxies = [
|
|
NodeHTTPProxyServer,
|
|
NodeHTTPSProxyServer,
|
|
NodeHTTP2ProxyServer,
|
|
];
|
|
|
|
for (let p of proxies) {
|
|
info(`Test with proxy:${p.name}`);
|
|
let proxy = new p();
|
|
await proxy.start();
|
|
registerCleanupFunction(async () => {
|
|
await proxy.stop();
|
|
});
|
|
|
|
let chan = makeChan(`https://localhost:${server.port}`);
|
|
let [req, buff] = await channelOpenPromise(chan, CL_ALLOW_UNKNOWN_CL);
|
|
equal(req.status, Cr.NS_OK);
|
|
equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200);
|
|
equal(buff, "OK");
|
|
req.QueryInterface(Ci.nsIProxiedChannel);
|
|
ok(!!req.proxyInfo);
|
|
notEqual(req.proxyInfo.type, "direct");
|
|
await proxy.stop();
|
|
}
|
|
});
|