chore: support http protocol with browserType.connect() (#17483)
This commit is contained in:
parent
b5643d0aab
commit
6b4afbb8df
|
|
@ -68,7 +68,14 @@ export class PlaywrightServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
async listen(port: number = 0): Promise<string> {
|
async listen(port: number = 0): Promise<string> {
|
||||||
const server = http.createServer((request, response) => {
|
const server = http.createServer((request: http.IncomingMessage, response: http.ServerResponse) => {
|
||||||
|
if (request.method === 'GET' && request.url === '/json') {
|
||||||
|
response.setHeader('Content-Type', 'application/json');
|
||||||
|
response.end(JSON.stringify({
|
||||||
|
wsEndpointPath: this._options.path,
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
response.end('Running');
|
response.end('Running');
|
||||||
});
|
});
|
||||||
server.on('error', error => debugLog(error));
|
server.on('error', error => debugLog(error));
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,12 @@ import { getUserAgent } from '../../common/userAgent';
|
||||||
import * as socks from '../../common/socksProxy';
|
import * as socks from '../../common/socksProxy';
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
import { ProgressController } from '../progress';
|
import { ProgressController } from '../progress';
|
||||||
|
import type { Progress } from '../progress';
|
||||||
import { WebSocketTransport } from '../transport';
|
import { WebSocketTransport } from '../transport';
|
||||||
import { findValidator, ValidationError, type ValidatorContext } from '../../protocol/validator';
|
import { findValidator, ValidationError, type ValidatorContext } from '../../protocol/validator';
|
||||||
|
import { fetchData } from '../../common/netUtils';
|
||||||
|
import type { HTTPRequestParams } from '../../common/netUtils';
|
||||||
|
import type http from 'http';
|
||||||
|
|
||||||
export class BrowserTypeDispatcher extends Dispatcher<BrowserType, channels.BrowserTypeChannel, RootDispatcher> implements channels.BrowserTypeChannel {
|
export class BrowserTypeDispatcher extends Dispatcher<BrowserType, channels.BrowserTypeChannel, RootDispatcher> implements channels.BrowserTypeChannel {
|
||||||
_type_BrowserType = true;
|
_type_BrowserType = true;
|
||||||
|
|
@ -62,7 +66,9 @@ export class BrowserTypeDispatcher extends Dispatcher<BrowserType, channels.Brow
|
||||||
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 paramsHeaders = Object.assign({ 'User-Agent': getUserAgent() }, params.headers || {});
|
||||||
const transport = await WebSocketTransport.connect(progress, params.wsEndpoint, paramsHeaders, true);
|
const wsEndpoint = await urlToWSEndpoint(progress, params.wsEndpoint);
|
||||||
|
|
||||||
|
const transport = await WebSocketTransport.connect(progress, wsEndpoint, paramsHeaders, true);
|
||||||
let socksInterceptor: SocksInterceptor | undefined;
|
let socksInterceptor: SocksInterceptor | undefined;
|
||||||
const pipe = new JsonPipeDispatcher(this);
|
const pipe = new JsonPipeDispatcher(this);
|
||||||
transport.onmessage = json => {
|
transport.onmessage = json => {
|
||||||
|
|
@ -154,3 +160,34 @@ class SocksInterceptor {
|
||||||
function tChannelForSocks(names: '*' | string[], arg: any, path: string, context: ValidatorContext) {
|
function tChannelForSocks(names: '*' | string[], arg: any, path: string, context: ValidatorContext) {
|
||||||
throw new ValidationError(`${path}: channels are not expected in SocksSupport`);
|
throw new ValidationError(`${path}: channels are not expected in SocksSupport`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function urlToWSEndpoint(progress: Progress, endpointURL: string): Promise<string> {
|
||||||
|
if (endpointURL.startsWith('ws'))
|
||||||
|
return endpointURL;
|
||||||
|
|
||||||
|
progress.log(`<ws preparing> retrieving websocket url from ${endpointURL}`);
|
||||||
|
const fetchUrl = new URL(endpointURL);
|
||||||
|
if (!fetchUrl.pathname.endsWith('/'))
|
||||||
|
fetchUrl.pathname += '/';
|
||||||
|
fetchUrl.pathname += 'json';
|
||||||
|
const json = await fetchData({
|
||||||
|
url: fetchUrl.toString(),
|
||||||
|
method: 'GET',
|
||||||
|
timeout: progress.timeUntilDeadline(),
|
||||||
|
headers: { 'User-Agent': getUserAgent() },
|
||||||
|
}, async (params: HTTPRequestParams, response: http.IncomingMessage) => {
|
||||||
|
return new Error(`Unexpected status ${response.statusCode} when connecting to ${fetchUrl.toString()}.\n` +
|
||||||
|
`This does not look like a Playwright server, try connecting via ws://.`);
|
||||||
|
});
|
||||||
|
progress.throwIfAborted();
|
||||||
|
|
||||||
|
const wsUrl = new URL(endpointURL);
|
||||||
|
let wsEndpointPath = JSON.parse(json).wsEndpointPath;
|
||||||
|
if (wsEndpointPath.startsWith('/'))
|
||||||
|
wsEndpointPath = wsEndpointPath.substring(1);
|
||||||
|
if (!wsUrl.pathname.endsWith('/'))
|
||||||
|
wsUrl.pathname += '/';
|
||||||
|
wsUrl.pathname += wsEndpointPath;
|
||||||
|
wsUrl.protocol = wsUrl.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
|
return wsUrl.toString();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -645,3 +645,13 @@ test('should connect when launching', async ({ browserType, startRemoteServer, h
|
||||||
|
|
||||||
(browserType as any)._defaultConnectOptions = undefined;
|
(browserType as any)._defaultConnectOptions = undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should connect over http', async ({ browserType, startRemoteServer, mode }) => {
|
||||||
|
test.skip(mode !== 'default');
|
||||||
|
const remoteServer = await startRemoteServer();
|
||||||
|
|
||||||
|
const url = new URL(remoteServer.wsEndpoint());
|
||||||
|
const browser = await browserType.connect(`http://localhost:${url.port}`);
|
||||||
|
expect(browser.version()).toBeTruthy();
|
||||||
|
await browser.close();
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue