Skip to content

Commit 72cd6f0

Browse files
authored
chore: prework for web ws connection (#34718)
1 parent 91f46bb commit 72cd6f0

File tree

2 files changed

+90
-10
lines changed

2 files changed

+90
-10
lines changed

packages/playwright-core/src/client/localUtils.ts

Lines changed: 84 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ import { ChannelOwner } from './channelOwner';
1818
import { Connection } from './connection';
1919
import * as localUtils from '../utils/localUtils';
2020

21-
import type { Size } from './types';
21+
import type { HeadersArray, Size } from './types';
2222
import type { HarBackend } from '../utils/harBackend';
23+
import type { Platform } from '../utils/platform';
2324
import type * as channels from '@protocol/channels';
2425

2526
type DeviceDescriptor = {
@@ -78,27 +79,100 @@ export class LocalUtils extends ChannelOwner<channels.LocalUtilsChannel> {
7879
}
7980

8081
async connect(params: channels.LocalUtilsConnectParams): Promise<Connection> {
81-
const { pipe, headers: connectHeaders } = await this._channel.connect(params);
82-
const closePipe = () => this._wrapApiCall(() => pipe.close().catch(() => {}), /* isInternal */ true);
82+
const transport = this._platform.ws ? new WebSocketTransport(this._platform) : new JsonPipeTransport(this);
83+
const connectHeaders = await transport.connect(params);
8384
const connection = new Connection(this, this._platform, this._instrumentation, connectHeaders);
8485
connection.markAsRemote();
85-
connection.on('close', closePipe);
86+
connection.on('close', () => transport.close());
8687

8788
let closeError: string | undefined;
88-
const onPipeClosed = (reason?: string) => {
89+
const onTransportClosed = (reason?: string) => {
8990
connection.close(reason || closeError);
9091
};
91-
pipe.on('closed', params => onPipeClosed(params.reason));
92-
connection.onmessage = message => this._wrapApiCall(() => pipe.send({ message }).catch(() => onPipeClosed()), /* isInternal */ true);
93-
94-
pipe.on('message', ({ message }) => {
92+
transport.onClose(reason => onTransportClosed(reason));
93+
connection.onmessage = message => transport.send(message).catch(() => onTransportClosed());
94+
transport.onMessage(message => {
9595
try {
9696
connection!.dispatch(message);
9797
} catch (e) {
9898
closeError = String(e);
99-
closePipe();
99+
transport.close();
100100
}
101101
});
102102
return connection;
103103
}
104104
}
105+
interface Transport {
106+
connect(params: channels.LocalUtilsConnectParams): Promise<HeadersArray>;
107+
send(message: any): Promise<void>;
108+
onMessage(callback: (message: object) => void): void;
109+
onClose(callback: (reason?: string) => void): void;
110+
close(): Promise<void>;
111+
}
112+
113+
class JsonPipeTransport implements Transport {
114+
private _pipe: channels.JsonPipeChannel | undefined;
115+
private _owner: ChannelOwner<channels.LocalUtilsChannel>;
116+
117+
constructor(owner: ChannelOwner<channels.LocalUtilsChannel>) {
118+
this._owner = owner;
119+
}
120+
121+
async connect(params: channels.LocalUtilsConnectParams) {
122+
const { pipe, headers: connectHeaders } = await this._owner._wrapApiCall(async () => {
123+
return await this._owner._channel.connect(params);
124+
}, /* isInternal */ true);
125+
this._pipe = pipe;
126+
return connectHeaders;
127+
}
128+
129+
async send(message: object) {
130+
this._owner._wrapApiCall(async () => {
131+
await this._pipe!.send({ message });
132+
}, /* isInternal */ true);
133+
}
134+
135+
onMessage(callback: (message: object) => void) {
136+
this._pipe!.on('message', ({ message }) => callback(message));
137+
}
138+
139+
onClose(callback: (reason?: string) => void) {
140+
this._pipe!.on('closed', ({ reason }) => callback(reason));
141+
}
142+
143+
async close() {
144+
await this._owner._wrapApiCall(async () => {
145+
await this._pipe!.close().catch(() => {});
146+
}, /* isInternal */ true);
147+
}
148+
}
149+
150+
class WebSocketTransport implements Transport {
151+
private _platform: Platform;
152+
private _ws: WebSocket | undefined;
153+
154+
constructor(platform: Platform) {
155+
this._platform = platform;
156+
}
157+
158+
async connect(params: channels.LocalUtilsConnectParams) {
159+
this._ws = this._platform.ws!(params.wsEndpoint);
160+
return [];
161+
}
162+
163+
async send(message: object) {
164+
this._ws!.send(JSON.stringify(message));
165+
}
166+
167+
onMessage(callback: (message: object) => void) {
168+
this._ws!.addEventListener('message', event => callback(JSON.parse(event.data)));
169+
}
170+
171+
onClose(callback: (reason?: string) => void) {
172+
this._ws!.addEventListener('close', () => callback());
173+
}
174+
175+
async close() {
176+
this._ws!.close();
177+
}
178+
}

packages/playwright-core/src/utils/platform.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export type Platform = {
2222
fs: () => typeof fs;
2323
path: () => typeof path;
2424
inspectCustom: symbol | undefined;
25+
ws?: (url: string) => WebSocket;
2526
};
2627

2728
export const emptyPlatform: Platform = {
@@ -41,3 +42,8 @@ export const nodePlatform: Platform = {
4142
path: () => path,
4243
inspectCustom: util.inspect.custom,
4344
};
45+
46+
export const webPlatform: Platform = {
47+
...emptyPlatform,
48+
ws: (url: string) => new WebSocket(url),
49+
};

0 commit comments

Comments
 (0)