325 lines
9.2 KiB
JavaScript
325 lines
9.2 KiB
JavaScript
"use strict";
|
|
|
|
AddonTestUtils.init(this);
|
|
AddonTestUtils.overrideCertDB();
|
|
AddonTestUtils.createAppInfo(
|
|
"xpcshell@tests.mozilla.org",
|
|
"XPCShell",
|
|
"1",
|
|
"43"
|
|
);
|
|
|
|
// Necessary for the pac script to proxy localhost requests
|
|
Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true);
|
|
|
|
// Pref is not builtin if direct failover is disabled in compile config.
|
|
ChromeUtils.defineLazyGetter(this, "directFailoverDisabled", () => {
|
|
return (
|
|
Services.prefs.getPrefType("network.proxy.failover_direct") ==
|
|
Ci.nsIPrefBranch.PREF_INVALID
|
|
);
|
|
});
|
|
|
|
const { ServiceRequest } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/ServiceRequest.sys.mjs"
|
|
);
|
|
|
|
// Prevent the request from reaching out to the network.
|
|
const { HttpServer } = ChromeUtils.importESModule(
|
|
"resource://testing-common/httpd.sys.mjs"
|
|
);
|
|
|
|
// No hosts defined to avoid the default proxy filter setup.
|
|
const nonProxiedServer = createHttpServer();
|
|
nonProxiedServer.registerPathHandler("/", (request, response) => {
|
|
response.setStatusLine(request.httpVersion, 200, "OK");
|
|
response.write("ok!");
|
|
});
|
|
const { primaryHost, primaryPort } = nonProxiedServer.identity;
|
|
|
|
function getProxyData(channel) {
|
|
if (!(channel instanceof Ci.nsIProxiedChannel) || !channel.proxyInfo) {
|
|
return;
|
|
}
|
|
let { type, host, port, sourceId } = channel.proxyInfo;
|
|
return { type, host, port, sourceId };
|
|
}
|
|
|
|
// Get a free port with no listener to use in the proxyinfo.
|
|
function getBadProxyPort() {
|
|
let server = new HttpServer();
|
|
server.start(-1);
|
|
const badPort = server.identity.primaryPort;
|
|
server.stop();
|
|
return badPort;
|
|
}
|
|
|
|
function xhr(url, options = { beConservative: true, bypassProxy: false }) {
|
|
return new Promise((resolve, reject) => {
|
|
let req = new XMLHttpRequest();
|
|
req.open("GET", `${url}?t=${Math.random()}`);
|
|
req.channel.QueryInterface(Ci.nsIHttpChannelInternal).beConservative =
|
|
options.beConservative;
|
|
req.channel.QueryInterface(Ci.nsIHttpChannelInternal).bypassProxy =
|
|
options.bypassProxy;
|
|
req.onload = () => {
|
|
resolve({ text: req.responseText, proxy: getProxyData(req.channel) });
|
|
};
|
|
req.onerror = () => {
|
|
reject({ status: req.status, proxy: getProxyData(req.channel) });
|
|
};
|
|
req.send();
|
|
});
|
|
}
|
|
|
|
// Same as the above xhr call, but ServiceRequest is always beConservative.
|
|
// This is here to specifically test bypassProxy with ServiceRequest.
|
|
function serviceRequest(url, options = { bypassProxy: false }) {
|
|
return new Promise((resolve, reject) => {
|
|
let req = new ServiceRequest();
|
|
req.open("GET", `${url}?t=${Math.random()}`, options);
|
|
req.onload = () => {
|
|
resolve({ text: req.responseText, proxy: getProxyData(req.channel) });
|
|
};
|
|
req.onerror = () => {
|
|
reject({ status: req.status, proxy: getProxyData(req.channel) });
|
|
};
|
|
req.send();
|
|
});
|
|
}
|
|
|
|
add_task(async function setup() {
|
|
await AddonTestUtils.promiseStartupManager();
|
|
});
|
|
|
|
async function getProxyExtension(proxyDetails) {
|
|
async function background(proxyDetails) {
|
|
browser.proxy.onRequest.addListener(
|
|
() => {
|
|
return proxyDetails;
|
|
},
|
|
{ urls: ["<all_urls>"] }
|
|
);
|
|
|
|
browser.test.sendMessage("proxied");
|
|
}
|
|
let extensionData = {
|
|
manifest: {
|
|
permissions: ["proxy", "<all_urls>"],
|
|
},
|
|
background: `(${background})(${JSON.stringify(proxyDetails)})`,
|
|
incognitoOverride: "spanning",
|
|
useAddonManager: "temporary",
|
|
};
|
|
|
|
let extension = ExtensionTestUtils.loadExtension(extensionData);
|
|
await extension.startup();
|
|
await extension.awaitMessage("proxied");
|
|
return extension;
|
|
}
|
|
|
|
add_task(async function test_failover_content_direct() {
|
|
// load a content page for fetch and non-system principal, expect
|
|
// failover to direct will work.
|
|
const proxyDetails = [
|
|
{ type: "http", host: "127.0.0.1", port: getBadProxyPort() },
|
|
{ type: "direct" },
|
|
];
|
|
|
|
// We need to load the content page before loading the proxy extension
|
|
// to ensure that we have a valid content page to run fetch from.
|
|
let contentUrl = `http://${primaryHost}:${primaryPort}/`;
|
|
let page = await ExtensionTestUtils.loadContentPage(contentUrl);
|
|
|
|
let extension = await getProxyExtension(proxyDetails);
|
|
|
|
await ExtensionTestUtils.fetch(contentUrl, `${contentUrl}?t=${Math.random()}`)
|
|
.then(text => {
|
|
equal(text, "ok!", "fetch completed");
|
|
})
|
|
.catch(() => {
|
|
ok(false, "fetch failed");
|
|
});
|
|
|
|
await extension.unload();
|
|
await page.close();
|
|
});
|
|
|
|
add_task(
|
|
{ skip_if: () => directFailoverDisabled },
|
|
async function test_failover_content() {
|
|
// load a content page for fetch and non-system principal, expect
|
|
// no failover
|
|
const proxyDetails = [
|
|
{ type: "http", host: "127.0.0.1", port: getBadProxyPort() },
|
|
];
|
|
|
|
// We need to load the content page before loading the proxy extension
|
|
// to ensure that we have a valid content page to run fetch from.
|
|
let contentUrl = `http://${primaryHost}:${primaryPort}/`;
|
|
let page = await ExtensionTestUtils.loadContentPage(contentUrl);
|
|
|
|
let extension = await getProxyExtension(proxyDetails);
|
|
|
|
await ExtensionTestUtils.fetch(
|
|
contentUrl,
|
|
`${contentUrl}?t=${Math.random()}`
|
|
)
|
|
.then(() => {
|
|
ok(false, "xhr unexpectedly completed");
|
|
})
|
|
.catch(e => {
|
|
equal(
|
|
e.message,
|
|
"NetworkError when attempting to fetch resource.",
|
|
"fetch failed"
|
|
);
|
|
});
|
|
|
|
await extension.unload();
|
|
await page.close();
|
|
}
|
|
);
|
|
|
|
add_task(
|
|
{ skip_if: () => directFailoverDisabled },
|
|
async function test_failover_system() {
|
|
const proxyDetails = [
|
|
{ type: "http", host: "127.0.0.1", port: getBadProxyPort() },
|
|
{ type: "http", host: "127.0.0.1", port: getBadProxyPort() },
|
|
];
|
|
|
|
let extension = await getProxyExtension(proxyDetails);
|
|
|
|
await xhr(`http://${primaryHost}:${primaryPort}/`)
|
|
.then(req => {
|
|
equal(req.proxy.type, "direct", "proxy failover to direct");
|
|
equal(req.text, "ok!", "xhr completed");
|
|
})
|
|
.catch(() => {
|
|
ok(false, "xhr failed");
|
|
});
|
|
|
|
await extension.unload();
|
|
}
|
|
);
|
|
|
|
add_task(
|
|
{
|
|
skip_if: () =>
|
|
AppConstants.platform === "android" || directFailoverDisabled,
|
|
},
|
|
async function test_failover_pac() {
|
|
const badPort = getBadProxyPort();
|
|
|
|
async function background(badPort) {
|
|
let pac = `function FindProxyForURL(url, host) { return "PROXY 127.0.0.1:${badPort}"; }`;
|
|
let proxySettings = {
|
|
proxyType: "autoConfig",
|
|
autoConfigUrl: `data:application/x-ns-proxy-autoconfig;charset=utf-8,${encodeURIComponent(
|
|
pac
|
|
)}`,
|
|
};
|
|
|
|
await browser.proxy.settings.set({ value: proxySettings });
|
|
browser.test.sendMessage("proxied");
|
|
}
|
|
let extensionData = {
|
|
manifest: {
|
|
permissions: ["proxy", "<all_urls>"],
|
|
},
|
|
background: `(${background})(${badPort})`,
|
|
incognitoOverride: "spanning",
|
|
useAddonManager: "temporary",
|
|
};
|
|
|
|
let extension = ExtensionTestUtils.loadExtension(extensionData);
|
|
await extension.startup();
|
|
await extension.awaitMessage("proxied");
|
|
equal(
|
|
Services.prefs.getIntPref("network.proxy.type"),
|
|
2,
|
|
"autoconfig type set"
|
|
);
|
|
ok(
|
|
Services.prefs.getStringPref("network.proxy.autoconfig_url"),
|
|
"autoconfig url set"
|
|
);
|
|
|
|
await xhr(`http://${primaryHost}:${primaryPort}/`)
|
|
.then(req => {
|
|
equal(req.proxy.type, "direct", "proxy failover to direct");
|
|
equal(req.text, "ok!", "xhr completed");
|
|
})
|
|
.catch(() => {
|
|
ok(false, "xhr failed");
|
|
});
|
|
|
|
await extension.unload();
|
|
}
|
|
);
|
|
|
|
add_task(async function test_bypass_proxy() {
|
|
const proxyDetails = [
|
|
{ type: "http", host: "127.0.0.1", port: getBadProxyPort() },
|
|
];
|
|
|
|
let extension = await getProxyExtension(proxyDetails);
|
|
|
|
await xhr(`http://${primaryHost}:${primaryPort}/`, { bypassProxy: true })
|
|
.then(req => {
|
|
equal(req.proxy, undefined, "no proxy used");
|
|
ok(true, "xhr completed");
|
|
})
|
|
.catch(req => {
|
|
equal(req.proxy, undefined, "no proxy used");
|
|
ok(false, "xhr error");
|
|
});
|
|
|
|
await extension.unload();
|
|
});
|
|
|
|
add_task(async function test_bypass_proxy() {
|
|
const proxyDetails = [
|
|
{ type: "http", host: "127.0.0.1", port: getBadProxyPort() },
|
|
];
|
|
|
|
let extension = await getProxyExtension(proxyDetails);
|
|
|
|
await serviceRequest(`http://${primaryHost}:${primaryPort}/`, {
|
|
bypassProxy: true,
|
|
})
|
|
.then(req => {
|
|
equal(req.proxy, undefined, "no proxy used");
|
|
ok(true, "xhr completed");
|
|
})
|
|
.catch(req => {
|
|
equal(req.proxy, undefined, "no proxy used");
|
|
ok(false, "xhr error");
|
|
});
|
|
|
|
await extension.unload();
|
|
});
|
|
|
|
add_task(async function test_failover_system_off() {
|
|
// Test test failover failures, uncomment and set pref to false
|
|
Services.prefs.setBoolPref("network.proxy.failover_direct", false);
|
|
|
|
const proxyDetails = [
|
|
{ type: "http", host: "127.0.0.1", port: getBadProxyPort() },
|
|
];
|
|
|
|
let extension = await getProxyExtension(proxyDetails);
|
|
|
|
await xhr(`http://${primaryHost}:${primaryPort}/`)
|
|
.then(req => {
|
|
equal(req.proxy.sourceId, extension.id, "extension matches");
|
|
ok(false, "xhr completed");
|
|
})
|
|
.catch(req => {
|
|
equal(req.proxy.sourceId, extension.id, "extension matches");
|
|
equal(req.status, 0, "xhr failed");
|
|
});
|
|
|
|
await extension.unload();
|
|
});
|