mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat: check client version on the server (#27585)
This commit is contained in:
parent
f0167091e6
commit
fc32ca676b
@ -27,7 +27,7 @@ import { ManualPromise } from '../utils/manualPromise';
|
||||
import type { AndroidDevice } from '../server/android/android';
|
||||
import type { SocksProxy } from '../common/socksProxy';
|
||||
import { debugLogger } from '../common/debugLogger';
|
||||
import { createHttpServer } from '../utils';
|
||||
import { createHttpServer, userAgentVersionMatchesErrorMessage } from '../utils';
|
||||
import { perMessageDeflate } from '../server/transport';
|
||||
|
||||
let lastConnectionId = 0;
|
||||
@ -45,6 +45,7 @@ type ServerOptions = {
|
||||
export class PlaywrightServer {
|
||||
private _preLaunchedPlaywright: Playwright | undefined;
|
||||
private _wsServer: WebSocketServer | undefined;
|
||||
private _server: http.Server | undefined;
|
||||
private _options: ServerOptions;
|
||||
|
||||
constructor(options: ServerOptions) {
|
||||
@ -69,6 +70,7 @@ export class PlaywrightServer {
|
||||
response.end('Running');
|
||||
});
|
||||
server.on('error', error => debugLogger.log('server', String(error)));
|
||||
this._server = server;
|
||||
|
||||
const wsEndpoint = await new Promise<string>((resolve, reject) => {
|
||||
server.listen(port, () => {
|
||||
@ -84,8 +86,7 @@ export class PlaywrightServer {
|
||||
|
||||
debugLogger.log('server', 'Listening at ' + wsEndpoint);
|
||||
this._wsServer = new wsServer({
|
||||
server,
|
||||
path: this._options.path,
|
||||
noServer: true,
|
||||
perMessageDeflate,
|
||||
});
|
||||
const browserSemaphore = new Semaphore(this._options.maxConnections);
|
||||
@ -96,6 +97,23 @@ export class PlaywrightServer {
|
||||
headers.push(process.env.PWTEST_SERVER_WS_HEADERS!);
|
||||
});
|
||||
}
|
||||
server.on('upgrade', (request, socket, head) => {
|
||||
const pathname = new URL('http://localhost' + request.url!).pathname;
|
||||
if (pathname !== this._options.path) {
|
||||
socket.write(`HTTP/${request.httpVersion} 400 Bad Request\r\n\r\n`);
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
const uaError = userAgentVersionMatchesErrorMessage(request.headers['user-agent'] || '');
|
||||
if (uaError) {
|
||||
socket.write(`HTTP/${request.httpVersion} 428 Precondition Required\r\n\r\n${uaError}`);
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
this._wsServer?.handleUpgrade(request, socket, head, ws => this._wsServer?.emit('connection', ws, request));
|
||||
});
|
||||
this._wsServer.on('connection', (ws, request) => {
|
||||
debugLogger.log('server', 'Connected client ws.extension=' + ws.extensions);
|
||||
const url = new URL('http://localhost' + (request.url || ''));
|
||||
@ -171,8 +189,10 @@ export class PlaywrightServer {
|
||||
}));
|
||||
await waitForClose;
|
||||
debugLogger.log('server', 'closing http server');
|
||||
await new Promise(f => server.options.server!.close(f));
|
||||
if (this._server)
|
||||
await new Promise(f => this._server!.close(f));
|
||||
this._wsServer = undefined;
|
||||
this._server = undefined;
|
||||
debugLogger.log('server', 'closed server');
|
||||
|
||||
debugLogger.log('server', 'closing browsers');
|
||||
|
@ -17,6 +17,7 @@
|
||||
import { execSync } from 'child_process';
|
||||
import os from 'os';
|
||||
import { getLinuxDistributionInfoSync } from '../utils/linuxUtils';
|
||||
import { wrapInASCIIBox } from './ascii';
|
||||
|
||||
let cachedUserAgent: string | undefined;
|
||||
|
||||
@ -76,8 +77,31 @@ export function getEmbedderName(): { embedderName: string, embedderVersion: stri
|
||||
}
|
||||
|
||||
export function getPlaywrightVersion(majorMinorOnly = false): string {
|
||||
const packageJson = require('./../../package.json');
|
||||
if (process.env.PW_VERSION_OVERRIDE)
|
||||
return process.env.PW_VERSION_OVERRIDE;
|
||||
return majorMinorOnly ? packageJson.version.split('.').slice(0, 2).join('.') : packageJson.version;
|
||||
const version = process.env.PW_VERSION_OVERRIDE || require('./../../package.json').version;
|
||||
return majorMinorOnly ? version.split('.').slice(0, 2).join('.') : version;
|
||||
}
|
||||
|
||||
export function userAgentVersionMatchesErrorMessage(userAgent: string) {
|
||||
const match = userAgent.match(/^Playwright\/(\d+\.\d+\.\d+)/);
|
||||
if (!match) {
|
||||
// Cannot parse user agent - be lax.
|
||||
return;
|
||||
}
|
||||
const received = match[1].split('.').slice(0, 2).join('.');
|
||||
const expected = getPlaywrightVersion(true);
|
||||
if (received !== expected) {
|
||||
return wrapInASCIIBox([
|
||||
`Playwright version mismatch:`,
|
||||
` - server version: v${expected}`,
|
||||
` - client version: v${received}`,
|
||||
``,
|
||||
`If you are using VSCode extension, restart VSCode.`,
|
||||
``,
|
||||
`If you are connecting to a remote service,`,
|
||||
`keep your local Playwright version in sync`,
|
||||
`with the remote service version.`,
|
||||
``,
|
||||
`<3 Playwright Team`
|
||||
].join('\n'), 1);
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ export class RunServer implements PlaywrightServer {
|
||||
private _process: TestChildProcess;
|
||||
_wsEndpoint: string;
|
||||
|
||||
async start(childProcess: CommonFixtures['childProcess'], mode?: 'extension' | 'default') {
|
||||
async start(childProcess: CommonFixtures['childProcess'], mode?: 'extension' | 'default', env?: NodeJS.ProcessEnv) {
|
||||
const command = ['node', path.join(__dirname, '..', '..', 'packages', 'playwright-core', 'lib', 'cli', 'cli.js'), 'run-server'];
|
||||
if (mode === 'extension')
|
||||
command.push('--mode=extension');
|
||||
@ -36,6 +36,7 @@ export class RunServer implements PlaywrightServer {
|
||||
env: {
|
||||
...process.env,
|
||||
PWTEST_UNDER_TEST: '1',
|
||||
...env,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -20,7 +20,7 @@ import os from 'os';
|
||||
import type http from 'http';
|
||||
import type net from 'net';
|
||||
import * as path from 'path';
|
||||
import { getUserAgent } from '../../packages/playwright-core/lib/utils/userAgent';
|
||||
import { getUserAgent, getPlaywrightVersion } from '../../packages/playwright-core/lib/utils/userAgent';
|
||||
import WebSocket from 'ws';
|
||||
import { expect, playwrightTest } from '../config/browserTest';
|
||||
import { parseTrace, suppressCertificateWarning } from '../config/utils';
|
||||
@ -28,6 +28,7 @@ import formidable from 'formidable';
|
||||
import type { Browser, ConnectOptions } from 'playwright-core';
|
||||
import { createHttpServer } from '../../packages/playwright-core/lib/utils/network';
|
||||
import { kTargetClosedErrorMessage } from '../config/errors';
|
||||
import { RunServer } from '../config/remoteServer';
|
||||
|
||||
type ExtraFixtures = {
|
||||
connect: (wsEndpoint: string, options?: ConnectOptions, redirectPortForTest?: number) => Promise<Browser>,
|
||||
@ -914,3 +915,13 @@ test.describe('launchServer only', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('should refuse connecting when versions do not match', async ({ connect, childProcess }) => {
|
||||
const server = new RunServer();
|
||||
await server.start(childProcess, 'default', { PW_VERSION_OVERRIDE: '1.2.3' });
|
||||
const error = await connect(server.wsEndpoint()).catch(e => e);
|
||||
await server.close();
|
||||
expect(error.message).toContain('Playwright version mismatch');
|
||||
expect(error.message).toContain('server version: v1.2');
|
||||
expect(error.message).toContain('client version: v' + getPlaywrightVersion(true));
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user