Skip to content

Commit

Permalink
fix(setupServer): set max listeners on the "request.signal" (#1765)
Browse files Browse the repository at this point in the history
  • Loading branch information
kettanaito authored Oct 10, 2023
1 parent 078e541 commit 17d5f3a
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 3 deletions.
8 changes: 5 additions & 3 deletions src/core/handlers/RequestHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ export abstract class RequestHandler<
resolutionContext?: ResponseResolutionContext
}): Promise<boolean> {
const parsedResult = await this.parse({
request: args.request.clone(),
request: args.request,
resolutionContext: args.resolutionContext,
})

Expand Down Expand Up @@ -186,6 +186,8 @@ export abstract class RequestHandler<
return null
}

// Clone the request instance before it's passed to the handler phases
// and the response resolver so we can always read it for logging.
const mainRequestRef = args.request.clone()

// Immediately mark the handler as used.
Expand All @@ -194,11 +196,11 @@ export abstract class RequestHandler<
this.isUsed = true

const parsedResult = await this.parse({
request: mainRequestRef.clone(),
request: args.request,
resolutionContext: args.resolutionContext,
})
const shouldInterceptRequest = this.predicate({
request: mainRequestRef.clone(),
request: args.request,
parsedResult,
resolutionContext: args.resolutionContext,
})
Expand Down
13 changes: 13 additions & 0 deletions src/node/SetupServerApi.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { setMaxListeners, defaultMaxListeners } from 'node:events'
import { invariant } from 'outvariant'
import {
BatchInterceptor,
Expand Down Expand Up @@ -50,6 +51,18 @@ export class SetupServerApi
*/
private init(): void {
this.interceptor.on('request', async ({ request, requestId }) => {
// Bump the maximum number of event listeners on the
// request's "AbortSignal". This prepares the request
// for each request handler cloning it at least once.
// Note that cloning a request automatically appends a
// new "abort" event listener to the parent request's
// "AbortController" so if the parent aborts, all the
// clones are automatically aborted.
setMaxListeners(
Math.max(defaultMaxListeners, this.currentHandlers.length),
request.signal,
)

const response = await handleRequest(
request,
requestId,
Expand Down
58 changes: 58 additions & 0 deletions test/node/msw-api/many-request-handlers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* @jest-environment node
*/
import { graphql, http, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'

// Create a large number of request handlers.
const restHandlers = new Array(100).fill(null).map((_, index) => {
return http.post(
`https://example.com/resource/${index}`,
async ({ request }) => {
const text = await request.text()
return HttpResponse.text(text + index.toString())
},
)
})

const graphqlHanlers = new Array(100).fill(null).map((_, index) => {
return graphql.query(`Get${index}`, () => {
return HttpResponse.json({ data: { index } })
})
})

const server = setupServer(...restHandlers, ...graphqlHanlers)

beforeAll(() => {
server.listen()
jest.spyOn(process.stderr, 'write')
})

afterAll(() => {
server.close()
jest.restoreAllMocks()
})

it('does not print a memory leak warning when having many request handlers', async () => {
const httpResponse = await fetch('https://example.com/resource/42', {
method: 'POST',
body: 'request-body-',
}).then((response) => response.text())

const graphqlResponse = await fetch('https://example.com', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: `query Get42 { index }`,
}),
}).then((response) => response.json())

// Must not print any memory leak warnings.
expect(process.stderr.write).not.toHaveBeenCalled()

// Must return the mocked response.
expect(httpResponse).toBe('request-body-42')
expect(graphqlResponse).toEqual({ data: { index: 42 } })
})

0 comments on commit 17d5f3a

Please sign in to comment.