feat(connect): allow exposing local network to the remote browser (experimental) (#19372)
`connectOptions: { _exposeNetwork: '*' | 'localhost' }`
References #19287.
This commit is contained in:
parent
8ea48752db
commit
256e9fd443
|
|
@ -69,7 +69,7 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
|
||||||
|
|
||||||
async launch(options: LaunchOptions = {}): Promise<Browser> {
|
async launch(options: LaunchOptions = {}): Promise<Browser> {
|
||||||
if (this._defaultConnectOptions)
|
if (this._defaultConnectOptions)
|
||||||
return await this._connectInsteadOfLaunching();
|
return await this._connectInsteadOfLaunching(this._defaultConnectOptions);
|
||||||
|
|
||||||
const logger = options.logger || this._defaultLaunchOptions?.logger;
|
const logger = options.logger || this._defaultLaunchOptions?.logger;
|
||||||
assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead');
|
assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead');
|
||||||
|
|
@ -89,14 +89,15 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _connectInsteadOfLaunching(): Promise<Browser> {
|
private async _connectInsteadOfLaunching(connectOptions: ConnectOptions): Promise<Browser> {
|
||||||
const connectOptions = this._defaultConnectOptions!;
|
return this._connect({
|
||||||
return this._connect(connectOptions.wsEndpoint, {
|
wsEndpoint: connectOptions.wsEndpoint,
|
||||||
headers: {
|
headers: {
|
||||||
'x-playwright-browser': this.name(),
|
|
||||||
'x-playwright-launch-options': JSON.stringify(this._defaultLaunchOptions || {}),
|
'x-playwright-launch-options': JSON.stringify(this._defaultLaunchOptions || {}),
|
||||||
...connectOptions.headers,
|
...connectOptions.headers,
|
||||||
},
|
},
|
||||||
|
_exposeNetwork: connectOptions._exposeNetwork,
|
||||||
|
slowMo: connectOptions.slowMo,
|
||||||
timeout: connectOptions.timeout ?? 3 * 60 * 1000, // 3 minutes
|
timeout: connectOptions.timeout ?? 3 * 60 * 1000, // 3 minutes
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -132,22 +133,28 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(options: api.ConnectOptions & { wsEndpoint?: string }): Promise<api.Browser>;
|
connect(options: api.ConnectOptions & { wsEndpoint: string }): Promise<api.Browser>;
|
||||||
connect(wsEndpoint: string, options?: api.ConnectOptions): Promise<api.Browser>;
|
connect(wsEndpoint: string, options?: api.ConnectOptions): Promise<api.Browser>;
|
||||||
async connect(optionsOrWsEndpoint: string|(api.ConnectOptions & { wsEndpoint?: string }), options?: api.ConnectOptions): Promise<Browser>{
|
async connect(optionsOrWsEndpoint: string | (api.ConnectOptions & { wsEndpoint: string }), options?: api.ConnectOptions): Promise<Browser>{
|
||||||
if (typeof optionsOrWsEndpoint === 'string')
|
if (typeof optionsOrWsEndpoint === 'string')
|
||||||
return this._connect(optionsOrWsEndpoint, options);
|
return this._connect({ ...options, wsEndpoint: optionsOrWsEndpoint });
|
||||||
assert(optionsOrWsEndpoint.wsEndpoint, 'options.wsEndpoint is required');
|
assert(optionsOrWsEndpoint.wsEndpoint, 'options.wsEndpoint is required');
|
||||||
return this._connect(optionsOrWsEndpoint.wsEndpoint, optionsOrWsEndpoint);
|
return this._connect(optionsOrWsEndpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _connect(wsEndpoint: string, params: Partial<ConnectOptions> = {}): Promise<Browser> {
|
async _connect(params: ConnectOptions): Promise<Browser> {
|
||||||
const logger = params.logger;
|
const logger = params.logger;
|
||||||
return await this._wrapApiCall(async () => {
|
return await this._wrapApiCall(async () => {
|
||||||
const deadline = params.timeout ? monotonicTime() + params.timeout : 0;
|
const deadline = params.timeout ? monotonicTime() + params.timeout : 0;
|
||||||
const headers = { 'x-playwright-browser': this.name(), ...params.headers };
|
const headers = { 'x-playwright-browser': this.name(), ...params.headers };
|
||||||
const localUtils = this._connection.localUtils();
|
const localUtils = this._connection.localUtils();
|
||||||
const connectParams: channels.LocalUtilsConnectParams = { wsEndpoint, headers, slowMo: params.slowMo, timeout: params.timeout };
|
const connectParams: channels.LocalUtilsConnectParams = {
|
||||||
|
wsEndpoint: params.wsEndpoint,
|
||||||
|
headers,
|
||||||
|
exposeNetwork: params._exposeNetwork,
|
||||||
|
slowMo: params.slowMo,
|
||||||
|
timeout: params.timeout,
|
||||||
|
};
|
||||||
if ((params as any).__testHookRedirectPortForwarding)
|
if ((params as any).__testHookRedirectPortForwarding)
|
||||||
connectParams.socksProxyRedirectPortForTest = (params as any).__testHookRedirectPortForwarding;
|
connectParams.socksProxyRedirectPortForTest = (params as any).__testHookRedirectPortForwarding;
|
||||||
const { pipe } = await localUtils._channel.connect(connectParams);
|
const { pipe } = await localUtils._channel.connect(connectParams);
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,7 @@ export type LaunchPersistentContextOptions = Omit<LaunchOptionsBase & BrowserCon
|
||||||
export type ConnectOptions = {
|
export type ConnectOptions = {
|
||||||
wsEndpoint: string,
|
wsEndpoint: string,
|
||||||
headers?: { [key: string]: string; };
|
headers?: { [key: string]: string; };
|
||||||
|
_exposeNetwork?: string,
|
||||||
slowMo?: number,
|
slowMo?: number,
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
logger?: Logger,
|
logger?: Logger,
|
||||||
|
|
|
||||||
|
|
@ -431,19 +431,31 @@ export class SocksProxyHandler extends EventEmitter {
|
||||||
};
|
};
|
||||||
|
|
||||||
private _sockets = new Map<string, net.Socket>();
|
private _sockets = new Map<string, net.Socket>();
|
||||||
|
private _pattern: string | undefined;
|
||||||
private _redirectPortForTest: number | undefined;
|
private _redirectPortForTest: number | undefined;
|
||||||
|
|
||||||
constructor(redirectPortForTest?: number) {
|
constructor(pattern: string | undefined, redirectPortForTest?: number) {
|
||||||
super();
|
super();
|
||||||
|
this._pattern = pattern;
|
||||||
this._redirectPortForTest = redirectPortForTest;
|
this._redirectPortForTest = redirectPortForTest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _matchesPattern(host: string, port: number) {
|
||||||
|
return this._pattern === '*' || (this._pattern === 'localhost' && host === 'localhost');
|
||||||
|
}
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
for (const uid of this._sockets.keys())
|
for (const uid of this._sockets.keys())
|
||||||
this.socketClosed({ uid });
|
this.socketClosed({ uid });
|
||||||
}
|
}
|
||||||
|
|
||||||
async socketRequested({ uid, host, port }: SocksSocketRequestedPayload): Promise<void> {
|
async socketRequested({ uid, host, port }: SocksSocketRequestedPayload): Promise<void> {
|
||||||
|
if (!this._matchesPattern(host, port)) {
|
||||||
|
const payload: SocksSocketFailedPayload = { uid, errorCode: 'ECONNREFUSED' };
|
||||||
|
this.emit(SocksProxyHandler.Events.SocksFailed, payload);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (host === 'local.playwright')
|
if (host === 'local.playwright')
|
||||||
host = '127.0.0.1';
|
host = '127.0.0.1';
|
||||||
// Node.js 17 does resolve localhost to ipv6
|
// Node.js 17 does resolve localhost to ipv6
|
||||||
|
|
|
||||||
|
|
@ -288,7 +288,7 @@ async function tetherHostNetwork(endpoint: string) {
|
||||||
'x-playwright-proxy': '*',
|
'x-playwright-proxy': '*',
|
||||||
};
|
};
|
||||||
const transport = await WebSocketTransport.connect(undefined /* progress */, wsEndpoint, headers, true /* followRedirects */);
|
const transport = await WebSocketTransport.connect(undefined /* progress */, wsEndpoint, headers, true /* followRedirects */);
|
||||||
const socksInterceptor = new SocksInterceptor(transport, undefined);
|
const socksInterceptor = new SocksInterceptor(transport, '*', undefined);
|
||||||
transport.onmessage = json => socksInterceptor.interceptMessage(json);
|
transport.onmessage = json => socksInterceptor.interceptMessage(json);
|
||||||
transport.onclose = () => {
|
transport.onclose = () => {
|
||||||
socksInterceptor.cleanup();
|
socksInterceptor.cleanup();
|
||||||
|
|
@ -387,7 +387,7 @@ export function addDockerCLI(program: Command) {
|
||||||
.option('--browser <name>', 'browser to launch')
|
.option('--browser <name>', 'browser to launch')
|
||||||
.option('--endpoint <url>', 'server endpoint')
|
.option('--endpoint <url>', 'server endpoint')
|
||||||
.action(async function(options: { browser: string, endpoint: string }) {
|
.action(async function(options: { browser: string, endpoint: string }) {
|
||||||
let browserType: any;
|
let browserType: playwright.BrowserType | undefined;
|
||||||
if (options.browser === 'chromium')
|
if (options.browser === 'chromium')
|
||||||
browserType = playwright.chromium;
|
browserType = playwright.chromium;
|
||||||
else if (options.browser === 'firefox')
|
else if (options.browser === 'firefox')
|
||||||
|
|
@ -404,9 +404,9 @@ export function addDockerCLI(program: Command) {
|
||||||
headless: false,
|
headless: false,
|
||||||
viewport: null,
|
viewport: null,
|
||||||
}),
|
}),
|
||||||
'x-playwright-proxy': '*',
|
|
||||||
},
|
},
|
||||||
});
|
_exposeNetwork: '*',
|
||||||
|
} as any);
|
||||||
const context = await browser.newContext();
|
const context = await browser.newContext();
|
||||||
context.on('page', (page: playwright.Page) => {
|
context.on('page', (page: playwright.Page) => {
|
||||||
page.on('dialog', () => {}); // Prevent dialogs from being automatically dismissed.
|
page.on('dialog', () => {}); // Prevent dialogs from being automatically dismissed.
|
||||||
|
|
|
||||||
|
|
@ -248,6 +248,7 @@ scheme.LocalUtilsHarUnzipResult = tOptional(tObject({}));
|
||||||
scheme.LocalUtilsConnectParams = tObject({
|
scheme.LocalUtilsConnectParams = tObject({
|
||||||
wsEndpoint: tString,
|
wsEndpoint: tString,
|
||||||
headers: tOptional(tAny),
|
headers: tOptional(tAny),
|
||||||
|
exposeNetwork: tOptional(tString),
|
||||||
slowMo: tOptional(tNumber),
|
slowMo: tOptional(tNumber),
|
||||||
timeout: tOptional(tNumber),
|
timeout: tOptional(tNumber),
|
||||||
socksProxyRedirectPortForTest: tOptional(tNumber),
|
socksProxyRedirectPortForTest: tOptional(tNumber),
|
||||||
|
|
|
||||||
|
|
@ -156,11 +156,15 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.
|
||||||
const controller = new ProgressController(metadata, this._object as SdkObject);
|
const controller = new ProgressController(metadata, this._object as SdkObject);
|
||||||
controller.setLogName('browser');
|
controller.setLogName('browser');
|
||||||
return await controller.run(async progress => {
|
return await controller.run(async progress => {
|
||||||
const paramsHeaders = Object.assign({ 'User-Agent': getUserAgent() }, params.headers || {});
|
const wsHeaders = {
|
||||||
|
'User-Agent': getUserAgent(),
|
||||||
|
'x-playwright-proxy': params.exposeNetwork ?? '',
|
||||||
|
...params.headers,
|
||||||
|
};
|
||||||
const wsEndpoint = await urlToWSEndpoint(progress, params.wsEndpoint);
|
const wsEndpoint = await urlToWSEndpoint(progress, params.wsEndpoint);
|
||||||
|
|
||||||
const transport = await WebSocketTransport.connect(progress, wsEndpoint, paramsHeaders, true);
|
const transport = await WebSocketTransport.connect(progress, wsEndpoint, wsHeaders, true);
|
||||||
const socksInterceptor = new SocksInterceptor(transport, params.socksProxyRedirectPortForTest);
|
const socksInterceptor = new SocksInterceptor(transport, params.exposeNetwork, params.socksProxyRedirectPortForTest);
|
||||||
const pipe = new JsonPipeDispatcher(this);
|
const pipe = new JsonPipeDispatcher(this);
|
||||||
transport.onmessage = json => {
|
transport.onmessage = json => {
|
||||||
if (socksInterceptor.interceptMessage(json))
|
if (socksInterceptor.interceptMessage(json))
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ export class SocksInterceptor {
|
||||||
private _socksSupportObjectGuid?: string;
|
private _socksSupportObjectGuid?: string;
|
||||||
private _ids = new Set<number>();
|
private _ids = new Set<number>();
|
||||||
|
|
||||||
constructor(transport: WebSocketTransport, redirectPortForTest: number | undefined) {
|
constructor(transport: WebSocketTransport, pattern: string | undefined, redirectPortForTest: number | undefined) {
|
||||||
this._handler = new socks.SocksProxyHandler(redirectPortForTest);
|
this._handler = new socks.SocksProxyHandler(pattern, redirectPortForTest);
|
||||||
|
|
||||||
let lastId = -1;
|
let lastId = -1;
|
||||||
this._channel = new Proxy(new EventEmitter(), {
|
this._channel = new Proxy(new EventEmitter(), {
|
||||||
|
|
|
||||||
|
|
@ -87,8 +87,9 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
||||||
}
|
}
|
||||||
return use({
|
return use({
|
||||||
wsEndpoint,
|
wsEndpoint,
|
||||||
headers
|
headers,
|
||||||
});
|
_exposeNetwork: process.env.PW_TEST_CONNECT_EXPOSE_NETWORK,
|
||||||
|
} as any);
|
||||||
}, { scope: 'worker', option: true }],
|
}, { scope: 'worker', option: true }],
|
||||||
screenshot: ['off', { scope: 'worker', option: true }],
|
screenshot: ['off', { scope: 'worker', option: true }],
|
||||||
video: ['off', { scope: 'worker', option: true }],
|
video: ['off', { scope: 'worker', option: true }],
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,7 @@ export const dockerPlugin: TestRunnerPlugin = {
|
||||||
throw new Error('ERROR: please launch docker container separately!');
|
throw new Error('ERROR: please launch docker container separately!');
|
||||||
println('');
|
println('');
|
||||||
process.env.PW_TEST_CONNECT_WS_ENDPOINT = info.httpEndpoint;
|
process.env.PW_TEST_CONNECT_WS_ENDPOINT = info.httpEndpoint;
|
||||||
process.env.PW_TEST_CONNECT_HEADERS = JSON.stringify({
|
process.env.PW_TEST_CONNECT_EXPOSE_NETWORK = '*';
|
||||||
'x-playwright-proxy': '*',
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -446,12 +446,14 @@ export type LocalUtilsHarUnzipResult = void;
|
||||||
export type LocalUtilsConnectParams = {
|
export type LocalUtilsConnectParams = {
|
||||||
wsEndpoint: string,
|
wsEndpoint: string,
|
||||||
headers?: any,
|
headers?: any,
|
||||||
|
exposeNetwork?: string,
|
||||||
slowMo?: number,
|
slowMo?: number,
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
socksProxyRedirectPortForTest?: number,
|
socksProxyRedirectPortForTest?: number,
|
||||||
};
|
};
|
||||||
export type LocalUtilsConnectOptions = {
|
export type LocalUtilsConnectOptions = {
|
||||||
headers?: any,
|
headers?: any,
|
||||||
|
exposeNetwork?: string,
|
||||||
slowMo?: number,
|
slowMo?: number,
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
socksProxyRedirectPortForTest?: number,
|
socksProxyRedirectPortForTest?: number,
|
||||||
|
|
|
||||||
|
|
@ -535,6 +535,7 @@ LocalUtils:
|
||||||
parameters:
|
parameters:
|
||||||
wsEndpoint: string
|
wsEndpoint: string
|
||||||
headers: json?
|
headers: json?
|
||||||
|
exposeNetwork: string?
|
||||||
slowMo: number?
|
slowMo: number?
|
||||||
timeout: number?
|
timeout: number?
|
||||||
socksProxyRedirectPortForTest: number?
|
socksProxyRedirectPortForTest: number?
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import type { BrowserType, Browser, LaunchOptions } from 'playwright-core';
|
import type { BrowserType, Browser } from 'playwright-core';
|
||||||
import type { CommonFixtures, TestChildProcess } from './commonFixtures';
|
import type { CommonFixtures, TestChildProcess } from './commonFixtures';
|
||||||
|
|
||||||
export interface PlaywrightServer {
|
export interface PlaywrightServer {
|
||||||
|
|
@ -79,7 +79,7 @@ export class RemoteServer implements PlaywrightServer {
|
||||||
const browserOptions = (browserType as any)._defaultLaunchOptions;
|
const browserOptions = (browserType as any)._defaultLaunchOptions;
|
||||||
// Copy options to prevent a large JSON string when launching subprocess.
|
// Copy options to prevent a large JSON string when launching subprocess.
|
||||||
// Otherwise, we get `Error: spawn ENAMETOOLONG` on Windows.
|
// Otherwise, we get `Error: spawn ENAMETOOLONG` on Windows.
|
||||||
const launchOptions: LaunchOptions = {
|
const launchOptions: Parameters<BrowserType['launchServer']>[0] = {
|
||||||
args: browserOptions.args,
|
args: browserOptions.args,
|
||||||
headless: browserOptions.headless,
|
headless: browserOptions.headless,
|
||||||
channel: browserOptions.channel,
|
channel: browserOptions.channel,
|
||||||
|
|
|
||||||
|
|
@ -679,7 +679,6 @@ for (const kind of ['launchServer', 'run-server'] as const) {
|
||||||
test.describe('socks proxy', () => {
|
test.describe('socks proxy', () => {
|
||||||
test.fixme(({ platform, browserName }) => browserName === 'webkit' && platform === 'win32');
|
test.fixme(({ platform, browserName }) => browserName === 'webkit' && platform === 'win32');
|
||||||
test.skip(({ mode }) => mode !== 'default');
|
test.skip(({ mode }) => mode !== 'default');
|
||||||
test.skip(kind === 'launchServer', 'This feature is not yet supported in launchServer');
|
|
||||||
|
|
||||||
test('should forward non-forwarded requests', async ({ server, startRemoteServer, connect }) => {
|
test('should forward non-forwarded requests', async ({ server, startRemoteServer, connect }) => {
|
||||||
let reachedOriginalTarget = false;
|
let reachedOriginalTarget = false;
|
||||||
|
|
@ -688,9 +687,7 @@ for (const kind of ['launchServer', 'run-server'] as const) {
|
||||||
res.end('<html><body>original-target</body></html>');
|
res.end('<html><body>original-target</body></html>');
|
||||||
});
|
});
|
||||||
const remoteServer = await startRemoteServer(kind);
|
const remoteServer = await startRemoteServer(kind);
|
||||||
const browser = await connect(remoteServer.wsEndpoint(), {
|
const browser = await connect(remoteServer.wsEndpoint(), { _exposeNetwork: '*' } as any);
|
||||||
headers: { 'x-playwright-proxy': '*' }
|
|
||||||
});
|
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
await page.goto(server.PREFIX + '/foo.html');
|
await page.goto(server.PREFIX + '/foo.html');
|
||||||
expect(await page.content()).toContain('original-target');
|
expect(await page.content()).toContain('original-target');
|
||||||
|
|
@ -707,9 +704,7 @@ for (const kind of ['launchServer', 'run-server'] as const) {
|
||||||
});
|
});
|
||||||
const examplePort = 20_000 + testInfo.workerIndex * 3;
|
const examplePort = 20_000 + testInfo.workerIndex * 3;
|
||||||
const remoteServer = await startRemoteServer(kind);
|
const remoteServer = await startRemoteServer(kind);
|
||||||
const browser = await connect(remoteServer.wsEndpoint(), {
|
const browser = await connect(remoteServer.wsEndpoint(), { _exposeNetwork: '*' } as any, dummyServerPort);
|
||||||
headers: { 'x-playwright-proxy': '*' }
|
|
||||||
}, dummyServerPort);
|
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
await page.goto(`http://127.0.0.1:${examplePort}/foo.html`);
|
await page.goto(`http://127.0.0.1:${examplePort}/foo.html`);
|
||||||
expect(await page.content()).toContain('from-dummy-server');
|
expect(await page.content()).toContain('from-dummy-server');
|
||||||
|
|
@ -726,9 +721,7 @@ for (const kind of ['launchServer', 'run-server'] as const) {
|
||||||
});
|
});
|
||||||
const examplePort = 20_000 + workerInfo.workerIndex * 3;
|
const examplePort = 20_000 + workerInfo.workerIndex * 3;
|
||||||
const remoteServer = await startRemoteServer(kind);
|
const remoteServer = await startRemoteServer(kind);
|
||||||
const browser = await connect(remoteServer.wsEndpoint(), {
|
const browser = await connect(remoteServer.wsEndpoint(), { _exposeNetwork: '*' } as any, dummyServerPort);
|
||||||
headers: { 'x-playwright-proxy': '*' }
|
|
||||||
}, dummyServerPort);
|
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
const response = await page.request.get(`http://127.0.0.1:${examplePort}/foo.html`);
|
const response = await page.request.get(`http://127.0.0.1:${examplePort}/foo.html`);
|
||||||
expect(response.status()).toBe(200);
|
expect(response.status()).toBe(200);
|
||||||
|
|
@ -744,9 +737,7 @@ for (const kind of ['launchServer', 'run-server'] as const) {
|
||||||
});
|
});
|
||||||
const examplePort = 20_000 + workerInfo.workerIndex * 3;
|
const examplePort = 20_000 + workerInfo.workerIndex * 3;
|
||||||
const remoteServer = await startRemoteServer(kind);
|
const remoteServer = await startRemoteServer(kind);
|
||||||
const browser = await connect(remoteServer.wsEndpoint(), {
|
const browser = await connect(remoteServer.wsEndpoint(), { _exposeNetwork: '*' } as any, dummyServerPort);
|
||||||
headers: { 'x-playwright-proxy': '*' }
|
|
||||||
}, dummyServerPort);
|
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
await page.goto(`http://local.playwright:${examplePort}/foo.html`);
|
await page.goto(`http://local.playwright:${examplePort}/foo.html`);
|
||||||
expect(await page.content()).toContain('from-dummy-server');
|
expect(await page.content()).toContain('from-dummy-server');
|
||||||
|
|
@ -756,9 +747,7 @@ for (const kind of ['launchServer', 'run-server'] as const) {
|
||||||
test('should lead to the error page for forwarded requests when the connection is refused', async ({ connect, startRemoteServer, browserName }, workerInfo) => {
|
test('should lead to the error page for forwarded requests when the connection is refused', async ({ connect, startRemoteServer, browserName }, workerInfo) => {
|
||||||
const examplePort = 20_000 + workerInfo.workerIndex * 3;
|
const examplePort = 20_000 + workerInfo.workerIndex * 3;
|
||||||
const remoteServer = await startRemoteServer(kind);
|
const remoteServer = await startRemoteServer(kind);
|
||||||
const browser = await connect(remoteServer.wsEndpoint(), {
|
const browser = await connect(remoteServer.wsEndpoint(), { _exposeNetwork: '*' } as any);
|
||||||
headers: { 'x-playwright-proxy': '*' }
|
|
||||||
});
|
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
const error = await page.goto(`http://127.0.0.1:${examplePort}`).catch(e => e);
|
const error = await page.goto(`http://127.0.0.1:${examplePort}`).catch(e => e);
|
||||||
if (browserName === 'chromium')
|
if (browserName === 'chromium')
|
||||||
|
|
@ -779,9 +768,7 @@ for (const kind of ['launchServer', 'run-server'] as const) {
|
||||||
});
|
});
|
||||||
const examplePort = 20_000 + workerInfo.workerIndex * 3;
|
const examplePort = 20_000 + workerInfo.workerIndex * 3;
|
||||||
const remoteServer = await startRemoteServer(kind);
|
const remoteServer = await startRemoteServer(kind);
|
||||||
const browser = await connect(remoteServer.wsEndpoint(), {
|
const browser = await connect(remoteServer.wsEndpoint(), { _exposeNetwork: 'localhost' } as any, dummyServerPort);
|
||||||
headers: { 'x-playwright-proxy': 'localhost' }
|
|
||||||
}, dummyServerPort);
|
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
|
|
||||||
// localhost should be proxied.
|
// localhost should be proxied.
|
||||||
|
|
@ -801,6 +788,30 @@ for (const kind of ['launchServer', 'run-server'] as const) {
|
||||||
});
|
});
|
||||||
expect(failed).toBe(true);
|
expect(failed).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should check proxy pattern on the client', async ({ connect, startRemoteServer, server, browserName, platform, dummyServerPort }, workerInfo) => {
|
||||||
|
let reachedOriginalTarget = false;
|
||||||
|
server.setRoute('/foo.html', async (req, res) => {
|
||||||
|
reachedOriginalTarget = true;
|
||||||
|
res.end('<html><body>from-original-server</body></html>');
|
||||||
|
});
|
||||||
|
const remoteServer = await startRemoteServer(kind);
|
||||||
|
const browser = await connect(remoteServer.wsEndpoint(), {
|
||||||
|
_exposeNetwork: 'localhost',
|
||||||
|
headers: {
|
||||||
|
'x-playwright-proxy': '*',
|
||||||
|
},
|
||||||
|
} as any, dummyServerPort);
|
||||||
|
const page = await browser.newPage();
|
||||||
|
|
||||||
|
// 127.0.0.1 should fail on the client side.
|
||||||
|
let failed = false;
|
||||||
|
await page.goto(`http://127.0.0.1:${server.PORT}/foo.html`).catch(e => {
|
||||||
|
failed = true;
|
||||||
|
});
|
||||||
|
expect(failed).toBe(true);
|
||||||
|
expect(reachedOriginalTarget).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue