controller.errorWith ends up in the response path if the error is not Error instance
interceptors expects the error to be from type Error in handleRequest, which is not required by Node.
const http = require('http')
const { ClientRequestInterceptor } = require('@mswjs/interceptors/ClientRequest')
const interceptor = new ClientRequestInterceptor()
interceptor.on('request', async function rootListener({ controller }) {
controller.errorWith({ message: 'custom error', code: 'test' })
})
// interceptor.apply()
const req = http.request('http://example.com')
req.on('response', () => {
console.log('response');
})
req.on('error', e => {
console.log('error', e);
})
req.end()
If the interceptor is applied, the program prints response and throws:
/.../nock/node_modules/@mswjs/interceptors/lib/node/chunk-WTJL7BRV.js:234
if (!Reflect.has(headers, kRawHeaders)) {
^
TypeError: Reflect.has called on non-object
at Reflect.has (<anonymous>)
at getRawFetchHeaders (/home/michael/projects/js/nock/node_modules/@mswjs/interceptors/lib/node/chunk-WTJL7BRV.js:234:16)
at MockHttpSocket.respondWith (/home/michael/projects/js/nock/node_modules/@mswjs/interceptors/lib/node/chunk-WTJL7BRV.js:488:32)
at Object.onResponse (/home/michael/projects/js/nock/node_modules/@mswjs/interceptors/lib/node/chunk-WTJL7BRV.js:901:18)
at handleResponse (/home/michael/projects/js/nock/node_modules/@mswjs/interceptors/lib/node/chunk-TQD7SQGP.js:100:21)
at handleRequest (/home/michael/projects/js/nock/node_modules/@mswjs/interceptors/lib/node/chunk-TQD7SQGP.js:186:12)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async _ClientRequestInterceptor.onRequest (/home/michael/projects/js/nock/node_modules/@mswjs/interceptors/lib/node/chunk-WTJL7BRV.js:895:32)
Node.js v20.17.0
For real requests, it is called the error callback.
WDYT?
I have the same issue currently when using @mswjs/interceptors through nock. I am currently unable to produce a reproducible example, so it is very difficult to investigate this.
Does anyone have an idea for a workaround until this is fixed? @mikicho maybe?
@marikaner In order to keep this issue clean, I opened an issue in nock to track this and discuss about workaround (https://github.com/nock/nock/issues/2789)
I think it should be safe to allow arbitrary error reasons. .destroy() isn't strict about this, as you've mentioned, and some other clients don't support custom error reasons at all.
The only thing, I'd be careful with the handleRequest as it tackles a bunch of scenarios and some of them are not that apparent. But all are covered with tests so any change will be verified.
I'd probably suggest to refactor handleRequest to have two internal callbacks: onResolve and onReject that are called whenever something is returned via .respondWith() or something throws during the request resolution (no matter if via throw or .errorWith()). Basically, to streamline the request handling to two paths:
- Something resolved this request to X;
- Something rejected this request with Y.
The current handleResponse and handleResponseError are too vague. Besides, there are situations when thrown errors are responses and get handled as response, which is confusing to read:
https://github.com/mswjs/interceptors/blob/472bcbe4b6b7f0652da10eb647a3aaa898ad4f7c/src/utils/handleRequest.ts#L74-L77
We can likely keep the existing handleResponse/handleResponseError, maybe rename them, and use them within the newly added onResolve and onReject callbacks.
@mikicho, would you like to give this one a try?
Sure! I'd love to contribute to this one... I'm looking into it.
@kettanaito I need clarification on what you have in mind. How is it different from what we currently have?
export async function handleRequest(options) {
const handleResponse..
const handleResomposeError..
...
const result = await until...
if (result.error)... // a.k.a onReject
if (result.data)... // a.ka onResolve
}
Right now we have cases where rejections can be translated to successful response handling:
https://github.com/mswjs/interceptors/blob/7e62e17cb5c0d96afcd10aa5420cac78892adff9/src/utils/handleRequest.ts#L76
And successful responses translated to errors:
https://github.com/mswjs/interceptors/blob/7e62e17cb5c0d96afcd10aa5420cac78892adff9/src/utils/handleRequest.ts#L47-L53
This all is intended but reading it is a bit confusing. So I proposed to have higher-level onResolve and onReject callbacks within the handleRequest function to make it easier to read. It's much easier to understand "onResolve triggered with an error, so treat it as an error" than "successful response callback triggered with an error, so it's an error". Do you see my line of thinking?
Released: v0.38.4 🎉
This has been released in v0.38.4!
Make sure to always update to the latest version (npm i @mswjs/interceptors@latest) to get the newest features and bug fixes.
Predictable release automation by @ossjs/release.