Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/* 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/. */
// Test that making an HTTPS request through an HTTP proxy that answers CONNECT
// with a 407 and a connection-based (sticky) auth scheme still ends up with a
// securityInfo corresponding to the original HTTPS request.
"use strict";
const { NodeHTTPProxyServer, NodeHTTPSServer } = ChromeUtils.importESModule(
);
/* import-globals-from head_channels.js */
// Answers the first CONNECT with a 407 offering mock_auth, then tunnels to the
// destination server once credentials arrive on the same connection.
function connectHandler(req, clientSocket) {
const net = require("net");
const url = require("url");
let buffered = Buffer.alloc(0);
const { port } = url.parse(`//${req.url}`, false, true);
function handleConnect(credentials, pending) {
if (credentials != "moz_test_credentials") {
clientSocket.write(
"HTTP/1.1 407 Unauthorized\r\n" +
"Proxy-agent: Node.js-Proxy\r\n" +
"Connection: keep-alive\r\n" +
"Proxy-Authenticate: mock_auth\r\n" +
"Content-Length: 0\r\n" +
"\r\n"
);
} else {
clientSocket.removeListener("data", onData);
let serverSocket = net.connect(
{ port, host: "127.0.0.1", family: 4 },
() => {
clientSocket.write(
"HTTP/1.1 200 Connection Established\r\n" +
"Proxy-agent: Node.js-Proxy\r\n" +
"\r\n"
);
if (pending && pending.length) {
serverSocket.write(pending);
}
serverSocket.pipe(clientSocket);
clientSocket.pipe(serverSocket);
}
);
}
}
function onData(data) {
buffered = Buffer.concat([buffered, data]);
let headersEnd = buffered.indexOf("\r\n\r\n");
if (headersEnd == -1) {
return;
}
let headers = buffered.subarray(0, headersEnd).toString("latin1");
let rest = buffered.subarray(headersEnd + 4);
buffered = Buffer.alloc(0);
let credentials = "";
for (let line of headers.split("\r\n")) {
let pair = line.split(":").map(element => element.trim());
if (pair[0] == "Proxy-Authorization") {
credentials = pair[1];
}
}
handleConnect(credentials, rest);
}
clientSocket.on("data", onData);
handleConnect(req.headers["proxy-authorization"] || "", null);
}
async function makeProxiedConnectionAndCheckSecurityInfo() {
Cc["@mozilla.org/network/http-auth-manager;1"]
.getService(Ci.nsIHttpAuthManager)
.clearAll();
let server = new NodeHTTPSServer();
await server.start();
await server.registerPathHandler("/test", (req, resp) => {
let body = "hello from the server";
resp.setHeader("Content-Type", "text/plain");
resp.setHeader("Content-Length", body.length);
resp.writeHead(200);
resp.end(body);
});
let proxy = new NodeHTTPProxyServer();
await proxy.start();
await proxy.registerConnectHandler(connectHandler);
try {
// The NodeHTTPSServer uses a certificate valid for `alt1.example.com`.
let chan = makeChan(`https://alt1.example.com:${server.port()}/test`);
let [req, buffer] = await channelOpenPromise(chan);
Assert.equal(req.status, Cr.NS_OK, "channel succeeded");
Assert.equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200);
Assert.equal(buffer, "hello from the server", "got the server's response");
Assert.equal(
req.QueryInterface(Ci.nsIProxiedChannel).httpProxyConnectResponseCode,
200,
"the tunnel was established"
);
let secInfo = req.QueryInterface(Ci.nsIChannel).securityInfo;
Assert.notEqual(secInfo, null, "channel should have securityInfo");
secInfo.QueryInterface(Ci.nsITransportSecurityInfo);
Assert.notEqual(
secInfo.serverCert,
null,
"securityInfo should have a serverCert"
);
} finally {
await proxy.stop();
await server.stop();
}
}
// Test with auth retries configured to use a new channel.
add_task(
{ pref_set: [["network.auth.use_redirect_for_retries", true]] },
async function retry_via_redirect() {
await makeProxiedConnectionAndCheckSecurityInfo();
}
);
// Test with auth retries configured to re-use the original channel.
add_task(
{ pref_set: [["network.auth.use_redirect_for_retries", false]] },
async function retry_via_restart() {
await makeProxiedConnectionAndCheckSecurityInfo();
}
);