Skip to content

Commit 94fc78e

Browse files
authored
feat(WebSocketHandler): add run method (#2527)
1 parent ca9d877 commit 94fc78e

File tree

6 files changed

+67
-132
lines changed

6 files changed

+67
-132
lines changed

src/core/handlers/WebSocketHandler.test.ts

Lines changed: 12 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
1-
import type { WebSocketConnectionData } from '@mswjs/interceptors/WebSocket'
21
import { WebSocketHandler } from './WebSocketHandler'
32

43
describe('parse', () => {
54
it('matches an exact url', () => {
65
expect(
76
new WebSocketHandler('ws://localhost:3000').parse({
8-
event: new MessageEvent('connection', {
9-
data: {
10-
client: {
11-
url: new URL('ws://localhost:3000'),
12-
},
13-
} as WebSocketConnectionData,
14-
}),
7+
url: new URL('ws://localhost:3000'),
158
}),
169
).toEqual({
1710
match: {
@@ -24,13 +17,7 @@ describe('parse', () => {
2417
it('ignores trailing slash', () => {
2518
expect(
2619
new WebSocketHandler('ws://localhost:3000').parse({
27-
event: new MessageEvent('connection', {
28-
data: {
29-
client: {
30-
url: new URL('ws://localhost:3000/'),
31-
},
32-
} as WebSocketConnectionData,
33-
}),
20+
url: new URL('ws://localhost:3000/'),
3421
}),
3522
).toEqual({
3623
match: {
@@ -41,13 +28,7 @@ describe('parse', () => {
4128

4229
expect(
4330
new WebSocketHandler('ws://localhost:3000/').parse({
44-
event: new MessageEvent('connection', {
45-
data: {
46-
client: {
47-
url: new URL('ws://localhost:3000'),
48-
},
49-
} as WebSocketConnectionData,
50-
}),
31+
url: new URL('ws://localhost:3000/'),
5132
}),
5233
).toEqual({
5334
match: {
@@ -60,13 +41,7 @@ describe('parse', () => {
6041
it('supports path parameters', () => {
6142
expect(
6243
new WebSocketHandler('ws://localhost:3000/:serviceName').parse({
63-
event: new MessageEvent('connection', {
64-
data: {
65-
client: {
66-
url: new URL('ws://localhost:3000/auth'),
67-
},
68-
} as WebSocketConnectionData,
69-
}),
44+
url: new URL('ws://localhost:3000/auth'),
7045
}),
7146
).toEqual({
7247
match: {
@@ -81,15 +56,9 @@ describe('parse', () => {
8156
it('ignores "/socket.io/" prefix in the client url', () => {
8257
expect(
8358
new WebSocketHandler('ws://localhost:3000').parse({
84-
event: new MessageEvent('connection', {
85-
data: {
86-
client: {
87-
url: new URL(
88-
'ws://localhost:3000/socket.io/?EIO=4&transport=websocket',
89-
),
90-
},
91-
} as WebSocketConnectionData,
92-
}),
59+
url: new URL(
60+
'ws://localhost:3000/socket.io/?EIO=4&transport=websocket',
61+
),
9362
}),
9463
).toEqual({
9564
match: {
@@ -100,15 +69,9 @@ describe('parse', () => {
10069

10170
expect(
10271
new WebSocketHandler('ws://localhost:3000/non-matching').parse({
103-
event: new MessageEvent('connection', {
104-
data: {
105-
client: {
106-
url: new URL(
107-
'ws://localhost:3000/socket.io/?EIO=4&transport=websocket',
108-
),
109-
},
110-
} as WebSocketConnectionData,
111-
}),
72+
url: new URL(
73+
'ws://localhost:3000/socket.io/?EIO=4&transport=websocket',
74+
),
11275
}),
11376
).toEqual({
11477
match: {
@@ -125,13 +88,7 @@ describe('parse', () => {
12588
*/
12689
expect(
12790
new WebSocketHandler('ws://localhost:3000/clients/socket.io/123').parse({
128-
event: new MessageEvent('connection', {
129-
data: {
130-
client: {
131-
url: new URL('ws://localhost:3000/clients/socket.io/123'),
132-
},
133-
} as WebSocketConnectionData,
134-
}),
91+
url: new URL('ws://localhost:3000/clients/socket.io/123'),
13592
}),
13693
).toEqual({
13794
match: {
@@ -142,13 +99,7 @@ describe('parse', () => {
14299

143100
expect(
144101
new WebSocketHandler('ws://localhost:3000').parse({
145-
event: new MessageEvent('connection', {
146-
data: {
147-
client: {
148-
url: new URL('ws://localhost:3000/clients/socket.io/123'),
149-
},
150-
} as WebSocketConnectionData,
151-
}),
102+
url: new URL('ws://localhost:3000/clients/socket.io/123'),
152103
}),
153104
).toEqual({
154105
match: {

src/core/handlers/WebSocketHandler.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ export interface WebSocketHandlerConnection extends WebSocketConnectionData {
2323
}
2424

2525
export const kEmitter = Symbol('kEmitter')
26-
export const kDispatchEvent = Symbol('kDispatchEvent')
2726
export const kSender = Symbol('kSender')
2827
const kStopPropagationPatched = Symbol('kStopPropagationPatched')
2928
const KOnStopPropagation = Symbol('KOnStopPropagation')
@@ -44,11 +43,8 @@ export class WebSocketHandler {
4443
this.__kind = 'EventHandler'
4544
}
4645

47-
public parse(args: {
48-
event: MessageEvent<WebSocketConnectionData>
49-
}): WebSocketHandlerParsedResult {
50-
const { data: connection } = args.event
51-
const { url: clientUrl } = connection.client
46+
public parse(args: { url: URL }): WebSocketHandlerParsedResult {
47+
const clientUrl = new URL(args.url)
5248

5349
/**
5450
* @note Remove the Socket.IO path prefix from the WebSocket
@@ -65,23 +61,30 @@ export class WebSocketHandler {
6561
}
6662

6763
public predicate(args: {
68-
event: MessageEvent<WebSocketConnectionData>
64+
url: URL
6965
parsedResult: WebSocketHandlerParsedResult
7066
}): boolean {
7167
return args.parsedResult.match.matches
7268
}
7369

74-
async [kDispatchEvent](
75-
event: MessageEvent<WebSocketConnectionData>,
76-
): Promise<void> {
77-
const parsedResult = this.parse({ event })
78-
const connection = event.data
70+
public async run(connection: WebSocketConnectionData): Promise<boolean> {
71+
const parsedResult = this.parse({
72+
url: connection.client.url,
73+
})
74+
75+
if (!this.predicate({ url: connection.client.url, parsedResult })) {
76+
return false
77+
}
7978

8079
const resolvedConnection: WebSocketHandlerConnection = {
8180
...connection,
8281
params: parsedResult.match.params || {},
8382
}
8483

84+
return this.connect(resolvedConnection)
85+
}
86+
87+
protected connect(connection: WebSocketHandlerConnection): boolean {
8588
// Support `event.stopPropagation()` for various client/server events.
8689
connection.client.addEventListener(
8790
'message',
@@ -111,7 +114,7 @@ export class WebSocketHandler {
111114

112115
// Emit the connection event on the handler.
113116
// This is what the developer adds listeners for.
114-
this[kEmitter].emit('connection', resolvedConnection)
117+
return this[kEmitter].emit('connection', connection)
115118
}
116119
}
117120

src/core/ws/handleWebSocketEvent.ts

Lines changed: 36 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { WebSocketConnectionData } from '@mswjs/interceptors/lib/browser/interceptors/WebSocket'
22
import { RequestHandler } from '../handlers/RequestHandler'
3-
import { WebSocketHandler, kDispatchEvent } from '../handlers/WebSocketHandler'
3+
import { WebSocketHandler } from '../handlers/WebSocketHandler'
44
import { webSocketInterceptor } from './webSocketInterceptor'
55
import {
66
onUnhandledRequest,
@@ -17,67 +17,48 @@ interface HandleWebSocketEventOptions {
1717

1818
export function handleWebSocketEvent(options: HandleWebSocketEventOptions) {
1919
webSocketInterceptor.on('connection', async (connection) => {
20-
const handlers = options.getHandlers()
20+
const handlers = options.getHandlers().filter(isHandlerKind('EventHandler'))
2121

22-
const connectionEvent = new MessageEvent('connection', {
23-
data: connection,
24-
})
22+
// Ignore this connection if the user hasn't defined any handlers.
23+
if (handlers.length > 0) {
24+
options?.onMockedConnection(connection)
2525

26-
// First, filter only those WebSocket handlers that
27-
// match the "ws.link()" endpoint predicate. Don't dispatch
28-
// anything yet so the logger can be attached to the connection
29-
// before it potentially sends events.
30-
const matchingHandlers: Array<WebSocketHandler> = []
26+
await Promise.all(
27+
handlers.map((handler) => {
28+
// Iterate over the handlers and forward the connection
29+
// event to WebSocket event handlers. This is equivalent
30+
// to dispatching that event onto multiple listeners.
31+
return handler.run(connection)
32+
}),
33+
)
3134

32-
for (const handler of handlers) {
33-
if (
34-
isHandlerKind('EventHandler')(handler) &&
35-
handler.predicate({
36-
event: connectionEvent,
37-
parsedResult: handler.parse({
38-
event: connectionEvent,
39-
}),
40-
})
41-
) {
42-
matchingHandlers.push(handler)
43-
}
35+
return
4436
}
4537

46-
if (matchingHandlers.length > 0) {
47-
options?.onMockedConnection(connection)
48-
49-
// Iterate over the handlers and forward the connection
50-
// event to WebSocket event handlers. This is equivalent
51-
// to dispatching that event onto multiple listeners.
52-
for (const handler of matchingHandlers) {
53-
handler[kDispatchEvent](connectionEvent)
54-
}
55-
} else {
56-
// Construct a request representing this WebSocket connection.
57-
const request = new Request(connection.client.url, {
58-
headers: {
59-
upgrade: 'websocket',
60-
connection: 'upgrade',
61-
},
62-
})
63-
await onUnhandledRequest(
64-
request,
65-
options.getUnhandledRequestStrategy(),
66-
).catch((error) => {
67-
const errorEvent = new Event('error')
68-
Object.defineProperty(errorEvent, 'cause', {
69-
enumerable: true,
70-
configurable: false,
71-
value: error,
72-
})
73-
connection.client.socket.dispatchEvent(errorEvent)
38+
// Construct a request representing this WebSocket connection.
39+
const request = new Request(connection.client.url, {
40+
headers: {
41+
upgrade: 'websocket',
42+
connection: 'upgrade',
43+
},
44+
})
45+
await onUnhandledRequest(
46+
request,
47+
options.getUnhandledRequestStrategy(),
48+
).catch((error) => {
49+
const errorEvent = new Event('error')
50+
Object.defineProperty(errorEvent, 'cause', {
51+
enumerable: true,
52+
configurable: false,
53+
value: error,
7454
})
55+
connection.client.socket.dispatchEvent(errorEvent)
56+
})
7557

76-
options?.onPassthroughConnection(connection)
58+
options?.onPassthroughConnection(connection)
7759

78-
// If none of the "ws" handlers matched,
79-
// establish the WebSocket connection as-is.
80-
connection.server.connect()
81-
}
60+
// If none of the "ws" handlers matched,
61+
// establish the WebSocket connection as-is.
62+
connection.server.connect()
8263
})
8364
}

src/core/ws/utils/getMessageLength.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ export function getMessageLength(data: WebSocketData): number {
1515
return data.byteLength
1616
}
1717

18-
return new Blob([data]).size
18+
return new Blob([data as any]).size
1919
}

src/core/ws/utils/getPublicData.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export async function getPublicData(data: WebSocketData): Promise<string> {
99

1010
// Handle all ArrayBuffer-like objects.
1111
if (typeof data === 'object' && 'byteLength' in data) {
12-
const text = new TextDecoder().decode(data)
12+
const text = new TextDecoder().decode(data as ArrayBuffer)
1313
return `ArrayBuffer(${truncateMessage(text)})`
1414
}
1515

test/node/ws-api/ws.server.connect.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ it('throws an error when connecting to a non-existing server', async () => {
8686
)
8787

8888
const errorListener = vi.fn()
89-
const ws = new WebSocket('wss://localhost:9876')
89+
const ws = new WebSocket('ws://localhost:9876')
9090
ws.onerror = errorListener
9191

9292
await vi.waitFor(() => {

0 commit comments

Comments
 (0)