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 { AndroidDevice } from '../server/android/android';
|
||||||
import type { SocksProxy } from '../common/socksProxy';
|
import type { SocksProxy } from '../common/socksProxy';
|
||||||
import { debugLogger } from '../common/debugLogger';
|
import { debugLogger } from '../common/debugLogger';
|
||||||
import { createHttpServer } from '../utils';
|
import { createHttpServer, userAgentVersionMatchesErrorMessage } from '../utils';
|
||||||
import { perMessageDeflate } from '../server/transport';
|
import { perMessageDeflate } from '../server/transport';
|
||||||
|
|
||||||
let lastConnectionId = 0;
|
let lastConnectionId = 0;
|
||||||
@ -45,6 +45,7 @@ type ServerOptions = {
|
|||||||
export class PlaywrightServer {
|
export class PlaywrightServer {
|
||||||
private _preLaunchedPlaywright: Playwright | undefined;
|
private _preLaunchedPlaywright: Playwright | undefined;
|
||||||
private _wsServer: WebSocketServer | undefined;
|
private _wsServer: WebSocketServer | undefined;
|
||||||
|
private _server: http.Server | undefined;
|
||||||
private _options: ServerOptions;
|
private _options: ServerOptions;
|
||||||
|
|
||||||
constructor(options: ServerOptions) {
|
constructor(options: ServerOptions) {
|
||||||
@ -69,6 +70,7 @@ export class PlaywrightServer {
|
|||||||
response.end('Running');
|
response.end('Running');
|
||||||
});
|
});
|
||||||
server.on('error', error => debugLogger.log('server', String(error)));
|
server.on('error', error => debugLogger.log('server', String(error)));
|
||||||
|
this._server = server;
|
||||||
|
|
||||||
const wsEndpoint = await new Promise<string>((resolve, reject) => {
|
const wsEndpoint = await new Promise<string>((resolve, reject) => {
|
||||||
server.listen(port, () => {
|
server.listen(port, () => {
|
||||||
@ -84,8 +86,7 @@ export class PlaywrightServer {
|
|||||||
|
|
||||||
debugLogger.log('server', 'Listening at ' + wsEndpoint);
|
debugLogger.log('server', 'Listening at ' + wsEndpoint);
|
||||||
this._wsServer = new wsServer({
|
this._wsServer = new wsServer({
|
||||||
server,
|
noServer: true,
|
||||||
path: this._options.path,
|
|
||||||
perMessageDeflate,
|
perMessageDeflate,
|
||||||
});
|
});
|
||||||
const browserSemaphore = new Semaphore(this._options.maxConnections);
|
const browserSemaphore = new Semaphore(this._options.maxConnections);
|
||||||
@ -96,6 +97,23 @@ export class PlaywrightServer {
|
|||||||
headers.push(process.env.PWTEST_SERVER_WS_HEADERS!);
|
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) => {
|
this._wsServer.on('connection', (ws, request) => {
|
||||||
debugLogger.log('server', 'Connected client ws.extension=' + ws.extensions);
|
debugLogger.log('server', 'Connected client ws.extension=' + ws.extensions);
|
||||||
const url = new URL('http://localhost' + (request.url || ''));
|
const url = new URL('http://localhost' + (request.url || ''));
|
||||||
@ -171,8 +189,10 @@ export class PlaywrightServer {
|
|||||||
}));
|
}));
|
||||||
await waitForClose;
|
await waitForClose;
|
||||||
debugLogger.log('server', 'closing http server');
|
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._wsServer = undefined;
|
||||||
|
this._server = undefined;
|
||||||
debugLogger.log('server', 'closed server');
|
debugLogger.log('server', 'closed server');
|
||||||
|
|
||||||
debugLogger.log('server', 'closing browsers');
|
debugLogger.log('server', 'closing browsers');
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
import { execSync } from 'child_process';
|
import { execSync } from 'child_process';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import { getLinuxDistributionInfoSync } from '../utils/linuxUtils';
|
import { getLinuxDistributionInfoSync } from '../utils/linuxUtils';
|
||||||
|
import { wrapInASCIIBox } from './ascii';
|
||||||
|
|
||||||
let cachedUserAgent: string | undefined;
|
let cachedUserAgent: string | undefined;
|
||||||
|
|
||||||
@ -76,8 +77,31 @@ export function getEmbedderName(): { embedderName: string, embedderVersion: stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getPlaywrightVersion(majorMinorOnly = false): string {
|
export function getPlaywrightVersion(majorMinorOnly = false): string {
|
||||||
const packageJson = require('./../../package.json');
|
const version = process.env.PW_VERSION_OVERRIDE || require('./../../package.json').version;
|
||||||
if (process.env.PW_VERSION_OVERRIDE)
|
return majorMinorOnly ? version.split('.').slice(0, 2).join('.') : version;
|
||||||
return process.env.PW_VERSION_OVERRIDE;
|
}
|
||||||
return majorMinorOnly ? packageJson.version.split('.').slice(0, 2).join('.') : packageJson.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;
|
private _process: TestChildProcess;
|
||||||
_wsEndpoint: string;
|
_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'];
|
const command = ['node', path.join(__dirname, '..', '..', 'packages', 'playwright-core', 'lib', 'cli', 'cli.js'), 'run-server'];
|
||||||
if (mode === 'extension')
|
if (mode === 'extension')
|
||||||
command.push('--mode=extension');
|
command.push('--mode=extension');
|
||||||
@ -36,6 +36,7 @@ export class RunServer implements PlaywrightServer {
|
|||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
PWTEST_UNDER_TEST: '1',
|
PWTEST_UNDER_TEST: '1',
|
||||||
|
...env,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ import os from 'os';
|
|||||||
import type http from 'http';
|
import type http from 'http';
|
||||||
import type net from 'net';
|
import type net from 'net';
|
||||||
import * as path from 'path';
|
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 WebSocket from 'ws';
|
||||||
import { expect, playwrightTest } from '../config/browserTest';
|
import { expect, playwrightTest } from '../config/browserTest';
|
||||||
import { parseTrace, suppressCertificateWarning } from '../config/utils';
|
import { parseTrace, suppressCertificateWarning } from '../config/utils';
|
||||||
@ -28,6 +28,7 @@ import formidable from 'formidable';
|
|||||||
import type { Browser, ConnectOptions } from 'playwright-core';
|
import type { Browser, ConnectOptions } from 'playwright-core';
|
||||||
import { createHttpServer } from '../../packages/playwright-core/lib/utils/network';
|
import { createHttpServer } from '../../packages/playwright-core/lib/utils/network';
|
||||||
import { kTargetClosedErrorMessage } from '../config/errors';
|
import { kTargetClosedErrorMessage } from '../config/errors';
|
||||||
|
import { RunServer } from '../config/remoteServer';
|
||||||
|
|
||||||
type ExtraFixtures = {
|
type ExtraFixtures = {
|
||||||
connect: (wsEndpoint: string, options?: ConnectOptions, redirectPortForTest?: number) => Promise<Browser>,
|
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