mirror of
https://github.com/microsoft/playwright.git
synced 2024-10-27 05:46:28 +03:00
chore: remove separate network tethering client connection (#20267)
This commit is contained in:
parent
b700c08dc5
commit
71798d658f
@ -38,7 +38,7 @@ PW_UUID=$(cat /proc/sys/kernel/random/uuid)
|
||||
# Make sure to re-start playwright server if something goes wrong.
|
||||
# The approach taken from: https://stackoverflow.com/a/697064/314883
|
||||
|
||||
until npx playwright run-server --port=5200 --path=/$PW_UUID --proxy-mode=tether; do
|
||||
until npx playwright run-server --port=5200 --path=/$PW_UUID; do
|
||||
echo "Server crashed with exit code $?. Respawning.." >&2
|
||||
sleep 1
|
||||
done
|
||||
|
@ -49,7 +49,7 @@ export class AndroidServerLauncherImpl {
|
||||
const path = options.wsPath ? (options.wsPath.startsWith('/') ? options.wsPath : `/${options.wsPath}`) : `/${createGuid()}`;
|
||||
|
||||
// 2. Start the server
|
||||
const server = new PlaywrightServer({ path, maxConnections: 1, preLaunchedAndroidDevice: device, browserProxyMode: 'client' });
|
||||
const server = new PlaywrightServer({ path, maxConnections: 1, preLaunchedAndroidDevice: device });
|
||||
const wsEndpoint = await server.listen(options.port);
|
||||
|
||||
// 3. Return the BrowserServer interface
|
||||
|
@ -57,7 +57,7 @@ export class BrowserServerLauncherImpl implements BrowserServerLauncher {
|
||||
const path = options.wsPath ? (options.wsPath.startsWith('/') ? options.wsPath : `/${options.wsPath}`) : `/${createGuid()}`;
|
||||
|
||||
// 2. Start the server
|
||||
const server = new PlaywrightServer({ path, maxConnections: Infinity, browserProxyMode: 'client', preLaunchedBrowser: browser, preLaunchedSocksProxy: socksProxy });
|
||||
const server = new PlaywrightServer({ path, maxConnections: Infinity, preLaunchedBrowser: browser, preLaunchedSocksProxy: socksProxy });
|
||||
const wsEndpoint = await server.listen(options.port);
|
||||
|
||||
// 3. Return the BrowserServer interface
|
||||
|
@ -270,14 +270,11 @@ program
|
||||
.option('--port <port>', 'Server port')
|
||||
.option('--path <path>', 'Endpoint Path', '/')
|
||||
.option('--max-clients <maxClients>', 'Maximum clients')
|
||||
.option('--proxy-mode <mode>', 'Either `client` or `tether`. Defaults to `client`.', 'client')
|
||||
.action(function(options) {
|
||||
runServer({
|
||||
port: options.port ? +options.port : undefined,
|
||||
path: options.path,
|
||||
maxConnections: options.maxClients ? +options.maxClients : Infinity,
|
||||
browserProxyMode: options.proxyMode,
|
||||
ownedByTetherClient: !!process.env.PW_OWNED_BY_TETHER_CLIENT,
|
||||
}).catch(logErrorAndExit);
|
||||
});
|
||||
|
||||
|
@ -59,10 +59,8 @@ export async function runServer(options: RunServerOptions) {
|
||||
port,
|
||||
path = '/',
|
||||
maxConnections = Infinity,
|
||||
browserProxyMode = 'client',
|
||||
ownedByTetherClient = false,
|
||||
} = options;
|
||||
const server = new PlaywrightServer({ path, maxConnections, browserProxyMode, ownedByTetherClient });
|
||||
const server = new PlaywrightServer({ path, maxConnections });
|
||||
const wsEndpoint = await server.listen(port);
|
||||
process.on('exit', () => server.close().catch(console.error));
|
||||
console.log('Listening on ' + wsEndpoint); // eslint-disable-line no-console
|
||||
|
@ -223,7 +223,6 @@ export async function ensurePlaywrightContainerOrDie(port: number): Promise<Cont
|
||||
}
|
||||
|
||||
const env: Record<string, string | undefined> = {
|
||||
PW_OWNED_BY_TETHER_CLIENT: '1',
|
||||
DEBUG: process.env.DEBUG,
|
||||
};
|
||||
for (const [key, value] of Object.entries(process.env)) {
|
||||
|
@ -25,7 +25,7 @@ import type { LaunchOptions } from '../server/types';
|
||||
import { AndroidDevice } from '../server/android/android';
|
||||
import { DebugControllerDispatcher } from '../server/dispatchers/debugControllerDispatcher';
|
||||
|
||||
export type ClientType = 'controller' | 'playwright' | 'launch-browser' | 'reuse-browser' | 'pre-launched-browser-or-android' | 'network-tethering';
|
||||
export type ClientType = 'controller' | 'playwright' | 'launch-browser' | 'reuse-browser' | 'pre-launched-browser-or-android';
|
||||
|
||||
type Options = {
|
||||
socksProxyPattern: string | undefined,
|
||||
@ -37,8 +37,7 @@ type PreLaunched = {
|
||||
playwright?: Playwright | undefined;
|
||||
browser?: Browser | undefined;
|
||||
androidDevice?: AndroidDevice | undefined;
|
||||
ownedSocksProxy?: SocksProxy | undefined;
|
||||
sharedSocksProxy?: SocksProxy | undefined;
|
||||
socksProxy?: SocksProxy | undefined;
|
||||
};
|
||||
|
||||
export class PlaywrightConnection {
|
||||
@ -91,20 +90,10 @@ export class PlaywrightConnection {
|
||||
return await this._initLaunchBrowserMode(scope);
|
||||
if (clientType === 'playwright')
|
||||
return await this._initPlaywrightConnectMode(scope);
|
||||
if (clientType === 'network-tethering')
|
||||
return await this._initPlaywrightTetheringMode(scope);
|
||||
throw new Error('Unsupported client type: ' + clientType);
|
||||
});
|
||||
}
|
||||
|
||||
private async _initPlaywrightTetheringMode(scope: RootDispatcher) {
|
||||
this._debugLog(`engaged playwright.tethering mode`);
|
||||
const playwright = createPlaywright('javascript');
|
||||
this._preLaunched.sharedSocksProxy?.setPattern(this._options.socksProxyPattern);
|
||||
// Tethering client owns the shared socks proxy.
|
||||
return new PlaywrightDispatcher(scope, playwright, this._preLaunched.sharedSocksProxy);
|
||||
}
|
||||
|
||||
private async _initPlaywrightConnectMode(scope: RootDispatcher) {
|
||||
this._debugLog(`engaged playwright.connect mode`);
|
||||
const playwright = createPlaywright('javascript');
|
||||
@ -113,14 +102,7 @@ export class PlaywrightConnection {
|
||||
await Promise.all(playwright.allBrowsers().map(browser => browser.close()));
|
||||
});
|
||||
|
||||
let ownedSocksProxy: SocksProxy | undefined;
|
||||
if (this._preLaunched.sharedSocksProxy) {
|
||||
// Note: tethering client configures the pattern, and connected client's pattern is ignored.
|
||||
playwright.options.socksProxyPort = this._preLaunched.sharedSocksProxy.port();
|
||||
this._debugLog(`using shared socks proxy on port ${playwright.options.socksProxyPort}`);
|
||||
} else {
|
||||
ownedSocksProxy = await this._createOwnedSocksProxy(playwright);
|
||||
}
|
||||
const ownedSocksProxy = await this._createOwnedSocksProxy(playwright);
|
||||
return new PlaywrightDispatcher(scope, playwright, ownedSocksProxy);
|
||||
}
|
||||
|
||||
@ -128,15 +110,7 @@ export class PlaywrightConnection {
|
||||
this._debugLog(`engaged launch mode for "${this._options.browserName}"`);
|
||||
const playwright = createPlaywright('javascript');
|
||||
|
||||
let ownedSocksProxy: SocksProxy | undefined;
|
||||
if (this._preLaunched.sharedSocksProxy) {
|
||||
// Note: tethering client configures the pattern, and connected client's pattern is ignored.
|
||||
playwright.options.socksProxyPort = this._preLaunched.sharedSocksProxy.port();
|
||||
this._debugLog(`using shared socks proxy on port ${playwright.options.socksProxyPort}`);
|
||||
} else {
|
||||
ownedSocksProxy = await this._createOwnedSocksProxy(playwright);
|
||||
}
|
||||
|
||||
const ownedSocksProxy = await this._createOwnedSocksProxy(playwright);
|
||||
const browser = await playwright[this._options.browserName as 'chromium'].launch(serverSideCallMetadata(), this._options.launchOptions);
|
||||
|
||||
this._cleanups.push(async () => {
|
||||
@ -156,8 +130,7 @@ export class PlaywrightConnection {
|
||||
const playwright = this._preLaunched.playwright!;
|
||||
|
||||
// Note: connected client owns the socks proxy and configures the pattern.
|
||||
playwright.options.socksProxyPort = this._preLaunched.ownedSocksProxy?.port();
|
||||
this._preLaunched.ownedSocksProxy?.setPattern(this._options.socksProxyPattern);
|
||||
this._preLaunched.socksProxy?.setPattern(this._options.socksProxyPattern);
|
||||
|
||||
const browser = this._preLaunched.browser!;
|
||||
browser.on(Browser.Events.Disconnected, () => {
|
||||
@ -165,7 +138,7 @@ export class PlaywrightConnection {
|
||||
this.close({ code: 1001, reason: 'Browser closed' });
|
||||
});
|
||||
|
||||
const playwrightDispatcher = new PlaywrightDispatcher(scope, playwright, this._preLaunched.ownedSocksProxy, browser);
|
||||
const playwrightDispatcher = new PlaywrightDispatcher(scope, playwright, this._preLaunched.socksProxy, browser);
|
||||
// In pre-launched mode, keep only the pre-launched browser.
|
||||
for (const b of playwright.allBrowsers()) {
|
||||
if (b !== browser)
|
||||
@ -196,13 +169,12 @@ export class PlaywrightConnection {
|
||||
}
|
||||
|
||||
private async _initReuseBrowsersMode(scope: RootDispatcher) {
|
||||
// Note: reuse browser mode does not support socks proxy, because
|
||||
// clients come and go, while the browser stays the same.
|
||||
|
||||
this._debugLog(`engaged reuse browsers mode for ${this._options.browserName}`);
|
||||
const playwright = this._preLaunched.playwright!;
|
||||
|
||||
// Note: connected client owns the socks proxy and configures the pattern.
|
||||
playwright.options.socksProxyPort = this._preLaunched.sharedSocksProxy?.port();
|
||||
this._debugLog(`using shared socks proxy on port ${playwright.options.socksProxyPort}`);
|
||||
|
||||
const requestedOptions = launchOptionsHash(this._options.launchOptions);
|
||||
let browser = playwright.allBrowsers().find(b => {
|
||||
if (b.options.name !== this._options.browserName)
|
||||
|
@ -25,7 +25,7 @@ import type { ClientType } from './playwrightConnection';
|
||||
import type { LaunchOptions } from '../server/types';
|
||||
import { ManualPromise } from '../utils/manualPromise';
|
||||
import type { AndroidDevice } from '../server/android/android';
|
||||
import { SocksProxy } from '../common/socksProxy';
|
||||
import { type SocksProxy } from '../common/socksProxy';
|
||||
|
||||
const debugLog = debug('pw:server');
|
||||
|
||||
@ -43,16 +43,12 @@ type ServerOptions = {
|
||||
preLaunchedBrowser?: Browser;
|
||||
preLaunchedAndroidDevice?: AndroidDevice;
|
||||
preLaunchedSocksProxy?: SocksProxy;
|
||||
browserProxyMode: 'client' | 'tether';
|
||||
ownedByTetherClient?: boolean;
|
||||
};
|
||||
|
||||
export class PlaywrightServer {
|
||||
private _preLaunchedPlaywright: Playwright | undefined;
|
||||
private _wsServer: WebSocketServer | undefined;
|
||||
private _networkTetheringSocksProxy: SocksProxy | undefined;
|
||||
private _options: ServerOptions;
|
||||
private _networkTetheringClientTimeout: NodeJS.Timeout | undefined;
|
||||
|
||||
constructor(options: ServerOptions) {
|
||||
this._options = options;
|
||||
@ -86,24 +82,12 @@ export class PlaywrightServer {
|
||||
resolve(wsEndpoint);
|
||||
}).on('error', reject);
|
||||
});
|
||||
if (this._options.browserProxyMode === 'tether') {
|
||||
this._networkTetheringSocksProxy = new SocksProxy();
|
||||
await this._networkTetheringSocksProxy.listen(0);
|
||||
debugLog('Launched tethering proxy at ' + this._networkTetheringSocksProxy.port());
|
||||
}
|
||||
|
||||
debugLog('Listening at ' + wsEndpoint);
|
||||
if (this._options.ownedByTetherClient) {
|
||||
this._networkTetheringClientTimeout = setTimeout(() => {
|
||||
this.close();
|
||||
}, 30_000);
|
||||
}
|
||||
|
||||
this._wsServer = new wsServer({ server, path: this._options.path });
|
||||
const browserSemaphore = new Semaphore(this._options.maxConnections);
|
||||
const controllerSemaphore = new Semaphore(1);
|
||||
const reuseBrowserSemaphore = new Semaphore(1);
|
||||
const networkTetheringSemaphore = new Semaphore(1);
|
||||
this._wsServer.on('connection', (ws, request) => {
|
||||
const url = new URL('http://localhost' + (request.url || ''));
|
||||
const browserHeader = request.headers['x-playwright-browser'];
|
||||
@ -121,11 +105,10 @@ export class PlaywrightServer {
|
||||
const log = newLogger();
|
||||
log(`serving connection: ${request.url}`);
|
||||
const isDebugControllerClient = !!request.headers['x-playwright-debug-controller'];
|
||||
const isNetworkTetheringClient = !!request.headers['x-playwright-network-tethering'];
|
||||
const shouldReuseBrowser = !!request.headers['x-playwright-reuse-context'];
|
||||
|
||||
// If we started in the legacy reuse-browser mode, create this._preLaunchedPlaywright.
|
||||
// If we get a reuse-controller request, create this._preLaunchedPlaywright.
|
||||
// If we get a debug-controller request, create this._preLaunchedPlaywright.
|
||||
if (isDebugControllerClient || shouldReuseBrowser) {
|
||||
if (!this._preLaunchedPlaywright)
|
||||
this._preLaunchedPlaywright = createPlaywright('javascript');
|
||||
@ -133,10 +116,7 @@ export class PlaywrightServer {
|
||||
|
||||
let clientType: ClientType = 'playwright';
|
||||
let semaphore: Semaphore = browserSemaphore;
|
||||
if (isNetworkTetheringClient) {
|
||||
clientType = 'network-tethering';
|
||||
semaphore = networkTetheringSemaphore;
|
||||
} else if (isDebugControllerClient) {
|
||||
if (isDebugControllerClient) {
|
||||
clientType = 'controller';
|
||||
semaphore = controllerSemaphore;
|
||||
} else if (shouldReuseBrowser) {
|
||||
@ -150,9 +130,6 @@ export class PlaywrightServer {
|
||||
semaphore = browserSemaphore;
|
||||
}
|
||||
|
||||
if (clientType === 'network-tethering' && this._options.ownedByTetherClient)
|
||||
clearTimeout(this._networkTetheringClientTimeout);
|
||||
|
||||
const connection = new PlaywrightConnection(
|
||||
semaphore.aquire(),
|
||||
clientType, ws,
|
||||
@ -161,14 +138,9 @@ export class PlaywrightServer {
|
||||
playwright: this._preLaunchedPlaywright,
|
||||
browser: this._options.preLaunchedBrowser,
|
||||
androidDevice: this._options.preLaunchedAndroidDevice,
|
||||
ownedSocksProxy: this._options.preLaunchedSocksProxy,
|
||||
sharedSocksProxy: this._networkTetheringSocksProxy,
|
||||
socksProxy: this._options.preLaunchedSocksProxy,
|
||||
},
|
||||
log, () => {
|
||||
semaphore.release();
|
||||
if (this._options.ownedByTetherClient && clientType === 'network-tethering')
|
||||
this.close();
|
||||
});
|
||||
log, () => semaphore.release());
|
||||
(ws as any)[kConnectionSymbol] = connection;
|
||||
});
|
||||
|
||||
@ -179,7 +151,6 @@ export class PlaywrightServer {
|
||||
const server = this._wsServer;
|
||||
if (!server)
|
||||
return;
|
||||
await this._networkTetheringSocksProxy?.close();
|
||||
debugLog('closing websocket server');
|
||||
const waitForClose = new Promise(f => server.close(f));
|
||||
// First disconnect all remaining clients.
|
||||
|
Loading…
Reference in New Issue
Block a user