mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-14 13:45:36 +03:00
chore: move connect utility into localUtils (#17590)
This commit is contained in:
parent
3409a37f77
commit
06e73b414f
@ -143,10 +143,11 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
|
||||
const deadline = params.timeout ? monotonicTime() + params.timeout : 0;
|
||||
let browser: Browser;
|
||||
const headers = { 'x-playwright-browser': this.name(), ...params.headers };
|
||||
const connectParams: channels.BrowserTypeConnectParams = { wsEndpoint, headers, slowMo: params.slowMo, timeout: params.timeout };
|
||||
const localUtils = this._connection.localUtils();
|
||||
const connectParams: channels.LocalUtilsConnectParams = { wsEndpoint, headers, slowMo: params.slowMo, timeout: params.timeout };
|
||||
if ((params as any).__testHookRedirectPortForwarding)
|
||||
connectParams.socksProxyRedirectPortForTest = (params as any).__testHookRedirectPortForwarding;
|
||||
const { pipe } = await this._channel.connect(connectParams);
|
||||
const { pipe } = await localUtils._channel.connect(connectParams);
|
||||
const closePipe = () => pipe.close().catch(() => {});
|
||||
const connection = new Connection(this._connection.localUtils());
|
||||
connection.markAsRemote();
|
||||
|
@ -245,6 +245,16 @@ scheme.LocalUtilsHarUnzipParams = tObject({
|
||||
harFile: tString,
|
||||
});
|
||||
scheme.LocalUtilsHarUnzipResult = tOptional(tObject({}));
|
||||
scheme.LocalUtilsConnectParams = tObject({
|
||||
wsEndpoint: tString,
|
||||
headers: tOptional(tAny),
|
||||
slowMo: tOptional(tNumber),
|
||||
timeout: tOptional(tNumber),
|
||||
socksProxyRedirectPortForTest: tOptional(tNumber),
|
||||
});
|
||||
scheme.LocalUtilsConnectResult = tObject({
|
||||
pipe: tChannel(['JsonPipe']),
|
||||
});
|
||||
scheme.RootInitializer = tOptional(tObject({}));
|
||||
scheme.RootInitializeParams = tObject({
|
||||
sdkLanguage: tString,
|
||||
@ -414,16 +424,6 @@ scheme.BrowserTypeInitializer = tObject({
|
||||
executablePath: tString,
|
||||
name: tString,
|
||||
});
|
||||
scheme.BrowserTypeConnectParams = tObject({
|
||||
wsEndpoint: tString,
|
||||
headers: tOptional(tAny),
|
||||
slowMo: tOptional(tNumber),
|
||||
timeout: tOptional(tNumber),
|
||||
socksProxyRedirectPortForTest: tOptional(tNumber),
|
||||
});
|
||||
scheme.BrowserTypeConnectResult = tObject({
|
||||
pipe: tChannel(['JsonPipe']),
|
||||
});
|
||||
scheme.BrowserTypeLaunchParams = tObject({
|
||||
channel: tOptional(tString),
|
||||
executablePath: tOptional(tString),
|
||||
|
@ -21,17 +21,6 @@ import type { RootDispatcher } from './dispatcher';
|
||||
import { Dispatcher } from './dispatcher';
|
||||
import { BrowserContextDispatcher } from './browserContextDispatcher';
|
||||
import type { CallMetadata } from '../instrumentation';
|
||||
import { JsonPipeDispatcher } from '../dispatchers/jsonPipeDispatcher';
|
||||
import { getUserAgent } from '../../common/userAgent';
|
||||
import * as socks from '../../common/socksProxy';
|
||||
import EventEmitter from 'events';
|
||||
import { ProgressController } from '../progress';
|
||||
import type { Progress } from '../progress';
|
||||
import { WebSocketTransport } from '../transport';
|
||||
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 {
|
||||
_type_BrowserType = true;
|
||||
@ -60,134 +49,4 @@ export class BrowserTypeDispatcher extends Dispatcher<BrowserType, channels.Brow
|
||||
defaultContext: browser._defaultContext ? new BrowserContextDispatcher(browserDispatcher, browser._defaultContext) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
async connect(params: channels.BrowserTypeConnectParams, metadata: CallMetadata): Promise<channels.BrowserTypeConnectResult> {
|
||||
const controller = new ProgressController(metadata, this._object);
|
||||
controller.setLogName('browser');
|
||||
return await controller.run(async progress => {
|
||||
const paramsHeaders = Object.assign({ 'User-Agent': getUserAgent() }, params.headers || {});
|
||||
const wsEndpoint = await urlToWSEndpoint(progress, params.wsEndpoint);
|
||||
|
||||
const transport = await WebSocketTransport.connect(progress, wsEndpoint, paramsHeaders, true);
|
||||
let socksInterceptor: SocksInterceptor | undefined;
|
||||
const pipe = new JsonPipeDispatcher(this);
|
||||
transport.onmessage = json => {
|
||||
if (json.method === '__create__' && json.params.type === 'SocksSupport')
|
||||
socksInterceptor = new SocksInterceptor(transport, params.socksProxyRedirectPortForTest, json.params.guid);
|
||||
if (socksInterceptor?.interceptMessage(json))
|
||||
return;
|
||||
const cb = () => {
|
||||
try {
|
||||
pipe.dispatch(json);
|
||||
} catch (e) {
|
||||
transport.close();
|
||||
}
|
||||
};
|
||||
if (params.slowMo)
|
||||
setTimeout(cb, params.slowMo);
|
||||
else
|
||||
cb();
|
||||
};
|
||||
pipe.on('message', message => {
|
||||
transport.send(message);
|
||||
});
|
||||
transport.onclose = () => {
|
||||
socksInterceptor?.cleanup();
|
||||
pipe.wasClosed();
|
||||
};
|
||||
pipe.on('close', () => transport.close());
|
||||
return { pipe };
|
||||
}, params.timeout || 0);
|
||||
}
|
||||
}
|
||||
|
||||
class SocksInterceptor {
|
||||
private _handler: socks.SocksProxyHandler;
|
||||
private _channel: channels.SocksSupportChannel & EventEmitter;
|
||||
private _socksSupportObjectGuid: string;
|
||||
private _ids = new Set<number>();
|
||||
|
||||
constructor(transport: WebSocketTransport, redirectPortForTest: number | undefined, socksSupportObjectGuid: string) {
|
||||
this._handler = new socks.SocksProxyHandler(redirectPortForTest);
|
||||
this._socksSupportObjectGuid = socksSupportObjectGuid;
|
||||
|
||||
let lastId = -1;
|
||||
this._channel = new Proxy(new EventEmitter(), {
|
||||
get: (obj: any, prop) => {
|
||||
if ((prop in obj) || obj[prop] !== undefined || typeof prop !== 'string')
|
||||
return obj[prop];
|
||||
return (params: any) => {
|
||||
try {
|
||||
const id = --lastId;
|
||||
this._ids.add(id);
|
||||
const validator = findValidator('SocksSupport', prop, 'Params');
|
||||
params = validator(params, '', { tChannelImpl: tChannelForSocks, binary: 'toBase64' });
|
||||
transport.send({ id, guid: socksSupportObjectGuid, method: prop, params, metadata: { stack: [], apiName: '', internal: true } } as any);
|
||||
} catch (e) {
|
||||
}
|
||||
};
|
||||
},
|
||||
}) as channels.SocksSupportChannel & EventEmitter;
|
||||
this._handler.on(socks.SocksProxyHandler.Events.SocksConnected, (payload: socks.SocksSocketConnectedPayload) => this._channel.socksConnected(payload));
|
||||
this._handler.on(socks.SocksProxyHandler.Events.SocksData, (payload: socks.SocksSocketDataPayload) => this._channel.socksData(payload));
|
||||
this._handler.on(socks.SocksProxyHandler.Events.SocksError, (payload: socks.SocksSocketErrorPayload) => this._channel.socksError(payload));
|
||||
this._handler.on(socks.SocksProxyHandler.Events.SocksFailed, (payload: socks.SocksSocketFailedPayload) => this._channel.socksFailed(payload));
|
||||
this._handler.on(socks.SocksProxyHandler.Events.SocksEnd, (payload: socks.SocksSocketEndPayload) => this._channel.socksEnd(payload));
|
||||
this._channel.on('socksRequested', payload => this._handler.socketRequested(payload));
|
||||
this._channel.on('socksClosed', payload => this._handler.socketClosed(payload));
|
||||
this._channel.on('socksData', payload => this._handler.sendSocketData(payload));
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
this._handler.cleanup();
|
||||
}
|
||||
|
||||
interceptMessage(message: any): boolean {
|
||||
if (this._ids.has(message.id)) {
|
||||
this._ids.delete(message.id);
|
||||
return true;
|
||||
}
|
||||
if (message.guid === this._socksSupportObjectGuid) {
|
||||
const validator = findValidator('SocksSupport', message.method, 'Event');
|
||||
const params = validator(message.params, '', { tChannelImpl: tChannelForSocks, binary: 'fromBase64' });
|
||||
this._channel.emit(message.method, params);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function tChannelForSocks(names: '*' | string[], arg: any, path: string, context: ValidatorContext) {
|
||||
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();
|
||||
}
|
||||
|
@ -18,11 +18,11 @@ import type * as channels from '@protocol/channels';
|
||||
import { Dispatcher } from './dispatcher';
|
||||
import { createGuid } from '../../utils';
|
||||
import { serializeError } from '../../protocol/serializers';
|
||||
import type { BrowserTypeDispatcher } from './browserTypeDispatcher';
|
||||
import type { LocalUtilsDispatcher } from './localUtilsDispatcher';
|
||||
|
||||
export class JsonPipeDispatcher extends Dispatcher<{ guid: string }, channels.JsonPipeChannel, BrowserTypeDispatcher> implements channels.JsonPipeChannel {
|
||||
export class JsonPipeDispatcher extends Dispatcher<{ guid: string }, channels.JsonPipeChannel, LocalUtilsDispatcher> implements channels.JsonPipeChannel {
|
||||
_type_JsonPipe = true;
|
||||
constructor(scope: BrowserTypeDispatcher) {
|
||||
constructor(scope: LocalUtilsDispatcher) {
|
||||
super(scope, { guid: 'jsonPipe@' + createGuid() }, 'JsonPipe', {});
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type EventEmitter from 'events';
|
||||
import EventEmitter from 'events';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import type * as channels from '@protocol/channels';
|
||||
@ -26,17 +26,32 @@ import { yazl, yauzl } from '../../zipBundle';
|
||||
import { ZipFile } from '../../utils/zipFile';
|
||||
import type * as har from '@trace/har';
|
||||
import type { HeadersArray } from '../types';
|
||||
import { JsonPipeDispatcher } from '../dispatchers/jsonPipeDispatcher';
|
||||
import * as socks from '../../common/socksProxy';
|
||||
import { WebSocketTransport } from '../transport';
|
||||
import type { CallMetadata } from '../instrumentation';
|
||||
import { getUserAgent } from '../../common/userAgent';
|
||||
import type { Progress } from '../progress';
|
||||
import { ProgressController } from '../progress';
|
||||
import { findValidator, ValidationError } from '../../protocol/validator';
|
||||
import type { ValidatorContext } from '../../protocol/validator';
|
||||
import { fetchData } from '../../common/netUtils';
|
||||
import type { HTTPRequestParams } from '../../common/netUtils';
|
||||
import type http from 'http';
|
||||
import type { Playwright } from '../playwright';
|
||||
import { SdkObject } from '../../server/instrumentation';
|
||||
|
||||
export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.LocalUtilsChannel, RootDispatcher> implements channels.LocalUtilsChannel {
|
||||
_type_LocalUtils: boolean;
|
||||
private _harBakends = new Map<string, HarBackend>();
|
||||
|
||||
constructor(scope: RootDispatcher) {
|
||||
super(scope, { guid: 'localUtils@' + createGuid() }, 'LocalUtils', {});
|
||||
constructor(scope: RootDispatcher, playwright: Playwright) {
|
||||
const localUtils = new SdkObject(playwright, 'localUtils', 'localUtils');
|
||||
super(scope, localUtils, 'LocalUtils', {});
|
||||
this._type_LocalUtils = true;
|
||||
}
|
||||
|
||||
async zip(params: channels.LocalUtilsZipParams, metadata?: channels.Metadata): Promise<void> {
|
||||
async zip(params: channels.LocalUtilsZipParams, metadata: CallMetadata): Promise<void> {
|
||||
const promise = new ManualPromise<void>();
|
||||
const zipFile = new yazl.ZipFile();
|
||||
(zipFile as any as EventEmitter).on('error', error => promise.reject(error));
|
||||
@ -91,7 +106,7 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.
|
||||
return promise;
|
||||
}
|
||||
|
||||
async harOpen(params: channels.LocalUtilsHarOpenParams, metadata?: channels.Metadata): Promise<channels.LocalUtilsHarOpenResult> {
|
||||
async harOpen(params: channels.LocalUtilsHarOpenParams, metadata: CallMetadata): Promise<channels.LocalUtilsHarOpenResult> {
|
||||
let harBackend: HarBackend;
|
||||
if (params.file.endsWith('.zip')) {
|
||||
const zipFile = new ZipFile(params.file);
|
||||
@ -110,14 +125,14 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.
|
||||
return { harId: harBackend.id };
|
||||
}
|
||||
|
||||
async harLookup(params: channels.LocalUtilsHarLookupParams, metadata?: channels.Metadata): Promise<channels.LocalUtilsHarLookupResult> {
|
||||
async harLookup(params: channels.LocalUtilsHarLookupParams, metadata: CallMetadata): Promise<channels.LocalUtilsHarLookupResult> {
|
||||
const harBackend = this._harBakends.get(params.harId);
|
||||
if (!harBackend)
|
||||
return { action: 'error', message: `Internal error: har was not opened` };
|
||||
return await harBackend.lookup(params.url, params.method, params.headers, params.postData, params.isNavigationRequest);
|
||||
}
|
||||
|
||||
async harClose(params: channels.LocalUtilsHarCloseParams, metadata?: channels.Metadata): Promise<void> {
|
||||
async harClose(params: channels.LocalUtilsHarCloseParams, metadata: CallMetadata): Promise<void> {
|
||||
const harBackend = this._harBakends.get(params.harId);
|
||||
if (harBackend) {
|
||||
this._harBakends.delete(harBackend.id);
|
||||
@ -125,7 +140,7 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.
|
||||
}
|
||||
}
|
||||
|
||||
async harUnzip(params: channels.LocalUtilsHarUnzipParams, metadata?: channels.Metadata): Promise<void> {
|
||||
async harUnzip(params: channels.LocalUtilsHarUnzipParams, metadata: CallMetadata): Promise<void> {
|
||||
const dir = path.dirname(params.zipFile);
|
||||
const zipFile = new ZipFile(params.zipFile);
|
||||
for (const entry of await zipFile.entries()) {
|
||||
@ -138,6 +153,46 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.
|
||||
zipFile.close();
|
||||
await fs.promises.unlink(params.zipFile);
|
||||
}
|
||||
|
||||
async connect(params: channels.LocalUtilsConnectParams, metadata: CallMetadata): Promise<channels.LocalUtilsConnectResult> {
|
||||
const controller = new ProgressController(metadata, this._object as SdkObject);
|
||||
controller.setLogName('browser');
|
||||
return await controller.run(async progress => {
|
||||
const paramsHeaders = Object.assign({ 'User-Agent': getUserAgent() }, params.headers || {});
|
||||
const wsEndpoint = await urlToWSEndpoint(progress, params.wsEndpoint);
|
||||
|
||||
const transport = await WebSocketTransport.connect(progress, wsEndpoint, paramsHeaders, true);
|
||||
let socksInterceptor: SocksInterceptor | undefined;
|
||||
const pipe = new JsonPipeDispatcher(this);
|
||||
transport.onmessage = json => {
|
||||
if (json.method === '__create__' && json.params.type === 'SocksSupport')
|
||||
socksInterceptor = new SocksInterceptor(transport, params.socksProxyRedirectPortForTest, json.params.guid);
|
||||
if (socksInterceptor?.interceptMessage(json))
|
||||
return;
|
||||
const cb = () => {
|
||||
try {
|
||||
pipe.dispatch(json);
|
||||
} catch (e) {
|
||||
transport.close();
|
||||
}
|
||||
};
|
||||
if (params.slowMo)
|
||||
setTimeout(cb, params.slowMo);
|
||||
else
|
||||
cb();
|
||||
};
|
||||
pipe.on('message', message => {
|
||||
transport.send(message);
|
||||
});
|
||||
transport.onclose = () => {
|
||||
socksInterceptor?.cleanup();
|
||||
pipe.wasClosed();
|
||||
};
|
||||
pipe.on('close', () => transport.close());
|
||||
return { pipe };
|
||||
}, params.timeout || 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const redirectStatus = [301, 302, 303, 307, 308];
|
||||
@ -262,6 +317,62 @@ class HarBackend {
|
||||
}
|
||||
}
|
||||
|
||||
class SocksInterceptor {
|
||||
private _handler: socks.SocksProxyHandler;
|
||||
private _channel: channels.SocksSupportChannel & EventEmitter;
|
||||
private _socksSupportObjectGuid: string;
|
||||
private _ids = new Set<number>();
|
||||
|
||||
constructor(transport: WebSocketTransport, redirectPortForTest: number | undefined, socksSupportObjectGuid: string) {
|
||||
this._handler = new socks.SocksProxyHandler(redirectPortForTest);
|
||||
this._socksSupportObjectGuid = socksSupportObjectGuid;
|
||||
|
||||
let lastId = -1;
|
||||
this._channel = new Proxy(new EventEmitter(), {
|
||||
get: (obj: any, prop) => {
|
||||
if ((prop in obj) || obj[prop] !== undefined || typeof prop !== 'string')
|
||||
return obj[prop];
|
||||
return (params: any) => {
|
||||
try {
|
||||
const id = --lastId;
|
||||
this._ids.add(id);
|
||||
const validator = findValidator('SocksSupport', prop, 'Params');
|
||||
params = validator(params, '', { tChannelImpl: tChannelForSocks, binary: 'toBase64' });
|
||||
transport.send({ id, guid: socksSupportObjectGuid, method: prop, params, metadata: { stack: [], apiName: '', internal: true } } as any);
|
||||
} catch (e) {
|
||||
}
|
||||
};
|
||||
},
|
||||
}) as channels.SocksSupportChannel & EventEmitter;
|
||||
this._handler.on(socks.SocksProxyHandler.Events.SocksConnected, (payload: socks.SocksSocketConnectedPayload) => this._channel.socksConnected(payload));
|
||||
this._handler.on(socks.SocksProxyHandler.Events.SocksData, (payload: socks.SocksSocketDataPayload) => this._channel.socksData(payload));
|
||||
this._handler.on(socks.SocksProxyHandler.Events.SocksError, (payload: socks.SocksSocketErrorPayload) => this._channel.socksError(payload));
|
||||
this._handler.on(socks.SocksProxyHandler.Events.SocksFailed, (payload: socks.SocksSocketFailedPayload) => this._channel.socksFailed(payload));
|
||||
this._handler.on(socks.SocksProxyHandler.Events.SocksEnd, (payload: socks.SocksSocketEndPayload) => this._channel.socksEnd(payload));
|
||||
this._channel.on('socksRequested', payload => this._handler.socketRequested(payload));
|
||||
this._channel.on('socksClosed', payload => this._handler.socketClosed(payload));
|
||||
this._channel.on('socksData', payload => this._handler.sendSocketData(payload));
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
this._handler.cleanup();
|
||||
}
|
||||
|
||||
interceptMessage(message: any): boolean {
|
||||
if (this._ids.has(message.id)) {
|
||||
this._ids.delete(message.id);
|
||||
return true;
|
||||
}
|
||||
if (message.guid === this._socksSupportObjectGuid) {
|
||||
const validator = findValidator('SocksSupport', message.method, 'Event');
|
||||
const params = validator(message.params, '', { tChannelImpl: tChannelForSocks, binary: 'fromBase64' });
|
||||
this._channel.emit(message.method, params);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function countMatchingHeaders(harHeaders: har.Header[], headers: HeadersArray): number {
|
||||
const set = new Set(headers.map(h => h.name.toLowerCase() + ':' + h.value));
|
||||
let matches = 0;
|
||||
@ -272,3 +383,37 @@ function countMatchingHeaders(harHeaders: har.Header[], headers: HeadersArray):
|
||||
return matches;
|
||||
}
|
||||
|
||||
function tChannelForSocks(names: '*' | string[], arg: any, path: string, context: ValidatorContext) {
|
||||
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();
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ export class PlaywrightDispatcher extends Dispatcher<Playwright, channels.Playwr
|
||||
webkit: new BrowserTypeDispatcher(scope, playwright.webkit),
|
||||
android: new AndroidDispatcher(scope, playwright.android),
|
||||
electron: new ElectronDispatcher(scope, playwright.electron),
|
||||
utils: new LocalUtilsDispatcher(scope),
|
||||
utils: new LocalUtilsDispatcher(scope, playwright),
|
||||
deviceDescriptors,
|
||||
selectors: new SelectorsDispatcher(scope, browserDispatcher?.selectors || playwright.selectors),
|
||||
preLaunchedBrowser: browserDispatcher,
|
||||
|
@ -43,7 +43,7 @@ export class SdkObject extends EventEmitter {
|
||||
attribution: Attribution;
|
||||
instrumentation: Instrumentation;
|
||||
|
||||
protected constructor(parent: SdkObject, guidPrefix?: string, guid?: string) {
|
||||
constructor(parent: SdkObject, guidPrefix?: string, guid?: string) {
|
||||
super();
|
||||
this.guid = guid || `${guidPrefix || ''}@${createGuid()}`;
|
||||
this.setMaxListeners(0);
|
||||
|
@ -389,6 +389,7 @@ export interface LocalUtilsChannel extends LocalUtilsEventTarget, Channel {
|
||||
harLookup(params: LocalUtilsHarLookupParams, metadata?: Metadata): Promise<LocalUtilsHarLookupResult>;
|
||||
harClose(params: LocalUtilsHarCloseParams, metadata?: Metadata): Promise<LocalUtilsHarCloseResult>;
|
||||
harUnzip(params: LocalUtilsHarUnzipParams, metadata?: Metadata): Promise<LocalUtilsHarUnzipResult>;
|
||||
connect(params: LocalUtilsConnectParams, metadata?: Metadata): Promise<LocalUtilsConnectResult>;
|
||||
}
|
||||
export type LocalUtilsZipParams = {
|
||||
zipFile: string,
|
||||
@ -442,6 +443,22 @@ export type LocalUtilsHarUnzipOptions = {
|
||||
|
||||
};
|
||||
export type LocalUtilsHarUnzipResult = void;
|
||||
export type LocalUtilsConnectParams = {
|
||||
wsEndpoint: string,
|
||||
headers?: any,
|
||||
slowMo?: number,
|
||||
timeout?: number,
|
||||
socksProxyRedirectPortForTest?: number,
|
||||
};
|
||||
export type LocalUtilsConnectOptions = {
|
||||
headers?: any,
|
||||
slowMo?: number,
|
||||
timeout?: number,
|
||||
socksProxyRedirectPortForTest?: number,
|
||||
};
|
||||
export type LocalUtilsConnectResult = {
|
||||
pipe: JsonPipeChannel,
|
||||
};
|
||||
|
||||
export interface LocalUtilsEvents {
|
||||
}
|
||||
@ -765,27 +782,10 @@ export interface BrowserTypeEventTarget {
|
||||
}
|
||||
export interface BrowserTypeChannel extends BrowserTypeEventTarget, Channel {
|
||||
_type_BrowserType: boolean;
|
||||
connect(params: BrowserTypeConnectParams, metadata?: Metadata): Promise<BrowserTypeConnectResult>;
|
||||
launch(params: BrowserTypeLaunchParams, metadata?: Metadata): Promise<BrowserTypeLaunchResult>;
|
||||
launchPersistentContext(params: BrowserTypeLaunchPersistentContextParams, metadata?: Metadata): Promise<BrowserTypeLaunchPersistentContextResult>;
|
||||
connectOverCDP(params: BrowserTypeConnectOverCDPParams, metadata?: Metadata): Promise<BrowserTypeConnectOverCDPResult>;
|
||||
}
|
||||
export type BrowserTypeConnectParams = {
|
||||
wsEndpoint: string,
|
||||
headers?: any,
|
||||
slowMo?: number,
|
||||
timeout?: number,
|
||||
socksProxyRedirectPortForTest?: number,
|
||||
};
|
||||
export type BrowserTypeConnectOptions = {
|
||||
headers?: any,
|
||||
slowMo?: number,
|
||||
timeout?: number,
|
||||
socksProxyRedirectPortForTest?: number,
|
||||
};
|
||||
export type BrowserTypeConnectResult = {
|
||||
pipe: JsonPipeChannel,
|
||||
};
|
||||
export type BrowserTypeLaunchParams = {
|
||||
channel?: string,
|
||||
executablePath?: string,
|
||||
|
@ -528,6 +528,16 @@ LocalUtils:
|
||||
zipFile: string
|
||||
harFile: string
|
||||
|
||||
connect:
|
||||
parameters:
|
||||
wsEndpoint: string
|
||||
headers: json?
|
||||
slowMo: number?
|
||||
timeout: number?
|
||||
socksProxyRedirectPortForTest: number?
|
||||
returns:
|
||||
pipe: JsonPipe
|
||||
|
||||
Root:
|
||||
type: interface
|
||||
|
||||
@ -772,16 +782,6 @@ BrowserType:
|
||||
|
||||
commands:
|
||||
|
||||
connect:
|
||||
parameters:
|
||||
wsEndpoint: string
|
||||
headers: json?
|
||||
slowMo: number?
|
||||
timeout: number?
|
||||
socksProxyRedirectPortForTest: number?
|
||||
returns:
|
||||
pipe: JsonPipe
|
||||
|
||||
launch:
|
||||
parameters:
|
||||
$mixin: LaunchOptions
|
||||
|
Loading…
Reference in New Issue
Block a user