Skip to content

Commit 28d26bd

Browse files
authored
fix: prevent instanceof handler check failures between different MSW versions (#2349)
1 parent 3135575 commit 28d26bd

File tree

10 files changed

+105
-22
lines changed

10 files changed

+105
-22
lines changed

src/browser/setupWorker/start/createFallbackRequestListener.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { XMLHttpRequestInterceptor } from '@mswjs/interceptors/XMLHttpRequest'
88
import { SetupWorkerInternalContext, StartOptions } from '../glossary'
99
import type { RequiredDeep } from '~/core/typeUtils'
1010
import { handleRequest } from '~/core/utils/handleRequest'
11+
import { isHandlerKind } from '~/core/utils/internal/isHandlerKind'
1112

1213
export function createFallbackRequestListener(
1314
context: SetupWorkerInternalContext,
@@ -24,7 +25,7 @@ export function createFallbackRequestListener(
2425
const response = await handleRequest(
2526
request,
2627
requestId,
27-
context.getRequestHandlers(),
28+
context.getRequestHandlers().filter(isHandlerKind('RequestHandler')),
2829
options,
2930
context.emitter,
3031
{

src/browser/setupWorker/start/createRequestListener.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@ import {
99
} from './utils/createMessageChannel'
1010
import { parseWorkerRequest } from '../../utils/parseWorkerRequest'
1111
import { RequestHandler } from '~/core/handlers/RequestHandler'
12-
import { HttpHandler } from '~/core/handlers/HttpHandler'
13-
import { GraphQLHandler } from '~/core/handlers/GraphQLHandler'
1412
import { handleRequest } from '~/core/utils/handleRequest'
1513
import { RequiredDeep } from '~/core/typeUtils'
1614
import { devUtils } from '~/core/utils/internal/devUtils'
1715
import { toResponseInit } from '~/core/utils/toResponseInit'
16+
import { isHandlerKind } from '~/core/utils/internal/isHandlerKind'
1817

1918
export const createRequestListener = (
2019
context: SetupWorkerInternalContext,
@@ -45,11 +44,7 @@ export const createRequestListener = (
4544
await handleRequest(
4645
request,
4746
requestId,
48-
context.getRequestHandlers().filter((handler) => {
49-
return (
50-
handler instanceof HttpHandler || handler instanceof GraphQLHandler
51-
)
52-
}),
47+
context.getRequestHandlers().filter(isHandlerKind('RequestHandler')),
5348
options,
5449
context.emitter,
5550
{

src/core/handlers/RequestHandler.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
import type { ResponseResolutionContext } from '../utils/executeHandlers'
88
import type { MaybePromise } from '../typeUtils'
99
import { StrictRequest, StrictResponse } from '..//HttpResponse'
10+
import type { HandlerKind } from './common'
1011

1112
export type DefaultRequestMultipartBody = Record<
1213
string,
@@ -117,6 +118,8 @@ export abstract class RequestHandler<
117118
StrictRequest<DefaultBodyType>
118119
>()
119120

121+
private readonly __kind: HandlerKind
122+
120123
public info: HandlerInfo & RequestHandlerInternalInfo
121124
/**
122125
* Indicates whether this request handler has been used
@@ -151,6 +154,7 @@ export abstract class RequestHandler<
151154
}
152155

153156
this.isUsed = false
157+
this.__kind = 'RequestHandler'
154158
}
155159

156160
/**

src/core/handlers/WebSocketHandler.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
matchRequestUrl,
99
} from '../utils/matching/matchRequestUrl'
1010
import { getCallFrame } from '../utils/internal/getCallFrame'
11+
import type { HandlerKind } from './common'
1112

1213
type WebSocketHandlerParsedResult = {
1314
match: Match
@@ -28,6 +29,8 @@ const kStopPropagationPatched = Symbol('kStopPropagationPatched')
2829
const KOnStopPropagation = Symbol('KOnStopPropagation')
2930

3031
export class WebSocketHandler {
32+
private readonly __kind: HandlerKind
33+
3134
public id: string
3235
public callFrame?: string
3336

@@ -38,6 +41,7 @@ export class WebSocketHandler {
3841

3942
this[kEmitter] = new Emitter()
4043
this.callFrame = getCallFrame(new Error())
44+
this.__kind = 'EventHandler'
4145
}
4246

4347
public parse(args: {

src/core/handlers/common.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type HandlerKind = 'RequestHandler' | 'EventHandler'

src/core/utils/executeHandlers.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export interface ResponseResolutionContext {
1818
* Returns the execution result object containing any matching request
1919
* handler and any mocked response it returned.
2020
*/
21-
export const executeHandlers = async <Handlers extends Array<unknown>>({
21+
export const executeHandlers = async <Handlers extends Array<RequestHandler>>({
2222
request,
2323
requestId,
2424
handlers,
@@ -33,10 +33,6 @@ export const executeHandlers = async <Handlers extends Array<unknown>>({
3333
let result: RequestHandlerExecutionResult<any> | null = null
3434

3535
for (const handler of handlers) {
36-
if (!(handler instanceof RequestHandler)) {
37-
continue
38-
}
39-
4036
result = await handler.run({ request, requestId, resolutionContext })
4137

4238
// If the handler produces some result for this request,
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { GraphQLHandler } from '../../handlers/GraphQLHandler'
2+
import { HttpHandler } from '../../handlers/HttpHandler'
3+
import { RequestHandler } from '../../handlers/RequestHandler'
4+
import { WebSocketHandler } from '../../handlers/WebSocketHandler'
5+
import { isHandlerKind } from './isHandlerKind'
6+
7+
it('returns true if expected a request handler and given a request handler', () => {
8+
expect(
9+
isHandlerKind('RequestHandler')(new HttpHandler('*', '*', () => {})),
10+
).toBe(true)
11+
12+
expect(
13+
isHandlerKind('RequestHandler')(
14+
new GraphQLHandler('all', '*', '*', () => {}),
15+
),
16+
).toBe(true)
17+
})
18+
19+
it('returns true if expected a request handler and given a custom request handler', () => {
20+
class MyHandler extends RequestHandler {
21+
constructor() {
22+
super({ info: { header: '*' }, resolver: () => {} })
23+
}
24+
predicate = () => false
25+
log() {}
26+
}
27+
28+
expect(isHandlerKind('RequestHandler')(new MyHandler())).toBe(true)
29+
})
30+
31+
it('returns false if expected a request handler but given event handler', () => {
32+
expect(isHandlerKind('RequestHandler')(new WebSocketHandler('*'))).toBe(false)
33+
})
34+
35+
it('returns false if expected a request handler but given arbitrary object', () => {
36+
expect(isHandlerKind('RequestHandler')(undefined)).toBe(false)
37+
expect(isHandlerKind('RequestHandler')(null)).toBe(false)
38+
expect(isHandlerKind('RequestHandler')({})).toBe(false)
39+
expect(isHandlerKind('RequestHandler')([])).toBe(false)
40+
expect(isHandlerKind('RequestHandler')(123)).toBe(false)
41+
expect(isHandlerKind('RequestHandler')('hello')).toBe(false)
42+
})
43+
44+
it('returns true if expected an event handler and given an event handler', () => {
45+
expect(isHandlerKind('EventHandler')(new WebSocketHandler('*'))).toBe(true)
46+
})
47+
48+
it('returns true if expected an event handler and given a custom event handler', () => {
49+
class MyEventHandler extends WebSocketHandler {
50+
constructor() {
51+
super('*')
52+
}
53+
}
54+
expect(isHandlerKind('EventHandler')(new MyEventHandler())).toBe(true)
55+
})
56+
57+
it('returns false if expected an event handler but given arbitrary object', () => {
58+
expect(isHandlerKind('EventHandler')(undefined)).toBe(false)
59+
expect(isHandlerKind('EventHandler')(null)).toBe(false)
60+
expect(isHandlerKind('EventHandler')({})).toBe(false)
61+
expect(isHandlerKind('EventHandler')([])).toBe(false)
62+
expect(isHandlerKind('EventHandler')(123)).toBe(false)
63+
expect(isHandlerKind('EventHandler')('hello')).toBe(false)
64+
})
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { HandlerKind } from '../../handlers/common'
2+
import type { RequestHandler } from '../../handlers/RequestHandler'
3+
import type { WebSocketHandler } from '../../handlers/WebSocketHandler'
4+
5+
/**
6+
* A filter function that ensures that the provided argument
7+
* is a handler of the given kind. This helps differentiate
8+
* between different kinds of handlers, e.g. request and event handlers.
9+
*/
10+
export function isHandlerKind<K extends HandlerKind>(kind: K) {
11+
return (
12+
input: unknown,
13+
): input is K extends 'EventHandler' ? WebSocketHandler : RequestHandler => {
14+
return (
15+
input != null &&
16+
typeof input === 'object' &&
17+
'__kind' in input &&
18+
input.__kind === kind
19+
)
20+
}
21+
}

src/core/ws/handleWebSocketEvent.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
onUnhandledRequest,
77
UnhandledRequestStrategy,
88
} from '../utils/request/onUnhandledRequest'
9+
import { isHandlerKind } from '../utils/internal/isHandlerKind'
910

1011
interface HandleWebSocketEventOptions {
1112
getUnhandledRequestStrategy: () => UnhandledRequestStrategy
@@ -30,7 +31,7 @@ export function handleWebSocketEvent(options: HandleWebSocketEventOptions) {
3031

3132
for (const handler of handlers) {
3233
if (
33-
handler instanceof WebSocketHandler &&
34+
isHandlerKind('EventHandler')(handler) &&
3435
handler.predicate({
3536
event: connectionEvent,
3637
parsedResult: handler.parse({

src/node/SetupServerCommonApi.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@ import type { LifeCycleEventsMap, SharedOptions } from '~/core/sharedOptions'
1414
import { SetupApi } from '~/core/SetupApi'
1515
import { handleRequest } from '~/core/utils/handleRequest'
1616
import type { RequestHandler } from '~/core/handlers/RequestHandler'
17-
import { HttpHandler } from '~/core/handlers/HttpHandler'
18-
import { GraphQLHandler } from '~/core/handlers/GraphQLHandler'
1917
import type { WebSocketHandler } from '~/core/handlers/WebSocketHandler'
2018
import { mergeRight } from '~/core/utils/internal/mergeRight'
2119
import { InternalError, devUtils } from '~/core/utils/internal/devUtils'
2220
import type { SetupServerCommon } from './glossary'
2321
import { handleWebSocketEvent } from '~/core/ws/handleWebSocketEvent'
2422
import { webSocketInterceptor } from '~/core/ws/webSocketInterceptor'
23+
import { isHandlerKind } from '~/core/utils/internal/isHandlerKind'
2524

2625
export const DEFAULT_LISTEN_OPTIONS: RequiredDeep<SharedOptions> = {
2726
onUnhandledRequest: 'warn',
@@ -63,12 +62,9 @@ export class SetupServerCommonApi
6362
const response = await handleRequest(
6463
request,
6564
requestId,
66-
this.handlersController.currentHandlers().filter((handler) => {
67-
return (
68-
handler instanceof HttpHandler ||
69-
handler instanceof GraphQLHandler
70-
)
71-
}),
65+
this.handlersController
66+
.currentHandlers()
67+
.filter(isHandlerKind('RequestHandler')),
7268
this.resolvedOptions,
7369
this.emitter,
7470
)

0 commit comments

Comments
 (0)