Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions docs/src/api/params.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,8 @@ Dangerous option; use with care. Defaults to `false`.
proxy.
- `bypass` ?<[string]> Optional comma-separated domains to bypass proxy, for example `".com, chromium.org,
.domain.com"`.
- `username` ?<[string]> Optional username to use if HTTP proxy requires authentication.
- `password` ?<[string]> Optional password to use if HTTP proxy requires authentication.
- `username` ?<[string]> Optional username to use if HTTP or SOCKS5 proxy requires authentication.
- `password` ?<[string]> Optional password to use if HTTP or SOCKS5 proxy requires authentication.

Network proxy settings.

Expand Down Expand Up @@ -857,8 +857,8 @@ Actual picture of each page will be scaled down if necessary to fit the specifie
- `server` <[string]> Proxy to be used for all requests. HTTP and SOCKS proxies are supported, for example
`http://myproxy.com:3128` or `socks5://myproxy.com:3128`. Short form `myproxy.com:3128` is considered an HTTP proxy.
- `bypass` ?<[string]> Optional comma-separated domains to bypass proxy, for example `".com, chromium.org, .domain.com"`.
- `username` ?<[string]> Optional username to use if HTTP proxy requires authentication.
- `password` ?<[string]> Optional password to use if HTTP proxy requires authentication.
- `username` ?<[string]> Optional username to use if HTTP or SOCKS5 proxy requires authentication.
- `password` ?<[string]> Optional password to use if HTTP or SOCKS5 proxy requires authentication.

Network proxy settings to use with this context. Defaults to none.

Expand Down
28 changes: 14 additions & 14 deletions packages/playwright-client/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10368,12 +10368,12 @@ export interface Browser {
bypass?: string;

/**
* Optional username to use if HTTP proxy requires authentication.
* Optional username to use if HTTP or SOCKS5 proxy requires authentication.
*/
username?: string;

/**
* Optional password to use if HTTP proxy requires authentication.
* Optional password to use if HTTP or SOCKS5 proxy requires authentication.
*/
password?: string;
};
Expand Down Expand Up @@ -15769,12 +15769,12 @@ export interface BrowserType<Unused = {}> {
bypass?: string;

/**
* Optional username to use if HTTP proxy requires authentication.
* Optional username to use if HTTP or SOCKS5 proxy requires authentication.
*/
username?: string;

/**
* Optional password to use if HTTP proxy requires authentication.
* Optional password to use if HTTP or SOCKS5 proxy requires authentication.
*/
password?: string;
};
Expand Down Expand Up @@ -16107,12 +16107,12 @@ export interface BrowserType<Unused = {}> {
bypass?: string;

/**
* Optional username to use if HTTP proxy requires authentication.
* Optional username to use if HTTP or SOCKS5 proxy requires authentication.
*/
username?: string;

/**
* Optional password to use if HTTP proxy requires authentication.
* Optional password to use if HTTP or SOCKS5 proxy requires authentication.
*/
password?: string;
};
Expand Down Expand Up @@ -17852,12 +17852,12 @@ export interface APIRequest {
bypass?: string;

/**
* Optional username to use if HTTP proxy requires authentication.
* Optional username to use if HTTP or SOCKS5 proxy requires authentication.
*/
username?: string;

/**
* Optional password to use if HTTP proxy requires authentication.
* Optional password to use if HTTP or SOCKS5 proxy requires authentication.
*/
password?: string;
};
Expand Down Expand Up @@ -22739,12 +22739,12 @@ export interface AndroidDevice {
bypass?: string;

/**
* Optional username to use if HTTP proxy requires authentication.
* Optional username to use if HTTP or SOCKS5 proxy requires authentication.
*/
username?: string;

/**
* Optional password to use if HTTP proxy requires authentication.
* Optional password to use if HTTP or SOCKS5 proxy requires authentication.
*/
password?: string;
};
Expand Down Expand Up @@ -23502,12 +23502,12 @@ export interface LaunchOptions {
bypass?: string;

/**
* Optional username to use if HTTP proxy requires authentication.
* Optional username to use if HTTP or SOCKS5 proxy requires authentication.
*/
username?: string;

/**
* Optional password to use if HTTP proxy requires authentication.
* Optional password to use if HTTP or SOCKS5 proxy requires authentication.
*/
password?: string;
};
Expand Down Expand Up @@ -23919,12 +23919,12 @@ export interface BrowserContextOptions {
bypass?: string;

/**
* Optional username to use if HTTP proxy requires authentication.
* Optional username to use if HTTP or SOCKS5 proxy requires authentication.
*/
username?: string;

/**
* Optional password to use if HTTP proxy requires authentication.
* Optional password to use if HTTP or SOCKS5 proxy requires authentication.
*/
password?: string;
};
Expand Down
12 changes: 12 additions & 0 deletions packages/playwright-core/src/server/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { Download } from './download';
import { SdkObject } from './instrumentation';
import { Page } from './page';
import { ClientCertificatesProxy } from './socksClientCertificatesInterceptor';
import { SocksUpstreamAuthProxy, needsSocksAuthInterception } from './socksProxyAuthInterceptor';
import { PlaywrightPipeServer } from '../remote/playwrightPipeServer';
import { PlaywrightWebSocketServer } from '../remote/playwrightWebSocketServer';
import { BrowserInfo, serverRegistry } from '../serverRegistry';
Expand Down Expand Up @@ -80,6 +81,7 @@ export abstract class Browser extends SdkObject {
private _contextForReuse: { context: BrowserContext, hash: string } | undefined;
_closeReason: string | undefined;
_isCollocatedWithServer: boolean = true;
_socksAuthProxy: SocksUpstreamAuthProxy | undefined;
private _server: BrowserServer;

constructor(parent: SdkObject, options: BrowserOptions) {
Expand All @@ -103,16 +105,24 @@ export abstract class Browser extends SdkObject {
async newContext(progress: Progress, options: types.BrowserContextOptions): Promise<BrowserContext> {
validateBrowserContextOptions(options, this.options);
let clientCertificatesProxy: ClientCertificatesProxy | undefined;
let socksAuthProxy: SocksUpstreamAuthProxy | undefined;
let context: BrowserContext | undefined;
try {
if (options.clientCertificates?.length) {
clientCertificatesProxy = await ClientCertificatesProxy.create(progress, options);
options = { ...options };
options.proxyOverride = clientCertificatesProxy.proxySettings();
options.internalIgnoreHTTPSErrors = true;
} else if (needsSocksAuthInterception(options.proxy)) {
// Browsers do not support RFC 1929; we front the authenticated upstream with a local
// unauthenticated SOCKS5 server and forward connections through.
socksAuthProxy = await SocksUpstreamAuthProxy.create(progress, options.proxy!);
options = { ...options };
options.proxyOverride = socksAuthProxy.proxySettings();
}
context = await progress.race(this.doCreateNewContext(options));
context._clientCertificatesProxy = clientCertificatesProxy;
context._socksAuthProxy = socksAuthProxy;
if ((options as any).__testHookBeforeSetStorageState)
await progress.race((options as any).__testHookBeforeSetStorageState());
await context.setStorageState(progress, options.storageState, 'initial');
Expand All @@ -121,6 +131,7 @@ export abstract class Browser extends SdkObject {
} catch (error) {
await context?.close(progress, { reason: 'Failed to create context' }).catch(() => {});
await clientCertificatesProxy?.close().catch(() => {});
await socksAuthProxy?.close().catch(() => {});
throw error;
}
}
Expand Down Expand Up @@ -174,6 +185,7 @@ export abstract class Browser extends SdkObject {
context.browserClosed();
if (this._defaultContext)
this._defaultContext.browserClosed();
this._socksAuthProxy?.close().catch(() => {});
this.stopServer(nullProgress).catch(() => {});
this.emit(Browser.Events.Disconnected);
this.instrumentation.onBrowserClose(this);
Expand Down
5 changes: 3 additions & 2 deletions packages/playwright-core/src/server/browserContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import type * as frames from './frames';
import type { PageError } from './page';
import type { Progress } from './progress';
import type { ClientCertificatesProxy } from './socksClientCertificatesInterceptor';
import type { SocksUpstreamAuthProxy } from './socksProxyAuthInterceptor';
import type { SerializedStorage } from '@injected/storageScript';
import type * as types from './types';
import type * as channels from '@protocol/channels';
Expand Down Expand Up @@ -111,6 +112,7 @@ export abstract class BrowserContext<EM extends EventMap = EventMap> extends Sdk
_closeReason: string | undefined;
readonly clock: Clock;
_clientCertificatesProxy: ClientCertificatesProxy | undefined;
_socksAuthProxy: SocksUpstreamAuthProxy | undefined;
private _playwrightBindingExposed?: Promise<void>;
readonly dialogManager: DialogManager;
private _consoleApiExposed = false;
Expand Down Expand Up @@ -259,6 +261,7 @@ export abstract class BrowserContext<EM extends EventMap = EventMap> extends Sdk
return;
}
this._clientCertificatesProxy?.close().catch(() => {});
this._socksAuthProxy?.close().catch(() => {});
this.tracing.abort();
if (this._isPersistentContext)
this.onClosePersistent();
Expand Down Expand Up @@ -785,8 +788,6 @@ export function normalizeProxySettings(proxy: types.ProxySettings): types.ProxyS
}
if (url.protocol === 'socks4:' && (proxy.username || proxy.password))
throw new Error(`Socks4 proxy protocol does not support authentication`);
if (url.protocol === 'socks5:' && (proxy.username || proxy.password))
throw new Error(`Browser does not support socks5 proxy authentication`);
server = url.protocol + '//' + url.host;
if (bypass)
bypass = bypass.split(',').map(t => t.trim()).join(',');
Expand Down
22 changes: 21 additions & 1 deletion packages/playwright-core/src/server/browserType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { PipeTransport } from './pipeTransport';
import { isProtocolError } from './protocolError';
import { registry } from './registry';
import { ClientCertificatesProxy } from './socksClientCertificatesInterceptor';
import { SocksUpstreamAuthProxy, needsSocksAuthInterception } from './socksProxyAuthInterceptor';
import { WebSocketTransport } from './transport';

import type { Browser, BrowserOptions, BrowserProcess } from './browser';
Expand Down Expand Up @@ -69,25 +70,44 @@ export abstract class BrowserType extends SdkObject {
const seleniumHubUrl = (options as any).__testHookSeleniumRemoteURL || process.env.SELENIUM_REMOTE_URL;
if (seleniumHubUrl)
return this.launchWithSeleniumHub(progress, seleniumHubUrl, options);
return this._innerLaunchWithRetries(progress, options, undefined, helper.debugProtocolLogger(protocolLogger)).catch(e => { throw this._rewriteStartupLog(e); });
let socksAuthProxy: SocksUpstreamAuthProxy | undefined;
if (needsSocksAuthInterception(options.proxy)) {
socksAuthProxy = await SocksUpstreamAuthProxy.create(progress, options.proxy!);
options = { ...options };
options.proxyOverride = socksAuthProxy.proxySettings();
}
try {
const browser = await this._innerLaunchWithRetries(progress, options, undefined, helper.debugProtocolLogger(protocolLogger)).catch(e => { throw this._rewriteStartupLog(e); });
browser._socksAuthProxy = socksAuthProxy;
return browser;
} catch (error) {
await socksAuthProxy?.close().catch(() => {});
throw error;
}
}

async launchPersistentContext(progress: Progress, userDataDir: string, options: channels.BrowserTypeLaunchPersistentContextOptions & { internalIgnoreHTTPSErrors?: boolean, socksProxyPort?: number }): Promise<BrowserContext> {
const launchOptions = this._validateLaunchOptions(options);
// Note: Any initial TLS requests will fail since we rely on the Page/Frames initialize which sets ignoreHTTPSErrors.
let clientCertificatesProxy: ClientCertificatesProxy | undefined;
let socksAuthProxy: SocksUpstreamAuthProxy | undefined;
if (options.clientCertificates?.length) {
clientCertificatesProxy = await ClientCertificatesProxy.create(progress, options);
launchOptions.proxyOverride = clientCertificatesProxy.proxySettings();
options = { ...options };
options.internalIgnoreHTTPSErrors = true;
} else if (needsSocksAuthInterception(options.proxy)) {
socksAuthProxy = await SocksUpstreamAuthProxy.create(progress, options.proxy!);
launchOptions.proxyOverride = socksAuthProxy.proxySettings();
}
try {
const browser = await this._innerLaunchWithRetries(progress, launchOptions, options, helper.debugProtocolLogger(), userDataDir).catch(e => { throw this._rewriteStartupLog(e); });
browser._defaultContext!._clientCertificatesProxy = clientCertificatesProxy;
browser._defaultContext!._socksAuthProxy = socksAuthProxy;
return browser._defaultContext!;
} catch (error) {
await clientCertificatesProxy?.close().catch(() => {});
await socksAuthProxy?.close().catch(() => {});
throw error;
}
}
Expand Down
Loading
Loading