Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

"use strict";
XPCOMUtils.defineLazyServiceGetter(
this,
"gProxyService",
"@mozilla.org/network/protocol-proxy-service;1",
"nsIProtocolProxyService"
);
const TRANSPARENT_PROXY_RESOLVES_HOST =
Ci.nsIProxyInfo.TRANSPARENT_PROXY_RESOLVES_HOST;
function getProxyInfo(url = "http://www.mozilla.org/") {
return new Promise(resolve => {
let channel = NetUtil.newChannel({
uri: url,
loadUsingSystemPrincipal: true,
});
gProxyService.asyncResolve(channel, 0, {
onProxyAvailable(req, uri, pi) {
resolve(pi);
},
});
});
}
const testData = [
{
// An ExtensionError is thrown for this, but we are unable to catch it as we
// do with the PAC script api. In this case, we expect null for proxyInfo.
proxyInfo: "not_defined",
expected: {
proxyInfo: null,
},
},
{
proxyInfo: 1,
expected: {
error: {
message:
"ProxyInfoData: proxyData must be an object or array of objects",
},
},
},
{
proxyInfo: [
{
type: "socks",
host: "foo.bar",
port: 1080,
username: "johnsmith",
password: "pass123",
proxyDNS: true,
failoverTimeout: 3,
},
{ type: "http", host: "192.168.1.1", port: 3128 },
{ type: "https", host: "192.168.1.2", port: 1121, failoverTimeout: 1 },
{
type: "socks",
host: "192.168.1.3",
port: 1999,
proxyDNS: true,
username: "mungosantamaria",
password: "foobar",
},
{ type: "direct" },
],
expected: {
proxyInfo: {
type: "socks",
host: "foo.bar",
port: 1080,
proxyDNS: true,
username: "johnsmith",
password: "pass123",
failoverTimeout: 3,
failoverProxy: {
host: "192.168.1.1",
port: 3128,
type: "http",
failoverProxy: {
host: "192.168.1.2",
port: 1121,
type: "https",
failoverTimeout: 1,
failoverProxy: {
host: "192.168.1.3",
port: 1999,
type: "socks",
proxyDNS: TRANSPARENT_PROXY_RESOLVES_HOST,
username: "mungosantamaria",
password: "foobar",
failoverProxy: {
type: "direct",
},
},
},
},
},
},
},
];
add_task(async function test_proxy_listener() {
let extensionData = {
manifest: {
permissions: ["proxy", "<all_urls>"],
},
background() {
// Some tests generate multiple errors, we'll just rely on the first.
let seenError = false;
let proxyInfo;
browser.proxy.onError.addListener(error => {
if (!seenError) {
browser.test.sendMessage("proxy-error-received", error);
seenError = true;
}
});
browser.proxy.onRequest.addListener(
details => {
browser.test.log(`onRequest ${JSON.stringify(details)}`);
if (proxyInfo == "not_defined") {
return not_defined; // eslint-disable-line no-undef
}
return proxyInfo;
},
{ urls: ["<all_urls>"] }
);
browser.test.onMessage.addListener((message, data) => {
if (message === "set-proxy") {
seenError = false;
proxyInfo = data.proxyInfo;
}
});
browser.test.sendMessage("ready");
},
};
let extension = ExtensionTestUtils.loadExtension(extensionData);
await extension.startup();
await extension.awaitMessage("ready");
for (let test of testData) {
extension.sendMessage("set-proxy", test);
let testError = test.expected.error;
let errorWait = testError && extension.awaitMessage("proxy-error-received");
let proxyInfo = await getProxyInfo();
let expectedProxyInfo = test.expected.proxyInfo;
if (testError) {
info("waiting for error data");
let error = await errorWait;
equal(error.message, testError.message, "Correct error message received");
equal(proxyInfo, null, "no proxyInfo received");
} else if (expectedProxyInfo === null) {
equal(proxyInfo, null, "no proxyInfo received");
} else {
for (
let proxyUsed = proxyInfo;
proxyUsed;
proxyUsed = proxyUsed.failoverProxy
) {
let {
type,
host,
port,
username,
password,
proxyDNS,
failoverTimeout,
} = expectedProxyInfo;
equal(proxyUsed.host, host, `Expected proxy host to be ${host}`);
equal(proxyUsed.port, port || -1, `Expected proxy port to be ${port}`);
equal(proxyUsed.type, type, `Expected proxy type to be ${type}`);
// May be null or undefined depending on use of newProxyInfoWithAuth or newProxyInfo
equal(
proxyUsed.username || "",
username || "",
`Expected proxy username to be ${username}`
);
equal(
proxyUsed.password || "",
password || "",
`Expected proxy password to be ${password}`
);
equal(
proxyUsed.flags,
proxyDNS == undefined ? 0 : proxyDNS,
`Expected proxyDNS to be ${proxyDNS}`
);
// Default timeout is 10
equal(
proxyUsed.failoverTimeout,
failoverTimeout || 10,
`Expected failoverTimeout to be ${failoverTimeout}`
);
expectedProxyInfo = expectedProxyInfo.failoverProxy;
}
ok(!expectedProxyInfo, "no left over failoverProxy");
}
}
await extension.unload();
});
async function getExtension(expectedProxyInfo) {
function background(proxyInfo) {
browser.test.log(
`testing proxy.onRequest with proxyInfo = ${JSON.stringify(proxyInfo)}`
);
browser.proxy.onRequest.addListener(
() => {
return proxyInfo;
},
{ urls: ["<all_urls>"] }
);
}
let extensionData = {
manifest: {
permissions: ["proxy", "<all_urls>"],
},
background: `(${background})(${JSON.stringify(expectedProxyInfo)})`,
};
let extension = ExtensionTestUtils.loadExtension(extensionData);
await extension.startup();
return extension;
}
add_task(async function test_passthrough() {
let ext1 = await getExtension(null);
let ext2 = await getExtension({ host: "1.2.3.4", port: 8888, type: "https" });
// Also use a restricted url to test the ability to proxy those.
let proxyInfo = await getProxyInfo("https://addons.mozilla.org/");
equal(proxyInfo.host, "1.2.3.4", `second extension won`);
equal(proxyInfo.port, "8888", `second extension won`);
equal(proxyInfo.type, "https", `second extension won`);
await ext2.unload();
proxyInfo = await getProxyInfo();
equal(proxyInfo, null, `expected no proxy`);
await ext1.unload();
});
add_task(async function test_ftp_disabled() {
let extension = await getExtension({
host: "1.2.3.4",
port: 8888,
type: "http",
});
let proxyInfo = await getProxyInfo("ftp://somewhere.mozilla.org/");
equal(
proxyInfo,
null,
`proxy of ftp request is not available when ftp is disabled`
);
await extension.unload();
});
add_task(async function test_ws() {
let proxyRequestCount = 0;
let proxy = createHttpServer();
proxy.registerPathHandler("CONNECT", (request, response) => {
response.setStatusLine(request.httpVersion, 404, "Proxy not found");
++proxyRequestCount;
});
let extension = await getExtension({
host: proxy.identity.primaryHost,
port: proxy.identity.primaryPort,
type: "http",
});
// We need a page to use the WebSocket constructor, so let's use an extension.
let dummy = ExtensionTestUtils.loadExtension({
background() {
// The connection will not be upgraded to WebSocket, so it will close.
let ws = new WebSocket("wss://example.net/");
ws.onclose = () => browser.test.sendMessage("websocket_closed");
},
});
await dummy.startup();
await dummy.awaitMessage("websocket_closed");
await dummy.unload();
equal(proxyRequestCount, 1, "Expected one proxy request");
await extension.unload();
});