fix: properly handle Unix socket paths in interceptors
Fix Unix socket path handling in MSW Interceptors
Problem
The MSW Interceptors library had an issue with TCP requests bound to Unix sockets. When making requests to Unix socket paths (via the socketPath option), the interceptor was incorrectly trying to establish a TCP connection to localhost:80 rather than using the provided socket path. This prevented proper interception and mocking of requests to Unix sockets.
This issue is particularly important for:
- Applications using Docker API (which often communicates through
/var/run/docker.sock) - TestContainers and other containerization tools that leverage Unix sockets
- Database connections that use Unix sockets (e.g., MySQL, PostgreSQL)
- Custom IPC mechanisms based on Unix sockets
Solution
The solution involves a comprehensive approach to properly handle Unix socket paths throughout the request interceptor chain:
-
Modified URL creation for Unix sockets:
- Updated
getUrlByRequestOptions.tsto create URLs with a special hostname"unix-socket-placeholder"for Unix socket requests - This prevents the library from attempting TCP connections to
localhost
- Updated
-
Enhanced socket connection handling:
- Updated
MockHttpSocket.tsto directly create socket connections usingnet.createConnection({ path: socketPath })when a socket path is specified - This bypasses the hostname/port mechanism for Unix socket connections
- Updated
-
Improved agent configuration:
- Modified
MockAgentandMockHttpsAgentclasses to properly handle and preserve thesocketPathproperty - Added support in
baseUrlFromConnectionOptions.tsto use the same special hostname for consistency
- Modified
-
Code organization improvements:
- Refactored
ClientRequest/index.tsto extract duplicate agent creation code into a helper method - Ensured the
socketPathproperty is properly preserved throughout the request handling chain
- Refactored
-
Comprehensive test coverage:
- Added regression tests verifying Unix socket connections work correctly
- Added unit tests for
normalizeClientRequestArgs.tsto verifysocketPathpreservation - Added tests for
getUrlByRequestOptions.tsto verify special hostname URL creation
Testing
The fix has been thoroughly tested with:
- Direct Unix socket connections to local test servers
- GET and POST requests with various headers and body content
- Error handling for non-existent sockets
- Integration with the full interception pipeline
All tests are now passing, confirming that Unix socket connections are properly handled.
Related Issues
This fix addresses issues encountered when working with applications that leverage Unix sockets for communication, particularly in containerized environments or when interacting with system services.
- https://github.com/mswjs/msw/issues/1600
- https://github.com/nock/nock/issues/2839
Hi @kettanaito, were you able to take a look at this by any chance? Or @msutkowski, @marcosvega91, @timdeschryver
I'm wondering what the expected behavior is from this lib POV. Should we intercept them or automatically pass through?
I'm wondering what the expected behavior is from this lib POV. Should we intercept them or automatically pass through?
That's a great question @mikicho. Could this introduce any vulnerabilities if requests were intercepted? I see no harm in passing them through. I recognize some use cases for intercepting them, but they should probably be rarely explored, and I'm not sure if any security issues might arise.
Sorry, I didn't have the time to look into this. Will do my best to review this this month.
@mikicho, correct me if I'm wrong, but socketPath seems like another way to construct a request. As a result, it performs a regular request that must be intercepted and allowed to be mocked like any other request.
@kettanaito I'm not familiar with this either. From what I understand, it is a Unix way to communicate between processes.
From a quick look, it seems like Node translates/overrides it to path, but I may be missing something.
@kettanaito @vinialbano I think the root cause is that we create a custom agent and then override the path property, which determines the type of the request (pipe/tcp), with null. It should be the socket path in order to create a pipe connection.
When I disable it or send agent: false in the options, it works like a charm:
const request = http.get({
socketPath,
path: '/test-get',
agent: false, // Disable the default agent
})
@kettanaito I believe this logic is outdated and no longer applicable to the socket-level interceptor we are currently using. After running a quick check, I noticed that all the tests still passed when I commented out these lines. We may want to remove it.
@mikicho, I think you are right regarding the default agent. Nice find!
Do we need any additional changes to support Unix socket paths after #731?
Update
I've moved the tests introduced in this pull request to #751, where they are passing without any additional changes. It looks like #731 has indeed fixed this issue.
Interestingly enough, we already had Unix socket tests at test/modules/http/compliance/http-unix-socket.test.ts and they were always passing. I wonder if this issue addresses something that the suggested tests does not cover. cc @vinialbano
Since the tests are passing, I'm consider this issue non-reproducible. If it still happens, please open a pull request with a failing test to confirm that. Thanks!
Released: v0.40.0 🎉
This has been released in v0.40.0!
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.