@depup/mswjs__interceptors 0.41.3-depup.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ClientRequest/package.json +11 -0
- package/LICENSE.md +9 -0
- package/README.md +31 -0
- package/RemoteHttpInterceptor/package.json +11 -0
- package/WebSocket/package.json +12 -0
- package/XMLHttpRequest/package.json +12 -0
- package/changes.json +10 -0
- package/fetch/package.json +12 -0
- package/lib/browser/Interceptor-Deczogc8.d.cts +65 -0
- package/lib/browser/Interceptor-gqKgs-aF.d.mts +65 -0
- package/lib/browser/XMLHttpRequest-BACqefB-.cjs +761 -0
- package/lib/browser/XMLHttpRequest-BACqefB-.cjs.map +1 -0
- package/lib/browser/XMLHttpRequest-BvxZV0WU.mjs +756 -0
- package/lib/browser/XMLHttpRequest-BvxZV0WU.mjs.map +1 -0
- package/lib/browser/bufferUtils-BiiO6HZv.mjs +20 -0
- package/lib/browser/bufferUtils-BiiO6HZv.mjs.map +1 -0
- package/lib/browser/bufferUtils-Uc0eRItL.cjs +38 -0
- package/lib/browser/bufferUtils-Uc0eRItL.cjs.map +1 -0
- package/lib/browser/createRequestId-Cs4oXfa1.cjs +205 -0
- package/lib/browser/createRequestId-Cs4oXfa1.cjs.map +1 -0
- package/lib/browser/createRequestId-DQcIlohW.mjs +170 -0
- package/lib/browser/createRequestId-DQcIlohW.mjs.map +1 -0
- package/lib/browser/fetch-DdKEdDOR.mjs +248 -0
- package/lib/browser/fetch-DdKEdDOR.mjs.map +1 -0
- package/lib/browser/fetch-U3v3Y4ap.cjs +253 -0
- package/lib/browser/fetch-U3v3Y4ap.cjs.map +1 -0
- package/lib/browser/getRawRequest-BTaNLFr0.mjs +218 -0
- package/lib/browser/getRawRequest-BTaNLFr0.mjs.map +1 -0
- package/lib/browser/getRawRequest-zx8rUJL2.cjs +259 -0
- package/lib/browser/getRawRequest-zx8rUJL2.cjs.map +1 -0
- package/lib/browser/glossary-BdLS4k1H.d.cts +70 -0
- package/lib/browser/glossary-DYwOrogs.d.mts +70 -0
- package/lib/browser/handleRequest-CvX2G-Lz.cjs +189 -0
- package/lib/browser/handleRequest-CvX2G-Lz.cjs.map +1 -0
- package/lib/browser/handleRequest-D7kpTI5U.mjs +178 -0
- package/lib/browser/handleRequest-D7kpTI5U.mjs.map +1 -0
- package/lib/browser/hasConfigurableGlobal-BvCTG97d.cjs +45 -0
- package/lib/browser/hasConfigurableGlobal-BvCTG97d.cjs.map +1 -0
- package/lib/browser/hasConfigurableGlobal-npXitu1-.mjs +33 -0
- package/lib/browser/hasConfigurableGlobal-npXitu1-.mjs.map +1 -0
- package/lib/browser/index.cjs +70 -0
- package/lib/browser/index.cjs.map +1 -0
- package/lib/browser/index.d.cts +96 -0
- package/lib/browser/index.d.mts +96 -0
- package/lib/browser/index.mjs +56 -0
- package/lib/browser/index.mjs.map +1 -0
- package/lib/browser/interceptors/WebSocket/index.cjs +622 -0
- package/lib/browser/interceptors/WebSocket/index.cjs.map +1 -0
- package/lib/browser/interceptors/WebSocket/index.d.cts +277 -0
- package/lib/browser/interceptors/WebSocket/index.d.mts +277 -0
- package/lib/browser/interceptors/WebSocket/index.mjs +615 -0
- package/lib/browser/interceptors/WebSocket/index.mjs.map +1 -0
- package/lib/browser/interceptors/XMLHttpRequest/index.cjs +7 -0
- package/lib/browser/interceptors/XMLHttpRequest/index.d.cts +15 -0
- package/lib/browser/interceptors/XMLHttpRequest/index.d.mts +15 -0
- package/lib/browser/interceptors/XMLHttpRequest/index.mjs +7 -0
- package/lib/browser/interceptors/fetch/index.cjs +6 -0
- package/lib/browser/interceptors/fetch/index.d.cts +13 -0
- package/lib/browser/interceptors/fetch/index.d.mts +13 -0
- package/lib/browser/interceptors/fetch/index.mjs +6 -0
- package/lib/browser/presets/browser.cjs +17 -0
- package/lib/browser/presets/browser.cjs.map +1 -0
- package/lib/browser/presets/browser.d.cts +12 -0
- package/lib/browser/presets/browser.d.mts +14 -0
- package/lib/browser/presets/browser.mjs +17 -0
- package/lib/browser/presets/browser.mjs.map +1 -0
- package/lib/browser/resolveWebSocketUrl-6K6EgqsA.cjs +31 -0
- package/lib/browser/resolveWebSocketUrl-6K6EgqsA.cjs.map +1 -0
- package/lib/browser/resolveWebSocketUrl-C83-x9iE.mjs +25 -0
- package/lib/browser/resolveWebSocketUrl-C83-x9iE.mjs.map +1 -0
- package/lib/node/BatchInterceptor-3LnAnLTx.cjs +49 -0
- package/lib/node/BatchInterceptor-3LnAnLTx.cjs.map +1 -0
- package/lib/node/BatchInterceptor-D7mXzHcQ.d.mts +26 -0
- package/lib/node/BatchInterceptor-DFaBPilf.mjs +44 -0
- package/lib/node/BatchInterceptor-DFaBPilf.mjs.map +1 -0
- package/lib/node/BatchInterceptor-D_YqR8qU.d.cts +26 -0
- package/lib/node/ClientRequest-2rDe54Ui.cjs +1043 -0
- package/lib/node/ClientRequest-2rDe54Ui.cjs.map +1 -0
- package/lib/node/ClientRequest-Ca8Qykuv.mjs +1034 -0
- package/lib/node/ClientRequest-Ca8Qykuv.mjs.map +1 -0
- package/lib/node/Interceptor-DEazpLJd.d.mts +133 -0
- package/lib/node/Interceptor-DJ2akVWI.d.cts +133 -0
- package/lib/node/RemoteHttpInterceptor.cjs +154 -0
- package/lib/node/RemoteHttpInterceptor.cjs.map +1 -0
- package/lib/node/RemoteHttpInterceptor.d.cts +39 -0
- package/lib/node/RemoteHttpInterceptor.d.mts +39 -0
- package/lib/node/RemoteHttpInterceptor.mjs +152 -0
- package/lib/node/RemoteHttpInterceptor.mjs.map +1 -0
- package/lib/node/XMLHttpRequest-B7kJdYYI.cjs +763 -0
- package/lib/node/XMLHttpRequest-B7kJdYYI.cjs.map +1 -0
- package/lib/node/XMLHttpRequest-C8dIZpds.mjs +757 -0
- package/lib/node/XMLHttpRequest-C8dIZpds.mjs.map +1 -0
- package/lib/node/bufferUtils-DiCTqG-7.cjs +38 -0
- package/lib/node/bufferUtils-DiCTqG-7.cjs.map +1 -0
- package/lib/node/bufferUtils-_8XfKIfX.mjs +20 -0
- package/lib/node/bufferUtils-_8XfKIfX.mjs.map +1 -0
- package/lib/node/chunk-CbDLau6x.cjs +34 -0
- package/lib/node/fetch-BmXpK10r.cjs +272 -0
- package/lib/node/fetch-BmXpK10r.cjs.map +1 -0
- package/lib/node/fetch-G1DVwDKG.mjs +265 -0
- package/lib/node/fetch-G1DVwDKG.mjs.map +1 -0
- package/lib/node/fetchUtils-BaY5iWXw.cjs +419 -0
- package/lib/node/fetchUtils-BaY5iWXw.cjs.map +1 -0
- package/lib/node/fetchUtils-CoU35g3M.mjs +359 -0
- package/lib/node/fetchUtils-CoU35g3M.mjs.map +1 -0
- package/lib/node/getRawRequest-BavnMWh_.cjs +36 -0
- package/lib/node/getRawRequest-BavnMWh_.cjs.map +1 -0
- package/lib/node/getRawRequest-DnwmXyOW.mjs +24 -0
- package/lib/node/getRawRequest-DnwmXyOW.mjs.map +1 -0
- package/lib/node/glossary-BLKRyLBd.cjs +12 -0
- package/lib/node/glossary-BLKRyLBd.cjs.map +1 -0
- package/lib/node/glossary-glQBRnVD.mjs +6 -0
- package/lib/node/glossary-glQBRnVD.mjs.map +1 -0
- package/lib/node/handleRequest-Bb7Y-XLw.cjs +220 -0
- package/lib/node/handleRequest-Bb7Y-XLw.cjs.map +1 -0
- package/lib/node/handleRequest-Y97UwBbF.mjs +190 -0
- package/lib/node/handleRequest-Y97UwBbF.mjs.map +1 -0
- package/lib/node/hasConfigurableGlobal-C97fWuaA.cjs +26 -0
- package/lib/node/hasConfigurableGlobal-C97fWuaA.cjs.map +1 -0
- package/lib/node/hasConfigurableGlobal-DBJA0vjm.mjs +20 -0
- package/lib/node/hasConfigurableGlobal-DBJA0vjm.mjs.map +1 -0
- package/lib/node/index-BMbJ8FXL.d.cts +113 -0
- package/lib/node/index-C0YAQ36w.d.mts +113 -0
- package/lib/node/index.cjs +54 -0
- package/lib/node/index.cjs.map +1 -0
- package/lib/node/index.d.cts +75 -0
- package/lib/node/index.d.mts +75 -0
- package/lib/node/index.mjs +40 -0
- package/lib/node/index.mjs.map +1 -0
- package/lib/node/interceptors/ClientRequest/index.cjs +6 -0
- package/lib/node/interceptors/ClientRequest/index.d.cts +2 -0
- package/lib/node/interceptors/ClientRequest/index.d.mts +3 -0
- package/lib/node/interceptors/ClientRequest/index.mjs +6 -0
- package/lib/node/interceptors/XMLHttpRequest/index.cjs +6 -0
- package/lib/node/interceptors/XMLHttpRequest/index.d.cts +14 -0
- package/lib/node/interceptors/XMLHttpRequest/index.d.mts +14 -0
- package/lib/node/interceptors/XMLHttpRequest/index.mjs +6 -0
- package/lib/node/interceptors/fetch/index.cjs +5 -0
- package/lib/node/interceptors/fetch/index.d.cts +12 -0
- package/lib/node/interceptors/fetch/index.d.mts +12 -0
- package/lib/node/interceptors/fetch/index.mjs +5 -0
- package/lib/node/node-DwCc6iuP.mjs +27 -0
- package/lib/node/node-DwCc6iuP.mjs.map +1 -0
- package/lib/node/node-dKdAf3tC.cjs +39 -0
- package/lib/node/node-dKdAf3tC.cjs.map +1 -0
- package/lib/node/presets/node.cjs +22 -0
- package/lib/node/presets/node.cjs.map +1 -0
- package/lib/node/presets/node.d.cts +13 -0
- package/lib/node/presets/node.d.mts +15 -0
- package/lib/node/presets/node.mjs +22 -0
- package/lib/node/presets/node.mjs.map +1 -0
- package/lib/node/utils/node/index.cjs +4 -0
- package/lib/node/utils/node/index.d.cts +16 -0
- package/lib/node/utils/node/index.d.mts +16 -0
- package/lib/node/utils/node/index.mjs +3 -0
- package/package.json +204 -0
- package/presets/browser/package.json +5 -0
- package/presets/node/package.json +11 -0
- package/src/BatchInterceptor.test.ts +255 -0
- package/src/BatchInterceptor.ts +95 -0
- package/src/Interceptor.test.ts +205 -0
- package/src/Interceptor.ts +249 -0
- package/src/InterceptorError.ts +7 -0
- package/src/RemoteHttpInterceptor.ts +251 -0
- package/src/RequestController.test.ts +104 -0
- package/src/RequestController.ts +109 -0
- package/src/createRequestId.test.ts +7 -0
- package/src/createRequestId.ts +9 -0
- package/src/getRawRequest.ts +21 -0
- package/src/glossary.ts +37 -0
- package/src/index.ts +15 -0
- package/src/interceptors/ClientRequest/MockHttpSocket.ts +724 -0
- package/src/interceptors/ClientRequest/agents.ts +110 -0
- package/src/interceptors/ClientRequest/index.test.ts +75 -0
- package/src/interceptors/ClientRequest/index.ts +193 -0
- package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.test.ts +54 -0
- package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.ts +45 -0
- package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.test.ts +427 -0
- package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.ts +268 -0
- package/src/interceptors/ClientRequest/utils/parserUtils.ts +48 -0
- package/src/interceptors/ClientRequest/utils/recordRawHeaders.test.ts +258 -0
- package/src/interceptors/ClientRequest/utils/recordRawHeaders.ts +262 -0
- package/src/interceptors/Socket/MockSocket.test.ts +264 -0
- package/src/interceptors/Socket/MockSocket.ts +58 -0
- package/src/interceptors/Socket/utils/baseUrlFromConnectionOptions.ts +26 -0
- package/src/interceptors/Socket/utils/normalizeSocketWriteArgs.test.ts +52 -0
- package/src/interceptors/Socket/utils/normalizeSocketWriteArgs.ts +33 -0
- package/src/interceptors/WebSocket/WebSocketClassTransport.ts +116 -0
- package/src/interceptors/WebSocket/WebSocketClientConnection.ts +152 -0
- package/src/interceptors/WebSocket/WebSocketOverride.ts +252 -0
- package/src/interceptors/WebSocket/WebSocketServerConnection.ts +420 -0
- package/src/interceptors/WebSocket/WebSocketTransport.ts +39 -0
- package/src/interceptors/WebSocket/index.ts +191 -0
- package/src/interceptors/WebSocket/utils/bindEvent.test.ts +27 -0
- package/src/interceptors/WebSocket/utils/bindEvent.ts +21 -0
- package/src/interceptors/WebSocket/utils/events.test.ts +101 -0
- package/src/interceptors/WebSocket/utils/events.ts +94 -0
- package/src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts +746 -0
- package/src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts +121 -0
- package/src/interceptors/XMLHttpRequest/index.ts +61 -0
- package/src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts +51 -0
- package/src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts +17 -0
- package/src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts +12 -0
- package/src/interceptors/XMLHttpRequest/utils/concateArrayBuffer.test.ts +12 -0
- package/src/interceptors/XMLHttpRequest/utils/createEvent.test.ts +26 -0
- package/src/interceptors/XMLHttpRequest/utils/createEvent.ts +41 -0
- package/src/interceptors/XMLHttpRequest/utils/createResponse.ts +49 -0
- package/src/interceptors/XMLHttpRequest/utils/getBodyByteLength.test.ts +164 -0
- package/src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts +16 -0
- package/src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts +14 -0
- package/src/interceptors/fetch/index.ts +214 -0
- package/src/interceptors/fetch/utils/brotli-decompress.browser.ts +14 -0
- package/src/interceptors/fetch/utils/brotli-decompress.ts +31 -0
- package/src/interceptors/fetch/utils/createNetworkError.ts +5 -0
- package/src/interceptors/fetch/utils/decompression.ts +85 -0
- package/src/interceptors/fetch/utils/followRedirect.ts +114 -0
- package/src/presets/browser.ts +11 -0
- package/src/presets/node.ts +13 -0
- package/src/utils/bufferUtils.test.ts +21 -0
- package/src/utils/bufferUtils.ts +22 -0
- package/src/utils/canParseUrl.ts +13 -0
- package/src/utils/cloneObject.test.ts +94 -0
- package/src/utils/cloneObject.ts +36 -0
- package/src/utils/createProxy.test.ts +164 -0
- package/src/utils/createProxy.ts +104 -0
- package/src/utils/emitAsync.ts +25 -0
- package/src/utils/fetchUtils.ts +119 -0
- package/src/utils/findPropertySource.test.ts +27 -0
- package/src/utils/findPropertySource.ts +20 -0
- package/src/utils/getCleanUrl.test.ts +32 -0
- package/src/utils/getCleanUrl.ts +6 -0
- package/src/utils/getUrlByRequestOptions.test.ts +163 -0
- package/src/utils/getUrlByRequestOptions.ts +152 -0
- package/src/utils/getValueBySymbol.test.ts +14 -0
- package/src/utils/getValueBySymbol.ts +19 -0
- package/src/utils/handleRequest.ts +205 -0
- package/src/utils/hasConfigurableGlobal.test.ts +83 -0
- package/src/utils/hasConfigurableGlobal.ts +34 -0
- package/src/utils/isNodeLikeError.ts +13 -0
- package/src/utils/isObject.test.ts +21 -0
- package/src/utils/isObject.ts +8 -0
- package/src/utils/isPropertyAccessible.ts +19 -0
- package/src/utils/nextTick.ts +11 -0
- package/src/utils/node/index.ts +39 -0
- package/src/utils/parseJson.test.ts +10 -0
- package/src/utils/parseJson.ts +12 -0
- package/src/utils/resolveWebSocketUrl.ts +45 -0
- package/src/utils/responseUtils.ts +59 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Here's how requests are handled in Node.js:
|
|
3
|
+
*
|
|
4
|
+
* 1. http.ClientRequest instance calls `agent.addRequest(request, options, cb)`.
|
|
5
|
+
* 2. Agent creates a new socket: `agent.createSocket(options, cb)`.
|
|
6
|
+
* 3. Agent creates a new connection: `agent.createConnection(options, cb)`.
|
|
7
|
+
*/
|
|
8
|
+
import net from 'node:net'
|
|
9
|
+
import http from 'node:http'
|
|
10
|
+
import https from 'node:https'
|
|
11
|
+
import {
|
|
12
|
+
MockHttpSocket,
|
|
13
|
+
type MockHttpSocketRequestCallback,
|
|
14
|
+
type MockHttpSocketResponseCallback,
|
|
15
|
+
} from './MockHttpSocket'
|
|
16
|
+
|
|
17
|
+
declare module 'node:http' {
|
|
18
|
+
interface Agent {
|
|
19
|
+
options?: http.AgentOptions
|
|
20
|
+
createConnection(options: any, callback: any): net.Socket
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface MockAgentOptions {
|
|
25
|
+
customAgent?: http.RequestOptions['agent']
|
|
26
|
+
onRequest: MockHttpSocketRequestCallback
|
|
27
|
+
onResponse: MockHttpSocketResponseCallback
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class MockAgent extends http.Agent {
|
|
31
|
+
private customAgent?: http.RequestOptions['agent']
|
|
32
|
+
private onRequest: MockHttpSocketRequestCallback
|
|
33
|
+
private onResponse: MockHttpSocketResponseCallback
|
|
34
|
+
|
|
35
|
+
constructor(options: MockAgentOptions) {
|
|
36
|
+
super()
|
|
37
|
+
this.customAgent = options.customAgent
|
|
38
|
+
this.onRequest = options.onRequest
|
|
39
|
+
this.onResponse = options.onResponse
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public createConnection(options: any, callback: any): net.Socket {
|
|
43
|
+
const createConnection =
|
|
44
|
+
this.customAgent instanceof http.Agent
|
|
45
|
+
? this.customAgent.createConnection
|
|
46
|
+
: super.createConnection
|
|
47
|
+
|
|
48
|
+
const createConnectionOptions =
|
|
49
|
+
this.customAgent instanceof http.Agent
|
|
50
|
+
? {
|
|
51
|
+
...options,
|
|
52
|
+
...this.customAgent.options,
|
|
53
|
+
}
|
|
54
|
+
: options
|
|
55
|
+
|
|
56
|
+
const socket = new MockHttpSocket({
|
|
57
|
+
connectionOptions: options,
|
|
58
|
+
createConnection: createConnection.bind(
|
|
59
|
+
this.customAgent || this,
|
|
60
|
+
createConnectionOptions,
|
|
61
|
+
callback
|
|
62
|
+
),
|
|
63
|
+
onRequest: this.onRequest.bind(this),
|
|
64
|
+
onResponse: this.onResponse.bind(this),
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
return socket
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export class MockHttpsAgent extends https.Agent {
|
|
72
|
+
private customAgent?: https.RequestOptions['agent']
|
|
73
|
+
private onRequest: MockHttpSocketRequestCallback
|
|
74
|
+
private onResponse: MockHttpSocketResponseCallback
|
|
75
|
+
|
|
76
|
+
constructor(options: MockAgentOptions) {
|
|
77
|
+
super()
|
|
78
|
+
this.customAgent = options.customAgent
|
|
79
|
+
this.onRequest = options.onRequest
|
|
80
|
+
this.onResponse = options.onResponse
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public createConnection(options: any, callback: any): net.Socket {
|
|
84
|
+
const createConnection =
|
|
85
|
+
this.customAgent instanceof http.Agent
|
|
86
|
+
? this.customAgent.createConnection
|
|
87
|
+
: super.createConnection
|
|
88
|
+
|
|
89
|
+
const createConnectionOptions =
|
|
90
|
+
this.customAgent instanceof http.Agent
|
|
91
|
+
? {
|
|
92
|
+
...options,
|
|
93
|
+
...this.customAgent.options,
|
|
94
|
+
}
|
|
95
|
+
: options
|
|
96
|
+
|
|
97
|
+
const socket = new MockHttpSocket({
|
|
98
|
+
connectionOptions: options,
|
|
99
|
+
createConnection: createConnection.bind(
|
|
100
|
+
this.customAgent || this,
|
|
101
|
+
createConnectionOptions,
|
|
102
|
+
callback
|
|
103
|
+
),
|
|
104
|
+
onRequest: this.onRequest.bind(this),
|
|
105
|
+
onResponse: this.onResponse.bind(this),
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
return socket
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { it, expect, beforeAll, afterEach, afterAll } from 'vitest'
|
|
2
|
+
import http from 'node:http'
|
|
3
|
+
import { HttpServer } from '@open-draft/test-server/http'
|
|
4
|
+
import { DeferredPromise } from '@open-draft/deferred-promise'
|
|
5
|
+
import { ClientRequestInterceptor } from '.'
|
|
6
|
+
import { sleep, waitForClientRequest } from '../../../test/helpers'
|
|
7
|
+
|
|
8
|
+
const httpServer = new HttpServer((app) => {
|
|
9
|
+
app.get('/', (_req, res) => {
|
|
10
|
+
res.status(200).send('/')
|
|
11
|
+
})
|
|
12
|
+
app.get('/get', (_req, res) => {
|
|
13
|
+
res.status(200).send('/get')
|
|
14
|
+
})
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const interceptor = new ClientRequestInterceptor()
|
|
18
|
+
|
|
19
|
+
beforeAll(async () => {
|
|
20
|
+
interceptor.apply()
|
|
21
|
+
await httpServer.listen()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
interceptor.removeAllListeners()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
afterAll(async () => {
|
|
29
|
+
interceptor.dispose()
|
|
30
|
+
await httpServer.close()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('abort the request if the abort signal is emitted', async () => {
|
|
34
|
+
const requestUrl = httpServer.http.url('/')
|
|
35
|
+
|
|
36
|
+
interceptor.on('request', async function delayedResponse({ controller }) {
|
|
37
|
+
await sleep(1_000)
|
|
38
|
+
controller.respondWith(new Response())
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const abortController = new AbortController()
|
|
42
|
+
const request = http.get(requestUrl, { signal: abortController.signal })
|
|
43
|
+
|
|
44
|
+
abortController.abort()
|
|
45
|
+
|
|
46
|
+
const abortErrorPromise = new DeferredPromise<Error>()
|
|
47
|
+
request.on('error', function (error) {
|
|
48
|
+
abortErrorPromise.resolve(error)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const abortError = await abortErrorPromise
|
|
52
|
+
expect(abortError.name).toEqual('AbortError')
|
|
53
|
+
|
|
54
|
+
expect(request.destroyed).toBe(true)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('patch the Headers object correctly after dispose and reapply', async () => {
|
|
58
|
+
interceptor.dispose()
|
|
59
|
+
interceptor.apply()
|
|
60
|
+
|
|
61
|
+
interceptor.on('request', ({ controller }) => {
|
|
62
|
+
const headers = new Headers({
|
|
63
|
+
'X-CustoM-HeadeR': 'Yes',
|
|
64
|
+
})
|
|
65
|
+
controller.respondWith(new Response(null, { headers }))
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const request = http.get(httpServer.http.url('/'))
|
|
69
|
+
const { res } = await waitForClientRequest(request)
|
|
70
|
+
|
|
71
|
+
expect(res.rawHeaders).toEqual(
|
|
72
|
+
expect.arrayContaining(['X-CustoM-HeadeR', 'Yes'])
|
|
73
|
+
)
|
|
74
|
+
expect(res.headers['x-custom-header']).toEqual('Yes')
|
|
75
|
+
})
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import http from 'node:http'
|
|
2
|
+
import https from 'node:https'
|
|
3
|
+
import { Interceptor } from '../../Interceptor'
|
|
4
|
+
import type { HttpRequestEventMap } from '../../glossary'
|
|
5
|
+
import {
|
|
6
|
+
kRequestId,
|
|
7
|
+
MockHttpSocketRequestCallback,
|
|
8
|
+
MockHttpSocketResponseCallback,
|
|
9
|
+
} from './MockHttpSocket'
|
|
10
|
+
import { MockAgent, MockHttpsAgent } from './agents'
|
|
11
|
+
import { RequestController } from '../../RequestController'
|
|
12
|
+
import { emitAsync } from '../../utils/emitAsync'
|
|
13
|
+
import { normalizeClientRequestArgs } from './utils/normalizeClientRequestArgs'
|
|
14
|
+
import { handleRequest } from '../../utils/handleRequest'
|
|
15
|
+
import {
|
|
16
|
+
recordRawFetchHeaders,
|
|
17
|
+
restoreHeadersPrototype,
|
|
18
|
+
} from './utils/recordRawHeaders'
|
|
19
|
+
|
|
20
|
+
export class ClientRequestInterceptor extends Interceptor<HttpRequestEventMap> {
|
|
21
|
+
static symbol = Symbol('client-request-interceptor')
|
|
22
|
+
|
|
23
|
+
constructor() {
|
|
24
|
+
super(ClientRequestInterceptor.symbol)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
protected setup(): void {
|
|
28
|
+
const {
|
|
29
|
+
ClientRequest: OriginalClientRequest,
|
|
30
|
+
get: originalGet,
|
|
31
|
+
request: originalRequest,
|
|
32
|
+
} = http
|
|
33
|
+
const { get: originalHttpsGet, request: originalHttpsRequest } = https
|
|
34
|
+
|
|
35
|
+
const onRequest = this.onRequest.bind(this)
|
|
36
|
+
const onResponse = this.onResponse.bind(this)
|
|
37
|
+
|
|
38
|
+
// Support requests performed via the `ClientRequest` constructor directly.
|
|
39
|
+
http.ClientRequest = new Proxy(http.ClientRequest, {
|
|
40
|
+
construct: (target, args: Parameters<typeof http.request>) => {
|
|
41
|
+
const [url, options, callback] = normalizeClientRequestArgs(
|
|
42
|
+
'http:',
|
|
43
|
+
args
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
// Create a mock agent instance appropriate for the request protocol.
|
|
47
|
+
const Agent = options.protocol === 'https:' ? MockHttpsAgent : MockAgent
|
|
48
|
+
const mockAgent = new Agent({
|
|
49
|
+
customAgent: options.agent,
|
|
50
|
+
onRequest,
|
|
51
|
+
onResponse,
|
|
52
|
+
})
|
|
53
|
+
options.agent = mockAgent
|
|
54
|
+
|
|
55
|
+
return Reflect.construct(target, [url, options, callback])
|
|
56
|
+
},
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
http.request = new Proxy(http.request, {
|
|
60
|
+
apply: (target, thisArg, args: Parameters<typeof http.request>) => {
|
|
61
|
+
const [url, options, callback] = normalizeClientRequestArgs(
|
|
62
|
+
'http:',
|
|
63
|
+
args
|
|
64
|
+
)
|
|
65
|
+
const mockAgent = new MockAgent({
|
|
66
|
+
customAgent: options.agent,
|
|
67
|
+
onRequest,
|
|
68
|
+
onResponse,
|
|
69
|
+
})
|
|
70
|
+
options.agent = mockAgent
|
|
71
|
+
|
|
72
|
+
return Reflect.apply(target, thisArg, [url, options, callback])
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
http.get = new Proxy(http.get, {
|
|
77
|
+
apply: (target, thisArg, args: Parameters<typeof http.get>) => {
|
|
78
|
+
const [url, options, callback] = normalizeClientRequestArgs(
|
|
79
|
+
'http:',
|
|
80
|
+
args
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
const mockAgent = new MockAgent({
|
|
84
|
+
customAgent: options.agent,
|
|
85
|
+
onRequest,
|
|
86
|
+
onResponse,
|
|
87
|
+
})
|
|
88
|
+
options.agent = mockAgent
|
|
89
|
+
|
|
90
|
+
return Reflect.apply(target, thisArg, [url, options, callback])
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
//
|
|
95
|
+
// HTTPS.
|
|
96
|
+
//
|
|
97
|
+
|
|
98
|
+
https.request = new Proxy(https.request, {
|
|
99
|
+
apply: (target, thisArg, args: Parameters<typeof https.request>) => {
|
|
100
|
+
const [url, options, callback] = normalizeClientRequestArgs(
|
|
101
|
+
'https:',
|
|
102
|
+
args
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
const mockAgent = new MockHttpsAgent({
|
|
106
|
+
customAgent: options.agent,
|
|
107
|
+
onRequest,
|
|
108
|
+
onResponse,
|
|
109
|
+
})
|
|
110
|
+
options.agent = mockAgent
|
|
111
|
+
|
|
112
|
+
return Reflect.apply(target, thisArg, [url, options, callback])
|
|
113
|
+
},
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
https.get = new Proxy(https.get, {
|
|
117
|
+
apply: (target, thisArg, args: Parameters<typeof https.get>) => {
|
|
118
|
+
const [url, options, callback] = normalizeClientRequestArgs(
|
|
119
|
+
'https:',
|
|
120
|
+
args
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
const mockAgent = new MockHttpsAgent({
|
|
124
|
+
customAgent: options.agent,
|
|
125
|
+
onRequest,
|
|
126
|
+
onResponse,
|
|
127
|
+
})
|
|
128
|
+
options.agent = mockAgent
|
|
129
|
+
|
|
130
|
+
return Reflect.apply(target, thisArg, [url, options, callback])
|
|
131
|
+
},
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
// Spy on `Header.prototype.set` and `Header.prototype.append` calls
|
|
135
|
+
// and record the raw header names provided. This is to support
|
|
136
|
+
// `IncomingMessage.prototype.rawHeaders`.
|
|
137
|
+
recordRawFetchHeaders()
|
|
138
|
+
|
|
139
|
+
this.subscriptions.push(() => {
|
|
140
|
+
http.ClientRequest = OriginalClientRequest
|
|
141
|
+
|
|
142
|
+
http.get = originalGet
|
|
143
|
+
http.request = originalRequest
|
|
144
|
+
|
|
145
|
+
https.get = originalHttpsGet
|
|
146
|
+
https.request = originalHttpsRequest
|
|
147
|
+
|
|
148
|
+
restoreHeadersPrototype()
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private onRequest: MockHttpSocketRequestCallback = async ({
|
|
153
|
+
request,
|
|
154
|
+
socket,
|
|
155
|
+
}) => {
|
|
156
|
+
const controller = new RequestController(request, {
|
|
157
|
+
passthrough() {
|
|
158
|
+
socket.passthrough()
|
|
159
|
+
},
|
|
160
|
+
async respondWith(response) {
|
|
161
|
+
await socket.respondWith(response)
|
|
162
|
+
},
|
|
163
|
+
errorWith(reason) {
|
|
164
|
+
if (reason instanceof Error) {
|
|
165
|
+
socket.errorWith(reason)
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
await handleRequest({
|
|
171
|
+
request,
|
|
172
|
+
requestId: Reflect.get(request, kRequestId),
|
|
173
|
+
controller,
|
|
174
|
+
emitter: this.emitter,
|
|
175
|
+
})
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
public onResponse: MockHttpSocketResponseCallback = async ({
|
|
179
|
+
requestId,
|
|
180
|
+
request,
|
|
181
|
+
response,
|
|
182
|
+
isMockedResponse,
|
|
183
|
+
}) => {
|
|
184
|
+
// Return the promise to when all the response event listeners
|
|
185
|
+
// are finished.
|
|
186
|
+
return emitAsync(this.emitter, 'response', {
|
|
187
|
+
requestId,
|
|
188
|
+
request,
|
|
189
|
+
response,
|
|
190
|
+
isMockedResponse,
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { it, expect } from 'vitest'
|
|
2
|
+
import { IncomingMessage } from 'http'
|
|
3
|
+
import { Socket } from 'net'
|
|
4
|
+
import * as zlib from 'zlib'
|
|
5
|
+
import { getIncomingMessageBody } from './getIncomingMessageBody'
|
|
6
|
+
|
|
7
|
+
it('returns utf8 string given a utf8 response body', async () => {
|
|
8
|
+
const utfBuffer = Buffer.from('one')
|
|
9
|
+
const message = new IncomingMessage(new Socket())
|
|
10
|
+
|
|
11
|
+
const pendingResponseBody = getIncomingMessageBody(message)
|
|
12
|
+
message.emit('data', utfBuffer)
|
|
13
|
+
message.emit('end')
|
|
14
|
+
|
|
15
|
+
expect(await pendingResponseBody).toEqual('one')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('returns utf8 string given a gzipped response body', async () => {
|
|
19
|
+
const utfBuffer = zlib.gzipSync(Buffer.from('two'))
|
|
20
|
+
const message = new IncomingMessage(new Socket())
|
|
21
|
+
message.headers = {
|
|
22
|
+
'content-encoding': 'gzip',
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const pendingResponseBody = getIncomingMessageBody(message)
|
|
26
|
+
message.emit('data', utfBuffer)
|
|
27
|
+
message.emit('end')
|
|
28
|
+
|
|
29
|
+
expect(await pendingResponseBody).toEqual('two')
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('returns utf8 string given a gzipped response body with incorrect "content-length"', async () => {
|
|
33
|
+
const utfBuffer = zlib.gzipSync(Buffer.from('three'))
|
|
34
|
+
const message = new IncomingMessage(new Socket())
|
|
35
|
+
message.headers = {
|
|
36
|
+
'content-encoding': 'gzip',
|
|
37
|
+
'content-length': '500',
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const pendingResponseBody = getIncomingMessageBody(message)
|
|
41
|
+
message.emit('data', utfBuffer)
|
|
42
|
+
message.emit('end')
|
|
43
|
+
|
|
44
|
+
expect(await pendingResponseBody).toEqual('three')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('returns empty string given an empty body', async () => {
|
|
48
|
+
const message = new IncomingMessage(new Socket())
|
|
49
|
+
|
|
50
|
+
const pendingResponseBody = getIncomingMessageBody(message)
|
|
51
|
+
message.emit('end')
|
|
52
|
+
|
|
53
|
+
expect(await pendingResponseBody).toEqual('')
|
|
54
|
+
})
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { IncomingMessage } from 'http'
|
|
2
|
+
import { PassThrough } from 'stream'
|
|
3
|
+
import * as zlib from 'zlib'
|
|
4
|
+
import { Logger } from '@open-draft/logger'
|
|
5
|
+
|
|
6
|
+
const logger = new Logger('http getIncomingMessageBody')
|
|
7
|
+
|
|
8
|
+
export function getIncomingMessageBody(
|
|
9
|
+
response: IncomingMessage
|
|
10
|
+
): Promise<string> {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
logger.info('cloning the original response...')
|
|
13
|
+
|
|
14
|
+
// Pipe the original response to support non-clone
|
|
15
|
+
// "response" input. No need to clone the response,
|
|
16
|
+
// as we always have access to the full "response" input,
|
|
17
|
+
// either a clone or an original one (in tests).
|
|
18
|
+
const responseClone = response.pipe(new PassThrough())
|
|
19
|
+
const stream =
|
|
20
|
+
response.headers['content-encoding'] === 'gzip'
|
|
21
|
+
? responseClone.pipe(zlib.createGunzip())
|
|
22
|
+
: responseClone
|
|
23
|
+
|
|
24
|
+
const encoding = response.readableEncoding || 'utf8'
|
|
25
|
+
stream.setEncoding(encoding)
|
|
26
|
+
logger.info('using encoding:', encoding)
|
|
27
|
+
|
|
28
|
+
let body = ''
|
|
29
|
+
|
|
30
|
+
stream.on('data', (responseBody) => {
|
|
31
|
+
logger.info('response body read:', responseBody)
|
|
32
|
+
body += responseBody
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
stream.once('end', () => {
|
|
36
|
+
logger.info('response body end')
|
|
37
|
+
resolve(body)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
stream.once('error', (error) => {
|
|
41
|
+
logger.info('error while reading response body:', error)
|
|
42
|
+
reject(error)
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
}
|