mirror of
https://github.com/microsoft/playwright.git
synced 2024-10-27 05:46:28 +03:00
feat(firefox): migrate to protocol-based proxy implementation (#3362)
This migrates Firefox to the protocol-based proxy implementation. Benefits: - supports secure web proxies (already supported by Chromium) - unlocks support for SOCKS proxies with authentication
This commit is contained in:
parent
bfdb59eada
commit
2db97e3b2d
@ -46,6 +46,24 @@ export class FFBrowser extends BrowserBase {
|
||||
browser._defaultContext = new FFBrowserContext(browser, null, options.persistent);
|
||||
promises.push((browser._defaultContext as FFBrowserContext)._initialize());
|
||||
}
|
||||
if (options.proxy) {
|
||||
const proxyServer = new URL(options.proxy.server);
|
||||
let aType: 'http'|'https'|'socks'|'socks4' = 'http';
|
||||
if (proxyServer.protocol === 'socks5:')
|
||||
aType = 'socks';
|
||||
else if (proxyServer.protocol === 'socks4:')
|
||||
aType = 'socks4';
|
||||
else if (proxyServer.protocol === 'https:')
|
||||
aType = 'https';
|
||||
promises.push(browser._connection.send('Browser.setBrowserProxy', {
|
||||
type: aType,
|
||||
bypass: options.proxy.bypass ? options.proxy.bypass.split(',').map(domain => domain.trim()) : [],
|
||||
host: proxyServer.hostname,
|
||||
port: parseInt(proxyServer.port, 10),
|
||||
username: options.proxy.username,
|
||||
password: options.proxy.password,
|
||||
}));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
return browser;
|
||||
}
|
||||
@ -159,7 +177,6 @@ export class FFBrowserContext extends BrowserContextBase {
|
||||
super(browser, options, !browserContextId);
|
||||
this._browser = browser;
|
||||
this._browserContextId = browserContextId;
|
||||
this._authenticateProxyViaHeader();
|
||||
}
|
||||
|
||||
async _initialize() {
|
||||
|
@ -100,14 +100,25 @@ export module Protocol {
|
||||
}[];
|
||||
};
|
||||
export type setExtraHTTPHeadersReturnValue = void;
|
||||
export type setProxyParameters = {
|
||||
export type setBrowserProxyParameters = {
|
||||
type: ("http"|"https"|"socks"|"socks4");
|
||||
bypass: string[];
|
||||
host: string;
|
||||
port: number;
|
||||
username?: string;
|
||||
password?: string;
|
||||
};
|
||||
export type setBrowserProxyReturnValue = void;
|
||||
export type setContextProxyParameters = {
|
||||
browserContextId?: string;
|
||||
type: ("http"|"https"|"socks"|"socks4");
|
||||
bypass: string[];
|
||||
host: string;
|
||||
port: number;
|
||||
username?: string;
|
||||
password?: string;
|
||||
};
|
||||
export type setProxyReturnValue = void;
|
||||
export type setContextProxyReturnValue = void;
|
||||
export type setHTTPCredentialsParameters = {
|
||||
browserContextId?: string;
|
||||
credentials: {
|
||||
@ -940,7 +951,8 @@ export module Protocol {
|
||||
"Browser.close": Browser.closeParameters;
|
||||
"Browser.getInfo": Browser.getInfoParameters;
|
||||
"Browser.setExtraHTTPHeaders": Browser.setExtraHTTPHeadersParameters;
|
||||
"Browser.setProxy": Browser.setProxyParameters;
|
||||
"Browser.setBrowserProxy": Browser.setBrowserProxyParameters;
|
||||
"Browser.setContextProxy": Browser.setContextProxyParameters;
|
||||
"Browser.setHTTPCredentials": Browser.setHTTPCredentialsParameters;
|
||||
"Browser.setRequestInterception": Browser.setRequestInterceptionParameters;
|
||||
"Browser.setGeolocationOverride": Browser.setGeolocationOverrideParameters;
|
||||
@ -1011,7 +1023,8 @@ export module Protocol {
|
||||
"Browser.close": Browser.closeReturnValue;
|
||||
"Browser.getInfo": Browser.getInfoReturnValue;
|
||||
"Browser.setExtraHTTPHeaders": Browser.setExtraHTTPHeadersReturnValue;
|
||||
"Browser.setProxy": Browser.setProxyReturnValue;
|
||||
"Browser.setBrowserProxy": Browser.setBrowserProxyReturnValue;
|
||||
"Browser.setContextProxy": Browser.setContextProxyReturnValue;
|
||||
"Browser.setHTTPCredentials": Browser.setHTTPCredentialsReturnValue;
|
||||
"Browser.setRequestInterception": Browser.setRequestInterceptionReturnValue;
|
||||
"Browser.setGeolocationOverride": Browser.setGeolocationOverrideReturnValue;
|
||||
|
@ -65,7 +65,7 @@ export class Firefox extends BrowserTypeBase {
|
||||
}
|
||||
|
||||
_defaultArgs(options: LaunchNonPersistentOptions, isPersistent: boolean, userDataDir: string): string[] {
|
||||
const { args = [], proxy, devtools, headless } = options;
|
||||
const { args = [], devtools, headless } = options;
|
||||
if (devtools)
|
||||
console.warn('devtools parameter is not supported as a launch argument in Firefox. You can launch the devtools window manually.');
|
||||
const userDataDirArg = args.find(arg => arg.startsWith('-profile') || arg.startsWith('--profile'));
|
||||
@ -73,25 +73,7 @@ export class Firefox extends BrowserTypeBase {
|
||||
throw new Error('Pass userDataDir parameter instead of specifying -profile argument');
|
||||
if (args.find(arg => arg.startsWith('-juggler')))
|
||||
throw new Error('Use the port parameter instead of -juggler argument');
|
||||
let firefoxUserPrefs = isPersistent ? undefined : options.firefoxUserPrefs;
|
||||
if (proxy) {
|
||||
// TODO: we should support proxy in persistent context without overriding user prefs.
|
||||
firefoxUserPrefs = firefoxUserPrefs || {};
|
||||
firefoxUserPrefs['network.proxy.type'] = 1;
|
||||
const proxyServer = new URL(proxy.server);
|
||||
const isSocks = proxyServer.protocol === 'socks5:';
|
||||
if (isSocks) {
|
||||
firefoxUserPrefs['network.proxy.socks'] = proxyServer.hostname;
|
||||
firefoxUserPrefs['network.proxy.socks_port'] = parseInt(proxyServer.port, 10);
|
||||
} else {
|
||||
firefoxUserPrefs['network.proxy.http'] = proxyServer.hostname;
|
||||
firefoxUserPrefs['network.proxy.http_port'] = parseInt(proxyServer.port, 10);
|
||||
firefoxUserPrefs['network.proxy.ssl'] = proxyServer.hostname;
|
||||
firefoxUserPrefs['network.proxy.ssl_port'] = parseInt(proxyServer.port, 10);
|
||||
}
|
||||
if (proxy.bypass)
|
||||
firefoxUserPrefs['network.proxy.no_proxies_on'] = proxy.bypass;
|
||||
}
|
||||
const firefoxUserPrefs = isPersistent ? undefined : options.firefoxUserPrefs;
|
||||
if (firefoxUserPrefs) {
|
||||
const lines: string[] = [];
|
||||
for (const [name, value] of Object.entries(firefoxUserPrefs))
|
||||
|
@ -60,30 +60,39 @@ it.fail(CHROMIUM && !HEADLESS)('should exclude patterns', async ({browserType, d
|
||||
server.setRoute('/target.html', async (req, res) => {
|
||||
res.end('<html><title>Served by the proxy</title></html>');
|
||||
});
|
||||
// FYI: using long and weird domain names to avoid ATT DNS hijacking
|
||||
// that resolves everything to some weird search results page.
|
||||
//
|
||||
// @see https://gist.github.com/CollinChaffin/24f6c9652efb3d6d5ef2f5502720ef00
|
||||
const browser = await browserType.launch({
|
||||
...defaultBrowserOptions,
|
||||
proxy: { server: `localhost:${server.PORT}`, bypass: 'non-existent1.com, .non-existent2.com, .zone' }
|
||||
proxy: { server: `localhost:${server.PORT}`, bypass: '1.non.existent.domain.for.the.test, 2.non.existent.domain.for.the.test, .another.test' }
|
||||
});
|
||||
|
||||
const page = await browser.newPage();
|
||||
await page.goto('http://non-existent.com/target.html');
|
||||
await page.goto('http://0.non.existent.domain.for.the.test/target.html');
|
||||
expect(await page.title()).toBe('Served by the proxy');
|
||||
|
||||
{
|
||||
const error = await page.goto('http://non-existent1.com/target.html').catch(e => e);
|
||||
const error = await page.goto('http://1.non.existent.domain.for.the.test/target.html').catch(e => e);
|
||||
expect(error.message).toBeTruthy();
|
||||
}
|
||||
|
||||
{
|
||||
const error = await page.goto('http://sub.non-existent2.com/target.html').catch(e => e);
|
||||
const error = await page.goto('http://2.non.existent.domain.for.the.test/target.html').catch(e => e);
|
||||
expect(error.message).toBeTruthy();
|
||||
}
|
||||
|
||||
{
|
||||
const error = await page.goto('http://foo.zone/target.html').catch(e => e);
|
||||
const error = await page.goto('http://foo.is.the.another.test/target.html').catch(e => e);
|
||||
expect(error.message).toBeTruthy();
|
||||
}
|
||||
|
||||
{
|
||||
await page.goto('http://3.non.existent.domain.for.the.test/target.html');
|
||||
expect(await page.title()).toBe('Served by the proxy');
|
||||
}
|
||||
|
||||
if (CHROMIUM) {
|
||||
// Should successfully navigate to the error page.
|
||||
await page.waitForEvent('framenavigated', frame => frame.url() === 'chrome-error://chromewebdata/');
|
||||
|
Loading…
Reference in New Issue
Block a user