Security News
PyPI Introduces Digital Attestations to Strengthen Python Package Security
PyPI now supports digital attestations, enhancing security and trust by allowing package maintainers to verify the authenticity of Python packages.
@mswjs/interceptors
Advanced tools
The @mswjs/interceptors package is a library for intercepting and mutating outgoing HTTP/HTTPS requests and WebSocket connections. It is primarily used for testing purposes, allowing developers to create mock servers and intercept network requests to return custom responses without having to alter the actual network infrastructure.
Intercepting HTTP/HTTPS requests
This feature allows you to intercept outgoing HTTP/HTTPS requests and return custom responses. The code sample demonstrates how to set up an interceptor for HTTP requests and provide a custom response with a mocked JSON body.
const { createInterceptor } = require('@mswjs/interceptors');
const { interceptClientRequest } = createInterceptor({
modules: [require('@mswjs/interceptors/lib/interceptors/http')],
resolver(request) {
return {
status: 200,
body: JSON.stringify({ mocked: true }),
};
},
});
interceptClientRequest();
Intercepting WebSocket connections
This feature enables the interception of WebSocket connections, allowing you to monitor or mock WebSocket events. The code sample shows how to set up an interceptor for WebSocket connections and log the intercepted events.
const { createInterceptor } = require('@mswjs/interceptors');
const { interceptClientRequest } = createInterceptor({
modules: [require('@mswjs/interceptors/lib/interceptors/ws')],
resolver(event) {
console.log('Intercepted WebSocket:', event);
},
});
interceptClientRequest();
Nock is a popular HTTP server mocking and expectations library for Node.js. It allows you to intercept HTTP requests and provide predefined responses. Compared to @mswjs/interceptors, nock is focused solely on HTTP/HTTPS and does not handle WebSocket connections.
Sinon is a testing library that provides standalone test spies, stubs, and mocks for JavaScript. It includes the ability to fake XMLHttpRequest and server responses, which is similar to the HTTP interception feature of @mswjs/interceptors. However, Sinon's focus is broader, encompassing various aspects of test doubles, not just network interception.
Mock-socket is a library that mocks WebSockets for front-end testing. It allows you to test WebSocket connections without an actual server. While @mswjs/interceptors can intercept WebSocket connections, mock-socket specializes in providing a WebSocket environment for testing purposes.
@mswjs/interceptors
Low-level HTTP/HTTPS/XHR/fetch request interception library.
Intercepts any requests issued by:
http.get
/http.request
https.get
/https.request
XMLHttpRequest
fetch
request
, node-fetch
, supertest
, etc.)While there are a lot of network communication mocking libraries, they tend to use request interception as an implementation detail, giving you a high-level API that includes request matching, timeouts, retries, and so forth.
This library is a strip-to-bone implementation that provides as little abstraction as possible to execute arbitrary logic upon any request. It's primarily designed as an underlying component for high-level API mocking solutions such as Mock Service Worker.
A traditional API mocking implementation in Node.js looks roughly like this:
import http from 'http'
function applyMock() {
// Store the original request module.
const originalHttpRequest = http.request
// Rewrite the request module entirely.
http.request = function (...args) {
// Decide whether to handle this request before
// the actual request happens.
if (shouldMock(args)) {
// If so, never create a request, respond to it
// using the mocked response from this blackbox.
return coerceToResponse.bind(this, mock)
}
// Otherwise, construct the original request
// and perform it as-is (receives the original response).
return originalHttpRequest(...args)
}
}
This library deviates from such implementation and uses class extensions instead of module rewrites. Such deviation is necessary because, unlike other solutions that include request matching and can determine whether to mock requests before they actually happen, this library is not opinionated about the mocked/bypassed nature of the requests. Instead, it intercepts all requests and delegates the decision of mocking to the end consumer.
class NodeClientRequest extends ClientRequest {
async end(...args) {
// Check if there's a mocked response for this request.
// You control this in the "resolver" function.
const mockedResponse = await resolver(isomorphicRequest)
// If there is a mocked response, use it to respond to this
// request, finalizing it afterward as if it received that
// response from the actual server it connected to.
if (mockedResponse) {
this.respondWith(mockedResponse)
this.finish()
return
}
// Otherwise, perform the original "ClientRequest.prototype.end" call.
return super.end(...args)
}
}
By extending the native modules, this library actually constructs requests as soon as they are constructed by the consumer. This enables all the request input validation and transformations done natively by Node.js—something that traditional solutions simply cannot do (they replace http.ClientRequest
entirely). The class extension allows to fully utilize Node.js internals instead of polyfilling them, which results in more resilient mocks.
This library extends (or patches, where applicable) the following native modules:
http.get
/http.request
https.get
/https.request
XMLHttpRequest
fetch
Once extended, it intercepts and normalizes all requests to the isomorphic request instances. The isomorphic request is an abstract representation of the request coming from different sources (ClientRequest
, XMLHttpRequest
, window.Request
, etc.) that allows us to handle such requests in the same, unified manner.
You can respond to an isomorphic request using an isomorphic response. In a similar way, the isomorphic response is a representation of the response to use for different requests. Responding to requests differs substantially when using modules like http
or XMLHttpRequest
. This library takes the responsibility for coercing isomorphic responses into appropriate responses depending on the request module automatically.
npm install @mswjs/interceptors
There are multiple individual interceptors exported from this library:
ClientRequestInterceptor
XMLHttpRequestInterceptor
FetchInterceptor
All aforementioned interceptors implement the same HTTP request interception contract, meaning that they allow you to handle intercepted requests in the same way, regardless of the request origin (http
/XMLHttpRequest
/fetch
).
To use multiple interceptors at once, consider BatchInterceptor
.
import { ClientRequestInterceptor } from '@mswjs/interceptors/lib/interceptors/ClientRequest'
const interceptor = new ClientRequestInterceptor()
interceptor.on('request', (request) => {
// Introspect request or mock its response
// via "request.respondWith()".
})
BatchInterceptor
Applies multiple request interceptors at the same time.
import { BatchInterceptor } from '@mswjs/interceptors'
import nodeInterceptors from '@mswjs/interceptors/lib/presets/node'
const interceptor = BatchInterceptor({
name: 'my-interceptor',
interceptors: nodeInterceptors,
})
interceptor.on('request', (request) => {
// Inspect the intercepted "request".
// Optionally, return a mocked response.
})
Using the
/presets/node
interceptors preset is the recommended way to ensure all requests get intercepted, regardless of their origin.
RemoteHttpInterceptor
Enables request interception in the current process while delegating the response resolution logic to the parent process. Requires the current process to be a child process. Requires the parent process to establish a resolver by calling the createRemoteResolver
function.
// child.js
import { RemoteHttpInterceptor } from '@mswjs/interceptors/lib/RemoteHttpInterceptor'
import { ClientRequestInterceptor } from '@mswjs/interceptors/lib/interceptors/ClientRequest'
const interceptor = new RemoteHttpInterceptor({
// Alternatively, you can use presets.
interceptors: [new ClientRequestInterceptor()],
})
interceptor.apply()
process.on('disconnect', () => {
interceptor.dispose()
})
You can still listen to and handle any requests in the child process via the request
event listener. Keep in mind that a single request can only be responded to once.
RemoteHttpResolver
Resolves an intercepted request in the given child process
. Requires for that child process to enable request interception by calling the createRemoteInterceptor
function.
// parent.js
import { spawn } from 'child_process'
import { RemoteHttpResolver } from '@mswjs/interceptors/lib/RemoteHttpInterceptor'
const appProcess = spawn('node', ['app.js'], {
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
})
const resolver = new RemoteHttpResolver({
process: appProcess,
})
resolver.on('request', (request) => {
// Optionally, return a mocked response
// for a request that occurred in the "appProcess".
})
apply
Applies interceptor, enabling the interception of requests in the current process.
interceptor.apply()
The same interceptor can be applied multiple times. If that happens, each subsequent interceptor instance will reusing a single running instance instead of applying itself repeatedly. Each interceptor instance should still be disposed individually.
on
Listens to the interceptor events.
Each interceptor decides what event map to implement. Currently, all exported interceptors implement an HTTP request event map that consists of the following events:
request
, signals when a new request happens;response
, signals when a response was sent.interceptor.on('request', (request) => {
console.log('[%s] %s', request.method, request.url.toString())
})
interceptor.on('response', (request, response) => {
console.log(
'Received response to [%s] %s:',
request.method,
request.url.href,
response
)
})
dispose
Disposes of the applied interceptor. This cleans up all the side-effects introduced by the interceptor (i.e. restores augmented modules).
interceptor.dispose()
The following libraries were used as an inspiration to write this low-level API:
FAQs
Low-level HTTP/HTTPS/XHR/fetch request interception library.
We found that @mswjs/interceptors demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
PyPI now supports digital attestations, enhancing security and trust by allowing package maintainers to verify the authenticity of Python packages.
Security News
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.