Skip to content

Commit

Permalink
feat: use random internal server port
Browse files Browse the repository at this point in the history
  • Loading branch information
kettanaito committed Dec 18, 2024
1 parent cfc120c commit 310a2c0
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 218 deletions.
8 changes: 4 additions & 4 deletions src/core/handlers/RemoteRequestHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ export class RemoteRequestHandler extends RequestHandler<
RemoteRequestHandlerResolverExtras
> {
protected remoteClient: RemoteClient
protected contextId?: string
protected boundaryId: string

constructor(
readonly args: {
remoteClient: RemoteClient
contextId?: string
boundaryId: string
},
) {
super({
Expand All @@ -37,7 +37,7 @@ export class RemoteRequestHandler extends RequestHandler<
})

this.remoteClient = args.remoteClient
this.contextId = args.contextId
this.boundaryId = args.boundaryId
}

async parse(args: {
Expand All @@ -60,7 +60,7 @@ export class RemoteRequestHandler extends RequestHandler<
* parsing phase since that's the only async phase before predicate.
*/
const response = await this.remoteClient.handleRequest({
contextId: this.contextId,
boundaryId: this.boundaryId,
requestId: createRequestId(),
request: args.request,
})
Expand Down
31 changes: 9 additions & 22 deletions src/node/SetupServerApi.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { AsyncLocalStorage } from 'node:async_hooks'
import { invariant } from 'outvariant'
import { ClientRequestInterceptor } from '@mswjs/interceptors/ClientRequest'
import { XMLHttpRequestInterceptor } from '@mswjs/interceptors/XMLHttpRequest'
import { FetchInterceptor } from '@mswjs/interceptors/fetch'
Expand All @@ -8,10 +7,10 @@ import type { RequestHandler } from '~/core/handlers/RequestHandler'
import type { ListenOptions, SetupServer } from './glossary'
import type { WebSocketHandler } from '~/core/handlers/WebSocketHandler'
import { SetupServerCommonApi } from './SetupServerCommonApi'
import { MSW_REMOTE_SERVER_PORT, RemoteClient } from './setupRemoteServer'
import { RemoteClient } from './setupRemoteServer'
import { RemoteRequestHandler } from '~/core/handlers/RemoteRequestHandler'
import { shouldBypassRequest } from '~/core/utils/internal/requestUtils'
import { remoteContext } from './remoteContext'
import { getRemoteContextFromEnvironment } from './remoteContext'

const handlersStorage = new AsyncLocalStorage<RequestHandlersContext>()

Expand Down Expand Up @@ -110,22 +109,12 @@ export class SetupServerApi
// run it in a special "remote" mode. That mode ensures that
// an extraneous Node.js process can affect this process' traffic.
if (this.resolvedOptions.remote?.enabled) {
const remotePort =
typeof this.resolvedOptions.remote === 'object'
? this.resolvedOptions.remote.port || MSW_REMOTE_SERVER_PORT
: MSW_REMOTE_SERVER_PORT

invariant(
typeof remotePort === 'number',
'Failed to enable request interception: expected the "remotePort" option to be a valid port number, but got "%s". Make sure it is the same port number you provide as the "port" option to "remote.listen()" in your tests.',
remotePort,
)

const remoteClient = new RemoteClient({
port: remotePort,
})
// Get the remote context from the environment since `server.listen()`
// is called in a different process and cannot be wrapped in `remote.boundary()`.
const remoteContext = getRemoteContextFromEnvironment()
const remoteClient = new RemoteClient(remoteContext.serverUrl)

// Kick off connection to the server early.
// Kick off the connection to the remote server early.
const remoteConnectionPromise = remoteClient.connect().then(
() => {
this.handlersController.currentHandlers = new Proxy(
Expand All @@ -137,20 +126,18 @@ export class SetupServerApi
remoteClient,
// Get the remote boundary context ID from the environment.
// This way, the user doesn't have to explicitly drill it here.
contextId: process.env[remoteContext.variableName],
boundaryId: remoteContext.boundary.id,
}),
Reflect.apply(target, thisArg, args),
)
},
},
)
},
// Ignore connection errors. Continue operation as normal.
// The remote server is not required for `setupServer` to work.
() => {
// eslint-disable-next-line no-console
console.error(
`Failed to connect to a remote server at port "${remotePort}"`,
`Failed to connect to a remote server at "${remoteContext.serverUrl}"`,
)
},
)
Expand Down
7 changes: 0 additions & 7 deletions src/node/glossary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,6 @@ export interface ListenOptions extends SharedOptions {
*/
remote?: {
enabled: boolean

/**
* Custom port number to synchronize this `setupServer` with
* the remote `setupRemoteServer`.
* @default 56957
*/
port?: number
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ export {
SetupRemoteServerApi,
setupRemoteServer,
} from './setupRemoteServer'
export { remoteContext } from './remoteContext'
export { getRemoteEnvironment } from './remoteContext'
66 changes: 54 additions & 12 deletions src/node/remoteContext.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,58 @@
import { invariant } from 'outvariant'
import { remoteHandlersContext } from './setupRemoteServer'

export const remoteContext = {
variableName: 'MSW_REMOTE_CONTEXT_ID',
getContextId() {
const store = remoteHandlersContext.getStore()

invariant(
store != null,
'Failed to call ".getContextId()" on remote context: no context found. Did you call this outside of the `remote.boundary()` scope?',
)

return store.contextId
},
export const MSW_REMOTE_SERVER_URL = 'MSW_REMOTE_SERVER_URL'
export const MSW_REMOTE_BOUNDARY_ID = 'MSW_REMOTE_BOUNDARY_ID'

export interface RemoteContext {
serverUrl: URL
boundary: {
id: string
}
}

export function getRemoteContext(): RemoteContext {
const store = remoteHandlersContext.getStore()

invariant(
store,
'Failed to retrieve remote context: no context found. Did you forget to call this within a `remote.boundary()`?',
)

return {
serverUrl: store.serverUrl,
boundary: {
id: store.boundaryId,
},
}
}

export function getRemoteContextFromEnvironment(): RemoteContext {
const serverUrl = process.env[MSW_REMOTE_SERVER_URL]
const boundaryId = process.env[MSW_REMOTE_BOUNDARY_ID]

invariant(
serverUrl,
'Failed to retrieve the remote context from environment: server URL is missing',
)
invariant(
boundaryId,
'Failed to retrieve the remote context from environment: boundary ID is missing',
)

return {
serverUrl: new URL(serverUrl),
boundary: {
id: boundaryId,
},
}
}

export function getRemoteEnvironment() {
const remoteContext = getRemoteContext()

return {
[MSW_REMOTE_SERVER_URL]: remoteContext.serverUrl.toString(),
[MSW_REMOTE_BOUNDARY_ID]: remoteContext.boundary.id,
}
}
Loading

0 comments on commit 310a2c0

Please sign in to comment.