Skip to content

Commit c5a58da

Browse files
committed
fix: support erroring unhandled websocket connections
1 parent 1ac66f0 commit c5a58da

File tree

9 files changed

+50
-40
lines changed

9 files changed

+50
-40
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@
241241
"@bundled-es-modules/statuses": "^1.0.1",
242242
"@inquirer/confirm": "^5.0.0",
243243
"@msgpack/msgpack": "^3.1.2",
244-
"@mswjs/interceptors": "^0.39.7",
244+
"@mswjs/interceptors": "^0.39.8",
245245
"@open-draft/deferred-promise": "^2.2.0",
246246
"@types/cookie": "^0.6.0",
247247
"@types/statuses": "^2.0.4",

pnpm-lock.yaml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/core/new/define-network.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,7 @@ export function defineNetwork(options: DefineNetworkOptions): NetworkApi {
8383
frame,
8484
options.onUnhandledFrame || 'bypass',
8585
).catch((error) => {
86-
/**
87-
* @fixme `.errorWith()` should exist on WebSocket frames too
88-
* since you can error the connection by dispatching the "error" event.
89-
*/
90-
if (frame.protocol === 'http') {
91-
frame.errorWith(error)
92-
}
86+
frame.errorWith(error)
9387
})
9488
},
9589
})

src/core/new/frames/base-frame.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ export abstract class BaseNetworkFrame<
1414
this.events = new Emitter<Events>()
1515
}
1616

17+
public abstract errorWith(reason?: unknown): unknown
18+
1719
/**
1820
* Returns a message to be used when this frame goes unhandled.
1921
*/

src/core/new/resolve-network-frame.ts

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -154,32 +154,26 @@ async function resolveWebSocketNetworkFrame(
154154
})
155155

156156
if (eventHandlers.length > 0) {
157-
const matches = await Promise.all<boolean>(
157+
await Promise.all(
158158
eventHandlers.map(async (handler) => {
159159
// Foward the connection data to every WebSocket handler.
160160
// This is equivalent to dispatching the connection event
161161
// onto multiple listeners.
162-
return handler.run(connection).then((matches) => {
163-
if (matches) {
164-
flow?.handled?.({ frame, handler })
162+
const matches = await handler.run(connection)
165163

166-
if (!flow?.quiet) {
167-
handler.log(connection)
168-
}
169-
}
164+
if (matches) {
165+
flow?.handled?.({ frame, handler })
170166

171-
return matches
172-
})
167+
if (!flow?.quiet) {
168+
handler.log(connection)
169+
}
170+
}
173171
}),
174172
)
175173

176-
if (matches.every((match) => !match)) {
177-
flow?.unhandled?.()
178-
}
179-
180174
return
181175
}
182176

177+
flow?.unhandled?.()
183178
frame.passthrough()
184-
flow.unhandled?.()
185179
}

src/core/new/sources/interceptor-source.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ export class InterceptorSource extends NetworkSource {
4545
* @fixme Incorrect disciminated union when merging multiple
4646
* events map in `BatchInterceptor` (results in `Listener<unknown>`).
4747
*/
48-
this.#interceptor.on('request', this.#onRequest.bind(this) as any)
49-
this.#interceptor.on('response', this.#onResponse.bind(this) as any)
48+
this.#interceptor
49+
.on('request', this.#onRequest.bind(this) as any)
50+
.on('response', this.#onResponse.bind(this) as any)
5051

5152
this.#interceptor.on(
5253
'connection',
@@ -163,6 +164,29 @@ class InterceptorWebSocketNetworkFrame extends WebSocketNetworkFrame {
163164
})
164165
}
165166

167+
public errorWith(reason?: unknown): void {
168+
if (reason instanceof Error) {
169+
const { client } = this.data.connection
170+
171+
/**
172+
* Use `client.errorWith(reason)` in the future.
173+
* @see https://github.com/mswjs/interceptors/issues/747
174+
*/
175+
const errorEvent = new Event('error')
176+
Object.defineProperty(errorEvent, 'cause', {
177+
enumerable: true,
178+
configurable: false,
179+
value: reason,
180+
})
181+
182+
/**
183+
* @fixme So this all is fine but it doesn't fire at the right time.
184+
* WS interceptor does NOT await listeners.
185+
*/
186+
client.socket.dispatchEvent(errorEvent)
187+
}
188+
}
189+
166190
public passthrough() {
167191
this.data.connection.server.connect()
168192
}

src/node/setup-remote-server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export interface SetupRemoteServerListenOptions extends SharedOptions {
1212
port: number
1313
}
1414

15-
interface SetupRemoteServer extends NetworkHandlersApi<AnyHandler> {
15+
interface SetupRemoteServer extends NetworkHandlersApi {
1616
listen(options: SetupRemoteServerListenOptions): Promise<void>
1717
close(): Promise<void>
1818
boundary<Args extends Array<any>, R>(

src/node/setup-server.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export class SetupServerApi implements SetupServer {
4040
public restoreHandlers: SetupServerCommon['restoreHandlers']
4141
public listHandlers: SetupServerCommon['listHandlers']
4242

43-
#network: NetworkApi<AnyHandler>
43+
#network: NetworkApi
4444
#handlersController: AsyncHandlersController
4545
#currentHandlersProxy?: ReversibleProxy<HandlersController['currentHandlers']>
4646
#listenOptions?: Partial<ListenOptions>
@@ -65,10 +65,6 @@ export class SetupServerApi implements SetupServer {
6565
}),
6666
})
6767

68-
this.#network.events.on('log', (event) => {
69-
event.preventDefault()
70-
})
71-
7268
/**
7369
* @fixme This expects a readonly emitter (subset of methods).
7470
*/

test/node/ws-api/on-unhandled-request/error.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ it(
3939

4040
expect(console.error).toHaveBeenCalledWith(
4141
`\
42-
[MSW] Error: intercepted a request without a matching request handler:
42+
[MSW] Error: intercepted a WebSocket connection without a matching event handler:
4343
44-
GET wss://localhost:4321/
44+
• wss://localhost:4321/
4545
46-
If you still wish to intercept this unhandled request, please create a request handler for it.
47-
Read more: https://mswjs.io/docs/http/intercepting-requests`,
46+
If you still wish to intercept this unhandled connection, please create an event handler for it.
47+
Read more: https://mswjs.io/docs/websocket`,
4848
)
4949

5050
expect(errorListener).toHaveBeenCalledOnce()

0 commit comments

Comments
 (0)