Skip to content

Commit

Permalink
feat: routeWebSocket (#32675)
Browse files Browse the repository at this point in the history
This introduces `WebSocketRoute` class and
`page/context.routeWebSocket()` methods.
  • Loading branch information
dgozman authored Sep 20, 2024
1 parent ace8cb2 commit cdcaa7f
Show file tree
Hide file tree
Showing 22 changed files with 1,970 additions and 7 deletions.
93 changes: 93 additions & 0 deletions docs/src/api/class-browsercontext.md
Original file line number Diff line number Diff line change
Expand Up @@ -1266,6 +1266,99 @@ When set to `minimal`, only record information necessary for routing from HAR. T

Optional setting to control resource content management. If `attach` is specified, resources are persisted as separate files or entries in the ZIP archive. If `embed` is specified, content is stored inline the HAR file.


## async method: BrowserContext.routeWebSocket
* since: v1.48

This method allows to modify websocket connections that are made by any page in the browser context.

Note that only `WebSocket`s created after this method was called will be routed. It is recommended to call this method before creating any pages.

**Usage**

Below is an example of a simple handler that blocks some websocket messages.
See [WebSocketRoute] for more details and examples.

```js
await context.routeWebSocket('/ws', async ws => {
ws.routeSend(message => {
if (message === 'to-be-blocked')
return;
ws.send(message);
});
await ws.connect();
});
```

```java
context.routeWebSocket("/ws", ws -> {
ws.routeSend(message -> {
if ("to-be-blocked".equals(message))
return;
ws.send(message);
});
ws.connect();
});
```

```python async
def message_handler(ws: WebSocketRoute, message: Union[str, bytes]):
if message == "to-be-blocked":
return
ws.send(message)

async def handler(ws: WebSocketRoute):
ws.route_send(lambda message: message_handler(ws, message))
await ws.connect()

await context.route_web_socket("/ws", handler)
```

```python sync
def message_handler(ws: WebSocketRoute, message: Union[str, bytes]):
if message == "to-be-blocked":
return
ws.send(message)

def handler(ws: WebSocketRoute):
ws.route_send(lambda message: message_handler(ws, message))
ws.connect()

context.route_web_socket("/ws", handler)
```

```csharp
await context.RouteWebSocketAsync("/ws", async ws => {
ws.RouteSend(message => {
if (message == "to-be-blocked")
return;
ws.Send(message);
});
await ws.ConnectAsync();
});
```

### param: BrowserContext.routeWebSocket.url
* since: v1.48
- `url` <[string]|[RegExp]|[function]\([URL]\):[boolean]>

Only WebSockets with the url matching this pattern will be routed. A string pattern can be relative to the [`option: baseURL`] from the context options.

### param: BrowserContext.routeWebSocket.handler
* since: v1.48
* langs: js, python
- `handler` <[function]\([WebSocketRoute]\): [Promise<any>|any]>

Handler function to route the WebSocket.

### param: BrowserContext.routeWebSocket.handler
* since: v1.48
* langs: csharp, java
- `handler` <[function]\([WebSocketRoute]\)>

Handler function to route the WebSocket.


## method: BrowserContext.serviceWorkers
* since: v1.11
* langs: js, python
Expand Down
93 changes: 93 additions & 0 deletions docs/src/api/class-page.md
Original file line number Diff line number Diff line change
Expand Up @@ -3632,6 +3632,99 @@ When set to `minimal`, only record information necessary for routing from HAR. T

Optional setting to control resource content management. If `attach` is specified, resources are persisted as separate files or entries in the ZIP archive. If `embed` is specified, content is stored inline the HAR file.


## async method: Page.routeWebSocket
* since: v1.48

This method allows to modify websocket connections that are made by the page.

Note that only `WebSocket`s created after this method was called will be routed. It is recommended to call this method before navigating the page.

**Usage**

Below is an example of a simple handler that blocks some websocket messages.
See [WebSocketRoute] for more details and examples.

```js
await page.routeWebSocket('/ws', async ws => {
ws.routeSend(message => {
if (message === 'to-be-blocked')
return;
ws.send(message);
});
await ws.connect();
});
```

```java
page.routeWebSocket("/ws", ws -> {
ws.routeSend(message -> {
if ("to-be-blocked".equals(message))
return;
ws.send(message);
});
ws.connect();
});
```

```python async
def message_handler(ws: WebSocketRoute, message: Union[str, bytes]):
if message == "to-be-blocked":
return
ws.send(message)

async def handler(ws: WebSocketRoute):
ws.route_send(lambda message: message_handler(ws, message))
await ws.connect()

await page.route_web_socket("/ws", handler)
```

```python sync
def message_handler(ws: WebSocketRoute, message: Union[str, bytes]):
if message == "to-be-blocked":
return
ws.send(message)

def handler(ws: WebSocketRoute):
ws.route_send(lambda message: message_handler(ws, message))
ws.connect()

page.route_web_socket("/ws", handler)
```

```csharp
await page.RouteWebSocketAsync("/ws", async ws => {
ws.RouteSend(message => {
if (message == "to-be-blocked")
return;
ws.Send(message);
});
await ws.ConnectAsync();
});
```

### param: Page.routeWebSocket.url
* since: v1.48
- `url` <[string]|[RegExp]|[function]\([URL]\):[boolean]>

Only WebSockets with the url matching this pattern will be routed. A string pattern can be relative to the [`option: baseURL`] from the context options.

### param: Page.routeWebSocket.handler
* since: v1.48
* langs: js, python
- `handler` <[function]\([WebSocketRoute]\): [Promise<any>|any]>

Handler function to route the WebSocket.

### param: Page.routeWebSocket.handler
* since: v1.48
* langs: csharp, java
- `handler` <[function]\([WebSocketRoute]\)>

Handler function to route the WebSocket.


## async method: Page.screenshot
* since: v1.8
- returns: <[Buffer]>
Expand Down
166 changes: 166 additions & 0 deletions docs/src/api/class-websocketroute.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# class: WebSocketRoute
* since: v1.48

Whenever a [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) route is set up with [`method: Page.routeWebSocket`] or [`method: BrowserContext.routeWebSocket`], the `WebSocketRoute` object allows to handle the WebSocket.

By default, the routed WebSocket will not actually connect to the server. This way, you can mock entire communcation over the WebSocket. Here is an example that responds to a `"query"` with a `"result"`.

```js
await page.routeWebSocket('/ws', async ws => {
ws.routeSend(message => {
if (message === 'query')
ws.receive('result');
});
});
```

```java
page.routeWebSocket("/ws", ws -> {
ws.routeSend(message -> {
if ("query".equals(message))
ws.receive("result");
});
});
```

```python async
def message_handler(ws, message):
if message == "query":
ws.receive("result")

await page.route_web_socket("/ws", lambda ws: ws.route_send(
lambda message: message_handler(ws, message)
))
```

```python sync
def message_handler(ws, message):
if message == "query":
ws.receive("result")

page.route_web_socket("/ws", lambda ws: ws.route_send(
lambda message: message_handler(ws, message)
))
```

```csharp
await page.RouteWebSocketAsync("/ws", async ws => {
ws.RouteSend(message => {
if (message == "query")
ws.receive("result");
});
});
```


## event: WebSocketRoute.close
* since: v1.48

Emitted when the [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) closes.



## async method: WebSocketRoute.close
* since: v1.48

Closes the server connection and the [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) object in the page.

### option: WebSocketRoute.close.code
* since: v1.48
- `code` <[int]>

Optional [close code](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#code).

### option: WebSocketRoute.close.reason
* since: v1.48
- `reason` <[string]>

Optional [close reason](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#reason).


## async method: WebSocketRoute.connect
* since: v1.48

By default, routed WebSocket does not connect to the server, so you can mock entire WebSocket communication. This method connects to the actual WebSocket server, giving the ability to send and receive messages from the server.

Once connected:
* Messages received from the server will be automatically dispatched to the [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) object in the page, unless [`method: WebSocketRoute.routeReceive`] is called.
* Messages sent by the `WebSocket.send()` call in the page will be automatically sent to the server, unless [`method: WebSocketRoute.routeSend`] is called.


## method: WebSocketRoute.receive
* since: v1.48

Dispatches a message to the [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) object in the page, like it was received from the server.

### param: WebSocketRoute.receive.message
* since: v1.48
- `message` <[string]|[Buffer]>

Message to receive.


## async method: WebSocketRoute.routeReceive
* since: v1.48

This method allows to route messages that are received by the [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) object in the page from the server. This method only makes sense if you are also calling [`method: WebSocketRoute.connect`].

Once this method is called, received messages are not automatically dispatched to the [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) object in the page - you should do that manually by calling [`method: WebSocketRoute.receive`].

Calling this method again times will override the handler with a new one.

### param: WebSocketRoute.routeReceive.handler
* since: v1.48
* langs: js, python
- `handler` <[function]\([string]\): [Promise<any>|any]>

Handler function to route received messages.

### param: WebSocketRoute.routeReceive.handler
* since: v1.48
* langs: csharp, java
- `handler` <[function]\([WebSocketFrame]\)>

Handler function to route received messages.



## async method: WebSocketRoute.routeSend
* since: v1.48

This method allows to route messages that are sent by `WebSocket.send()` call in the page, instead of actually sending them to the server. Once this method is called, sent messages **are not** automatically forwarded to the server - you should do that manually by calling [`method: WebSocketRoute.send`].

Calling this method again times will override the handler with a new one.

### param: WebSocketRoute.routeSend.handler
* since: v1.48
* langs: js, python
- `handler` <[function]\([string]|[Buffer]\): [Promise<any>|any]>

Handler function to route sent messages.

### param: WebSocketRoute.routeSend.handler
* since: v1.48
* langs: csharp, java
- `handler` <[function]\([WebSocketFrame]\)>

Handler function to route sent messages.


## method: WebSocketRoute.send
* since: v1.48

Sends a message to the server, like it was sent in the page with `WebSocket.send()`.

### param: WebSocketRoute.send.message
* since: v1.48
- `message` <[string]|[Buffer]>

Message to send.


## method: WebSocketRoute.url
* since: v1.48
- returns: <[string]>

URL of the WebSocket created in the page.
2 changes: 1 addition & 1 deletion packages/playwright-core/src/client/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export { TimeoutError } from './errors';
export { Frame } from './frame';
export { Keyboard, Mouse, Touchscreen } from './input';
export { JSHandle } from './jsHandle';
export { Request, Response, Route, WebSocket } from './network';
export { Request, Response, Route, WebSocket, WebSocketRoute } from './network';
export { APIRequest, APIRequestContext, APIResponse } from './fetch';
export { Page } from './page';
export { Selectors } from './selectors';
Expand Down
Loading

0 comments on commit cdcaa7f

Please sign in to comment.