chore: explicit server mode control (#23215)

This commit is contained in:
Pavel Feldman 2023-05-23 10:56:37 -07:00 committed by GitHub
parent b814e8a5f1
commit 6cce93b697
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 29 additions and 40 deletions

View File

@ -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 });
const server = new PlaywrightServer({ mode: 'launchServer', path, maxConnections: 1, preLaunchedAndroidDevice: device });
const wsEndpoint = await server.listen(options.port);
// 3. Return the BrowserServer interface

View File

@ -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, preLaunchedBrowser: browser, preLaunchedSocksProxy: socksProxy });
const server = new PlaywrightServer({ mode: 'launchServer', path, maxConnections: Infinity, preLaunchedBrowser: browser, preLaunchedSocksProxy: socksProxy });
const wsEndpoint = await server.listen(options.port);
// 3. Return the BrowserServer interface

View File

@ -54,6 +54,7 @@ export function runDriver() {
export type RunServerOptions = {
port?: number,
path?: string,
extension?: boolean,
maxConnections?: number,
browserProxyMode?: 'client' | 'tether',
ownedByTetherClient?: boolean,
@ -64,8 +65,9 @@ export async function runServer(options: RunServerOptions) {
port,
path = '/',
maxConnections = Infinity,
extension,
} = options;
const server = new PlaywrightServer({ path, maxConnections });
const server = new PlaywrightServer({ mode: extension ? 'extension' : 'default', 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

View File

@ -246,11 +246,13 @@ program
.option('--port <port>', 'Server port')
.option('--path <path>', 'Endpoint Path', '/')
.option('--max-clients <maxClients>', 'Maximum clients')
.option('--mode <mode>', 'Server mode, either "default" or "extension"')
.action(function(options) {
runServer({
port: options.port ? +options.port : undefined,
path: options.path,
maxConnections: options.maxClients ? +options.maxClients : Infinity,
extension: options.mode === 'extension' || !!process.env.PW_EXTENSION_MODE,
}).catch(logErrorAndExit);
});

View File

@ -28,7 +28,7 @@ import { startProfiling, stopProfiling } from '../utils';
import { monotonicTime } from '../utils';
import { debugLogger } from '../common/debugLogger';
export type ClientType = 'controller' | 'playwright' | 'launch-browser' | 'reuse-browser' | 'pre-launched-browser-or-android';
export type ClientType = 'controller' | 'launch-browser' | 'reuse-browser' | 'pre-launched-browser-or-android';
type Options = {
socksProxyPattern: string | undefined,
@ -102,24 +102,10 @@ export class PlaywrightConnection {
return this._preLaunched.browser ? await this._initPreLaunchedBrowserMode(scope) : await this._initPreLaunchedAndroidMode(scope);
if (clientType === 'launch-browser')
return await this._initLaunchBrowserMode(scope);
if (clientType === 'playwright')
return await this._initPlaywrightConnectMode(scope);
throw new Error('Unsupported client type: ' + clientType);
});
}
private async _initPlaywrightConnectMode(scope: RootDispatcher) {
debugLogger.log('server', `[${this._id}] engaged playwright.connect mode`);
const playwright = createPlaywright('javascript');
// Close all launched browsers on disconnect.
this._cleanups.push(async () => {
await Promise.all(playwright.allBrowsers().map(browser => browser.close()));
});
const ownedSocksProxy = await this._createOwnedSocksProxy(playwright);
return new PlaywrightDispatcher(scope, playwright, ownedSocksProxy);
}
private async _initLaunchBrowserMode(scope: RootDispatcher) {
debugLogger.log('server', `[${this._id}] engaged launch mode for "${this._options.browserName}"`);
const playwright = createPlaywright('javascript');

View File

@ -34,6 +34,7 @@ const kConnectionSymbol = Symbol('kConnection');
type ServerOptions = {
path: string;
maxConnections: number;
mode: 'default' | 'launchServer' | 'extension';
preLaunchedBrowser?: Browser;
preLaunchedAndroidDevice?: AndroidDevice;
preLaunchedSocksProxy?: SocksProxy;
@ -97,38 +98,35 @@ export class PlaywrightServer {
const proxyValue = url.searchParams.get('proxy') || (Array.isArray(proxyHeader) ? proxyHeader[0] : proxyHeader);
const launchOptionsHeader = request.headers['x-playwright-launch-options'] || '';
const launchOptionsHeaderValue = Array.isArray(launchOptionsHeader) ? launchOptionsHeader[0] : launchOptionsHeader;
const launchOptionsParam = url.searchParams.get('launch-options');
let launchOptions: LaunchOptions = {};
try {
launchOptions = JSON.parse(Array.isArray(launchOptionsHeader) ? launchOptionsHeader[0] : launchOptionsHeader);
launchOptions = JSON.parse(launchOptionsParam || launchOptionsHeaderValue);
} catch (e) {
}
const id = String(++lastConnectionId);
debugLogger.log('server', `[${id}] serving connection: ${request.url}`);
const isDebugControllerClient = !!request.headers['x-playwright-debug-controller'];
const shouldReuseBrowser = !!request.headers['x-playwright-reuse-context'];
// If we started in the legacy reuse-browser mode, create this._preLaunchedPlaywright.
// If we get a debug-controller request, create this._preLaunchedPlaywright.
if (isDebugControllerClient || shouldReuseBrowser) {
// Instantiate playwright for the extension modes.
const isExtension = this._options.mode === 'extension';
if (isExtension) {
if (!this._preLaunchedPlaywright)
this._preLaunchedPlaywright = createPlaywright('javascript');
}
let clientType: ClientType = 'playwright';
let clientType: ClientType = 'launch-browser';
let semaphore: Semaphore = browserSemaphore;
if (isDebugControllerClient) {
if (isExtension && url.searchParams.has('debug-controller')) {
clientType = 'controller';
semaphore = controllerSemaphore;
} else if (shouldReuseBrowser) {
} else if (isExtension) {
clientType = 'reuse-browser';
semaphore = reuseBrowserSemaphore;
} else if (this._options.preLaunchedBrowser || this._options.preLaunchedAndroidDevice) {
} else if (this._options.mode === 'launchServer') {
clientType = 'pre-launched-browser-or-android';
semaphore = browserSemaphore;
} else if (browserName) {
clientType = 'launch-browser';
semaphore = browserSemaphore;
}
const connection = new PlaywrightConnection(

View File

@ -409,7 +409,7 @@ ${colors.dim('Waiting for file changes. Press')} ${colors.bold('enter')} ${color
async function toggleShowBrowser(config: FullConfigInternal, originalWorkers: number) {
if (!showBrowserServer) {
config.config.workers = 1;
showBrowserServer = new PlaywrightServer({ path: '/' + createGuid(), maxConnections: 1 });
showBrowserServer = new PlaywrightServer({ mode: 'extension', path: '/' + createGuid(), maxConnections: 1 });
const wsEndpoint = await showBrowserServer.listen();
process.env.PW_TEST_REUSE_CONTEXT = '1';
process.env.PW_TEST_CONNECT_WS_ENDPOINT = wsEndpoint;

View File

@ -132,7 +132,7 @@ const test = baseTest.extend<BrowserTestTestFixtures, BrowserTestWorkerFixtures>
server = remoteServer;
} else {
const runServer = new RunServer();
await runServer._start(childProcess);
await runServer.start(childProcess);
server = runServer;
}
return server;

View File

@ -115,9 +115,7 @@ export class Backend extends EventEmitter {
}
async connect(wsEndpoint: string) {
this._transport = await WebSocketTransport.connect(wsEndpoint, {
'x-playwright-debug-controller': 'true'
});
this._transport = await WebSocketTransport.connect(wsEndpoint + '?debug-controller');
this._transport.onmessage = (message: any) => {
if (!message.id) {
this.emit(message.method, message.params);

View File

@ -27,9 +27,12 @@ export class RunServer implements PlaywrightServer {
private _process: TestChildProcess;
_wsEndpoint: string;
async _start(childProcess: CommonFixtures['childProcess']) {
async start(childProcess: CommonFixtures['childProcess'], mode?: 'extension' | 'default') {
const command = ['node', path.join(__dirname, '..', '..', 'packages', 'playwright-core', 'lib', 'cli', 'cli.js'), 'run-server'];
if (mode === 'extension')
command.push('--mode=extension');
this._process = childProcess({
command: ['node', path.join(__dirname, '..', '..', 'packages', 'playwright-core', 'lib', 'cli', 'cli.js'), 'run-server'],
command,
env: {
...process.env,
PWTEST_UNDER_TEST: '1',

View File

@ -31,7 +31,7 @@ type Fixtures = {
const test = baseTest.extend<Fixtures>({
wsEndpoint: async ({ }, use) => {
process.env.PW_DEBUG_CONTROLLER_HEADLESS = '1';
const server = new PlaywrightServer({ path: '/' + createGuid(), maxConnections: Number.MAX_VALUE, enableSocksProxy: false });
const server = new PlaywrightServer({ mode: 'extension', path: '/' + createGuid(), maxConnections: Number.MAX_VALUE, enableSocksProxy: false });
const wsEndpoint = await server.listen();
await use(wsEndpoint);
await server.close();

View File

@ -23,7 +23,7 @@ const test = baseTest.extend<{ runServer: () => Promise<PlaywrightServer> }>({
let server: PlaywrightServer | undefined;
await use(async () => {
const runServer = new RunServer();
await runServer._start(childProcess);
await runServer.start(childProcess, 'extension');
server = runServer;
return server;
});