Skip to content

Commit 4cdc259

Browse files
authored
Document the WebSocket API (#371)
1 parent 3fbdbfc commit 4cdc259

7 files changed

Lines changed: 1055 additions & 3 deletions

File tree

websites/mswjs.io/src/components/react/action.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import { ArrowRightCircleIcon } from '@heroicons/react/24/solid'
33

44
export function Action({ children }: { children: ReactNode }) {
55
return (
6-
<p className="text-primary font-semibold">
7-
<ArrowRightCircleIcon className="inline w-5 -mt-[0.25ch] mr-[0.5ch] align-middle" />
6+
<p className="font-semibold text-primary">
7+
<ArrowRightCircleIcon className="inline -mt-[0.25ch] mr-[0.5ch] w-5 align-middle" />
88
{children}
99
</p>
1010
)
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
---
2+
order: 4
3+
title: ws
4+
description: Intercept WebSocket connections.
5+
keywords:
6+
- websocket
7+
- socket
8+
- event
9+
- handler
10+
- namespace
11+
---
12+
13+
The `ws` namespace helps you create event handlers to intercept WebSocket connections.
14+
15+
## Call signature
16+
17+
The `ws` namespace exposes a method called `link()`. The `link()` method creates a WebSocket link preconfigured to handle WebSocket connections matching the specified URL.
18+
19+
```ts
20+
ws.link(url: string | URL | RegExp)
21+
```
22+
23+
import { PageCard } from '../../../components/react/pageCard'
24+
import { CodeBracketSquareIcon } from '@heroicons/react/24/outline'
25+
26+
<PageCard
27+
icon={CodeBracketSquareIcon}
28+
url="https://github.com/mswjs/msw/tree/main/src/core/ws/ws.ts"
29+
title="ws.ts"
30+
description="Source code for the `ws` namespace."
31+
/>
32+
33+
## Event handler
34+
35+
The object returned from the `ws.link()` call is referred to as a _WebSocket link_. The link has the following properties and methods:
36+
37+
### `.addEventListener(event, listener)`
38+
39+
Adds a [connection listener](#connection-listener) for the outgoing WebSocket client connections.
40+
41+
### `.clients`
42+
43+
- `Set<WebSocketClientConnection>`
44+
45+
The set of all active WebSocket clients.
46+
47+
### `.broadcast(data)`
48+
49+
- `data: string | Blob | ArrayBuffer`
50+
51+
Sends the given data to all active WebSocket clients.
52+
53+
```js {2}
54+
const api = ws.link('wss://*')
55+
api.broadcast('hello, everyone')
56+
```
57+
58+
### `.broadcastExcept(clients, data)`
59+
60+
- `clients: WebSocketClientConnection | Array<WebSocketClientConnection>`
61+
- `data: string | Blob | ArrayBuffer`
62+
63+
Sends the given data to all active WebSocket clients except the given `clients`.
64+
65+
```js {4}
66+
const api = ws.link('wss://*')
67+
68+
api.addEventListener('connection', ({ client }) => {
69+
api.broadcastExcept(client, 'all except this')
70+
})
71+
```
72+
73+
You can also provide an array of WebSocket client connections as the argument to `clients`:
74+
75+
```js
76+
const ignoredClients = Array.from(api.clients).filter((client) => {
77+
return client.url.includes('abc')
78+
})
79+
80+
api.broadcastExcept(ignoredClients, 'hello')
81+
```
82+
83+
## Connection listener
84+
85+
| Argument | Type | Description |
86+
| -------- | --------------------------------------------------------- | ---------------------------------------------------- |
87+
| `client` | [`WebSocketClientConnection`](#websocketclientconnection) | Outgoing WebSocket client connection object. |
88+
| `server` | [`WebSocketServerConnection`](#websocketserverconnection) | Actual WebSocket server connection object. |
89+
| `params` | `Record<string, string>` | Path parameters extracted from the connection `url`. |
90+
| `info` | [`WebSocketConnectionInfo`](#websocketconnectioninfo) | Extra information about this WebSocket connection. |
91+
92+
The connection listener is called on every outgoing WebSocket client connection.
93+
94+
```js {7-9}
95+
import { ws } from 'msw'
96+
import { setupWorker } from 'msw/browser'
97+
98+
const api = ws.link('wss://chat.example.com')
99+
100+
const worker = setupWorker(
101+
api.addEventListener('connection', () => {
102+
console.log('client connected!')
103+
}),
104+
)
105+
106+
await worker.start()
107+
108+
const socket = new WebSocket('wss://chat.example.com')
109+
socket.onopen = () => {
110+
console.log('connection established!')
111+
}
112+
```
113+
114+
In this example, the WebSocket connection to `wss://chat.example.com` emits the `"connection"` event on the `api` event handler because the endpoint matches the one provided to the `ws.link()` call. Since the connection is successful, the `"open"` event is also dispatched on the `socket` instance.
115+
116+
## `WebSocketClientConnection`
117+
118+
The `WebSocketClientConnection` object represents an intercepted WebSocket client connection from the _server's_ perspective. This means that the `message` event on the client stands for a message _sent_ by the client and received by the "server".
119+
120+
### `.addEventListener(event, listener, options)`
121+
122+
Adds a listener to the given client event. These are the supported client events:
123+
124+
| Event name | Description |
125+
| ---------- | --------------------------------------------------------------------- |
126+
| `message` | Dispatched when this client _sends_ a message. |
127+
| `error` | Dispatched when this client connection has been closed with an error. |
128+
| `close` | Dispatched when this client is closed (e.g. by your application). |
129+
130+
### `.removeEventListener(event, listener, options)`
131+
132+
Removes the listener for the given client event.
133+
134+
### `.send(data)`
135+
136+
- `data: string | Blob | ArrayBuffer`
137+
138+
Sends data to the WebSocket client. This is equivalent to the client receiving that data from the server.
139+
140+
```js {2-4}
141+
api.addEventListener('connection', ({ client }) => {
142+
client.send('hello')
143+
client.send(new Blob(['hello']))
144+
client.send(new TextEncoder().encode('hello'))
145+
})
146+
```
147+
148+
### `.close(code, reason)`
149+
150+
- `code: number | undefined`, default: `1000`
151+
- `reason: string | undefined`
152+
153+
Closes the active WebSocket client connection.
154+
155+
```js {2}
156+
api.addEventListener('connection', ({ client }) => {
157+
client.close()
158+
})
159+
```
160+
161+
Unlike the `WebSocket.prototype.close()` method, the `client.close()` method accepts non-configurable close codes. This allows you to emulate client close scenarios based on server-side errors.
162+
163+
```js {3}
164+
api.addEventListener('connection', ({ client }) => {
165+
client.addEventListener('message', (event) => {
166+
client.close(1003, 'Invalid data')
167+
})
168+
})
169+
```
170+
171+
You can also implement custom close code and reason:
172+
173+
```js {2}
174+
api.addEventListener('connection', ({ client }) => {
175+
client.close(4000, 'Custom close reason')
176+
})
177+
```
178+
179+
## `WebSocketServerConnection`
180+
181+
The `WebSocketServerConnection` object represents the actual WebSocket server connection.
182+
183+
### `.connect()`
184+
185+
Establishes connection to the actual WebSocket server.
186+
187+
```js {2}
188+
api.addEventListener('connection', ({ server }) => {
189+
server.connect()
190+
})
191+
```
192+
193+
### `.addEventListener(event, listener, options)`
194+
195+
Adds a listener to the original server WebSocket connection. The supported events are:
196+
197+
| Event name | Description |
198+
| ---------- | ----------------------------------------------------------------------------- |
199+
| `open` | Dispatched when the connection to the original server has been opened. |
200+
| `message` | Dispatched when the original server _sends_ a message. |
201+
| `error` | Dispatched when the original server connection has been closed with an error. |
202+
| `close` | Dispatched when the original server connection has been closed. |
203+
204+
### `.removeEventListener(event, listener, options)`
205+
206+
Removes the listener for the given server event.
207+
208+
### `.send(data)`
209+
210+
- `data: string | Blob | ArrayBuffer`
211+
212+
Sends data to the actual WebSocket server. This is equivalent to the client sending this data to the server.
213+
214+
```js {6}
215+
api.addEventListener('connection', ({ server }) => {
216+
server.connect()
217+
218+
server.addEventListener('message', (event) => {
219+
if (event.data === 'hello from server') {
220+
server.send('hello from client')
221+
}
222+
})
223+
})
224+
```
225+
226+
## `WebSocketConnectionInfo`
227+
228+
The `info` argument on the `connection` event listener contains additional WebSocket connection infromation.
229+
230+
| Property name | Type | Description |
231+
| `protocols` | `string | string[] | undefined` | The list of protocols used when establishing this WebSocket connection. |
232+
233+
```js
234+
api.addEventListener('connection', ({ info }) => {
235+
if (info.protocols?.includes('chat')) {
236+
// ...
237+
}
238+
})
239+
```

0 commit comments

Comments
 (0)