interceptors icon indicating copy to clipboard operation
interceptors copied to clipboard

Support raw `net.connection`

Open kettanaito opened this issue 10 months ago • 4 comments

  • https://github.com/mswjs/msw/issues/2165
  • https://github.com/mswjs/msw/issues/2409

Since https://github.com/mswjs/interceptors/releases/tag/v0.32.0, we now technically can implement a net.Socket interceptor that would allow us to intercept ClientRequest but also any agents using sockets directly, like Undici.

This task would involve:

  • Implementing the net.Socket interceptor interface.
  • Refactoring ClientRequest (and potentially XHR) interceptors to rely on the Socket interceptor. These higher level interceptors would only concern themselves with parsing the socket packets and deciding whether a particular connection should be intercepted, then controlling it.
  • Design some sort of manager that would "lock" socket instances behind certain interceptors. This is to avoid issues when a single request can be handled by multiple interceptors (see #378). Once we bring the interception to the socket level, all requests will be covered by that interceptor and subsequent higher interceptors that extend it.

kettanaito avatar Feb 11 '25 15:02 kettanaito

@kettanaito For clarity, would this address the issues with msw not intercepting requests from undici, as you described in this comment?

Undici is a custom client that doesn't rely on the node:http module, which is a primary request-issuing module which MSW observes in Node.js (as of now). Because of that, the requests Undici makes are invisible to MSW (I believe Undici uses net.Socket directly).

gotgenes avatar May 28 '25 15:05 gotgenes

Yes, it would. Undici uses net.connect() to establish network connections. Introducing a raw net-level interception would capture manual Undici requests, too.

That being said, this is a rather huge change. It would require us to refactor the entire API of interceptors as now each interceptor could have its own parser:

  1. Intercept on net level, route socket packets to the interceptor;
  2. Interceptor uses a parser (e.g. HTTP parser), decides if this is the data it's meant to intercept;
  3. Interceptor proceeds with parsing the data and notifying the consumer.

I don't see this feature being shipped any time this year.

kettanaito avatar May 29 '25 11:05 kettanaito

@kettanaito Thanks for your reply. Something I still feel confused about is that the fetch implementation in Node is supposed to be code from the undici package, bundled into Node distributions. You pointed out in #2165:

Also please note that MSW supports global fetch in Node.js still. That is also, technically, Undici. It just has to be a global fetch function. It's important to disambiguate that we aren't talking about global fetch but about a standalone usage of Undici, like their request function.

What I observe is, if I try to use undici.fetch with msw, msw will not intercept the requests and respond. But for both Node v22 and v24, using the built-in fetch ("global fetch"), msw will intercept the requests. I don't yet understand why, though. How does the built-in fetch still end up using a layer msw can intercept, even though it's from a version of undici?

gotgenes avatar Jun 02 '25 19:06 gotgenes

That difference is because in Node.js the global fetch uses Undici. Interceptors applies itself to a global fetch specifically, not to its underlying implementation.

This will work:

import { fetch } from 'undici'

globalThis.fetch = fetch

// ...intercept
fetch()

This, however, will not:

import { fetch } from 'undici'

// ...intercept
fetch()

For the reasons I've described above.

kettanaito avatar Jun 02 '25 21:06 kettanaito