@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.
Files changed (248) hide show
  1. package/ClientRequest/package.json +11 -0
  2. package/LICENSE.md +9 -0
  3. package/README.md +31 -0
  4. package/RemoteHttpInterceptor/package.json +11 -0
  5. package/WebSocket/package.json +12 -0
  6. package/XMLHttpRequest/package.json +12 -0
  7. package/changes.json +10 -0
  8. package/fetch/package.json +12 -0
  9. package/lib/browser/Interceptor-Deczogc8.d.cts +65 -0
  10. package/lib/browser/Interceptor-gqKgs-aF.d.mts +65 -0
  11. package/lib/browser/XMLHttpRequest-BACqefB-.cjs +761 -0
  12. package/lib/browser/XMLHttpRequest-BACqefB-.cjs.map +1 -0
  13. package/lib/browser/XMLHttpRequest-BvxZV0WU.mjs +756 -0
  14. package/lib/browser/XMLHttpRequest-BvxZV0WU.mjs.map +1 -0
  15. package/lib/browser/bufferUtils-BiiO6HZv.mjs +20 -0
  16. package/lib/browser/bufferUtils-BiiO6HZv.mjs.map +1 -0
  17. package/lib/browser/bufferUtils-Uc0eRItL.cjs +38 -0
  18. package/lib/browser/bufferUtils-Uc0eRItL.cjs.map +1 -0
  19. package/lib/browser/createRequestId-Cs4oXfa1.cjs +205 -0
  20. package/lib/browser/createRequestId-Cs4oXfa1.cjs.map +1 -0
  21. package/lib/browser/createRequestId-DQcIlohW.mjs +170 -0
  22. package/lib/browser/createRequestId-DQcIlohW.mjs.map +1 -0
  23. package/lib/browser/fetch-DdKEdDOR.mjs +248 -0
  24. package/lib/browser/fetch-DdKEdDOR.mjs.map +1 -0
  25. package/lib/browser/fetch-U3v3Y4ap.cjs +253 -0
  26. package/lib/browser/fetch-U3v3Y4ap.cjs.map +1 -0
  27. package/lib/browser/getRawRequest-BTaNLFr0.mjs +218 -0
  28. package/lib/browser/getRawRequest-BTaNLFr0.mjs.map +1 -0
  29. package/lib/browser/getRawRequest-zx8rUJL2.cjs +259 -0
  30. package/lib/browser/getRawRequest-zx8rUJL2.cjs.map +1 -0
  31. package/lib/browser/glossary-BdLS4k1H.d.cts +70 -0
  32. package/lib/browser/glossary-DYwOrogs.d.mts +70 -0
  33. package/lib/browser/handleRequest-CvX2G-Lz.cjs +189 -0
  34. package/lib/browser/handleRequest-CvX2G-Lz.cjs.map +1 -0
  35. package/lib/browser/handleRequest-D7kpTI5U.mjs +178 -0
  36. package/lib/browser/handleRequest-D7kpTI5U.mjs.map +1 -0
  37. package/lib/browser/hasConfigurableGlobal-BvCTG97d.cjs +45 -0
  38. package/lib/browser/hasConfigurableGlobal-BvCTG97d.cjs.map +1 -0
  39. package/lib/browser/hasConfigurableGlobal-npXitu1-.mjs +33 -0
  40. package/lib/browser/hasConfigurableGlobal-npXitu1-.mjs.map +1 -0
  41. package/lib/browser/index.cjs +70 -0
  42. package/lib/browser/index.cjs.map +1 -0
  43. package/lib/browser/index.d.cts +96 -0
  44. package/lib/browser/index.d.mts +96 -0
  45. package/lib/browser/index.mjs +56 -0
  46. package/lib/browser/index.mjs.map +1 -0
  47. package/lib/browser/interceptors/WebSocket/index.cjs +622 -0
  48. package/lib/browser/interceptors/WebSocket/index.cjs.map +1 -0
  49. package/lib/browser/interceptors/WebSocket/index.d.cts +277 -0
  50. package/lib/browser/interceptors/WebSocket/index.d.mts +277 -0
  51. package/lib/browser/interceptors/WebSocket/index.mjs +615 -0
  52. package/lib/browser/interceptors/WebSocket/index.mjs.map +1 -0
  53. package/lib/browser/interceptors/XMLHttpRequest/index.cjs +7 -0
  54. package/lib/browser/interceptors/XMLHttpRequest/index.d.cts +15 -0
  55. package/lib/browser/interceptors/XMLHttpRequest/index.d.mts +15 -0
  56. package/lib/browser/interceptors/XMLHttpRequest/index.mjs +7 -0
  57. package/lib/browser/interceptors/fetch/index.cjs +6 -0
  58. package/lib/browser/interceptors/fetch/index.d.cts +13 -0
  59. package/lib/browser/interceptors/fetch/index.d.mts +13 -0
  60. package/lib/browser/interceptors/fetch/index.mjs +6 -0
  61. package/lib/browser/presets/browser.cjs +17 -0
  62. package/lib/browser/presets/browser.cjs.map +1 -0
  63. package/lib/browser/presets/browser.d.cts +12 -0
  64. package/lib/browser/presets/browser.d.mts +14 -0
  65. package/lib/browser/presets/browser.mjs +17 -0
  66. package/lib/browser/presets/browser.mjs.map +1 -0
  67. package/lib/browser/resolveWebSocketUrl-6K6EgqsA.cjs +31 -0
  68. package/lib/browser/resolveWebSocketUrl-6K6EgqsA.cjs.map +1 -0
  69. package/lib/browser/resolveWebSocketUrl-C83-x9iE.mjs +25 -0
  70. package/lib/browser/resolveWebSocketUrl-C83-x9iE.mjs.map +1 -0
  71. package/lib/node/BatchInterceptor-3LnAnLTx.cjs +49 -0
  72. package/lib/node/BatchInterceptor-3LnAnLTx.cjs.map +1 -0
  73. package/lib/node/BatchInterceptor-D7mXzHcQ.d.mts +26 -0
  74. package/lib/node/BatchInterceptor-DFaBPilf.mjs +44 -0
  75. package/lib/node/BatchInterceptor-DFaBPilf.mjs.map +1 -0
  76. package/lib/node/BatchInterceptor-D_YqR8qU.d.cts +26 -0
  77. package/lib/node/ClientRequest-2rDe54Ui.cjs +1043 -0
  78. package/lib/node/ClientRequest-2rDe54Ui.cjs.map +1 -0
  79. package/lib/node/ClientRequest-Ca8Qykuv.mjs +1034 -0
  80. package/lib/node/ClientRequest-Ca8Qykuv.mjs.map +1 -0
  81. package/lib/node/Interceptor-DEazpLJd.d.mts +133 -0
  82. package/lib/node/Interceptor-DJ2akVWI.d.cts +133 -0
  83. package/lib/node/RemoteHttpInterceptor.cjs +154 -0
  84. package/lib/node/RemoteHttpInterceptor.cjs.map +1 -0
  85. package/lib/node/RemoteHttpInterceptor.d.cts +39 -0
  86. package/lib/node/RemoteHttpInterceptor.d.mts +39 -0
  87. package/lib/node/RemoteHttpInterceptor.mjs +152 -0
  88. package/lib/node/RemoteHttpInterceptor.mjs.map +1 -0
  89. package/lib/node/XMLHttpRequest-B7kJdYYI.cjs +763 -0
  90. package/lib/node/XMLHttpRequest-B7kJdYYI.cjs.map +1 -0
  91. package/lib/node/XMLHttpRequest-C8dIZpds.mjs +757 -0
  92. package/lib/node/XMLHttpRequest-C8dIZpds.mjs.map +1 -0
  93. package/lib/node/bufferUtils-DiCTqG-7.cjs +38 -0
  94. package/lib/node/bufferUtils-DiCTqG-7.cjs.map +1 -0
  95. package/lib/node/bufferUtils-_8XfKIfX.mjs +20 -0
  96. package/lib/node/bufferUtils-_8XfKIfX.mjs.map +1 -0
  97. package/lib/node/chunk-CbDLau6x.cjs +34 -0
  98. package/lib/node/fetch-BmXpK10r.cjs +272 -0
  99. package/lib/node/fetch-BmXpK10r.cjs.map +1 -0
  100. package/lib/node/fetch-G1DVwDKG.mjs +265 -0
  101. package/lib/node/fetch-G1DVwDKG.mjs.map +1 -0
  102. package/lib/node/fetchUtils-BaY5iWXw.cjs +419 -0
  103. package/lib/node/fetchUtils-BaY5iWXw.cjs.map +1 -0
  104. package/lib/node/fetchUtils-CoU35g3M.mjs +359 -0
  105. package/lib/node/fetchUtils-CoU35g3M.mjs.map +1 -0
  106. package/lib/node/getRawRequest-BavnMWh_.cjs +36 -0
  107. package/lib/node/getRawRequest-BavnMWh_.cjs.map +1 -0
  108. package/lib/node/getRawRequest-DnwmXyOW.mjs +24 -0
  109. package/lib/node/getRawRequest-DnwmXyOW.mjs.map +1 -0
  110. package/lib/node/glossary-BLKRyLBd.cjs +12 -0
  111. package/lib/node/glossary-BLKRyLBd.cjs.map +1 -0
  112. package/lib/node/glossary-glQBRnVD.mjs +6 -0
  113. package/lib/node/glossary-glQBRnVD.mjs.map +1 -0
  114. package/lib/node/handleRequest-Bb7Y-XLw.cjs +220 -0
  115. package/lib/node/handleRequest-Bb7Y-XLw.cjs.map +1 -0
  116. package/lib/node/handleRequest-Y97UwBbF.mjs +190 -0
  117. package/lib/node/handleRequest-Y97UwBbF.mjs.map +1 -0
  118. package/lib/node/hasConfigurableGlobal-C97fWuaA.cjs +26 -0
  119. package/lib/node/hasConfigurableGlobal-C97fWuaA.cjs.map +1 -0
  120. package/lib/node/hasConfigurableGlobal-DBJA0vjm.mjs +20 -0
  121. package/lib/node/hasConfigurableGlobal-DBJA0vjm.mjs.map +1 -0
  122. package/lib/node/index-BMbJ8FXL.d.cts +113 -0
  123. package/lib/node/index-C0YAQ36w.d.mts +113 -0
  124. package/lib/node/index.cjs +54 -0
  125. package/lib/node/index.cjs.map +1 -0
  126. package/lib/node/index.d.cts +75 -0
  127. package/lib/node/index.d.mts +75 -0
  128. package/lib/node/index.mjs +40 -0
  129. package/lib/node/index.mjs.map +1 -0
  130. package/lib/node/interceptors/ClientRequest/index.cjs +6 -0
  131. package/lib/node/interceptors/ClientRequest/index.d.cts +2 -0
  132. package/lib/node/interceptors/ClientRequest/index.d.mts +3 -0
  133. package/lib/node/interceptors/ClientRequest/index.mjs +6 -0
  134. package/lib/node/interceptors/XMLHttpRequest/index.cjs +6 -0
  135. package/lib/node/interceptors/XMLHttpRequest/index.d.cts +14 -0
  136. package/lib/node/interceptors/XMLHttpRequest/index.d.mts +14 -0
  137. package/lib/node/interceptors/XMLHttpRequest/index.mjs +6 -0
  138. package/lib/node/interceptors/fetch/index.cjs +5 -0
  139. package/lib/node/interceptors/fetch/index.d.cts +12 -0
  140. package/lib/node/interceptors/fetch/index.d.mts +12 -0
  141. package/lib/node/interceptors/fetch/index.mjs +5 -0
  142. package/lib/node/node-DwCc6iuP.mjs +27 -0
  143. package/lib/node/node-DwCc6iuP.mjs.map +1 -0
  144. package/lib/node/node-dKdAf3tC.cjs +39 -0
  145. package/lib/node/node-dKdAf3tC.cjs.map +1 -0
  146. package/lib/node/presets/node.cjs +22 -0
  147. package/lib/node/presets/node.cjs.map +1 -0
  148. package/lib/node/presets/node.d.cts +13 -0
  149. package/lib/node/presets/node.d.mts +15 -0
  150. package/lib/node/presets/node.mjs +22 -0
  151. package/lib/node/presets/node.mjs.map +1 -0
  152. package/lib/node/utils/node/index.cjs +4 -0
  153. package/lib/node/utils/node/index.d.cts +16 -0
  154. package/lib/node/utils/node/index.d.mts +16 -0
  155. package/lib/node/utils/node/index.mjs +3 -0
  156. package/package.json +204 -0
  157. package/presets/browser/package.json +5 -0
  158. package/presets/node/package.json +11 -0
  159. package/src/BatchInterceptor.test.ts +255 -0
  160. package/src/BatchInterceptor.ts +95 -0
  161. package/src/Interceptor.test.ts +205 -0
  162. package/src/Interceptor.ts +249 -0
  163. package/src/InterceptorError.ts +7 -0
  164. package/src/RemoteHttpInterceptor.ts +251 -0
  165. package/src/RequestController.test.ts +104 -0
  166. package/src/RequestController.ts +109 -0
  167. package/src/createRequestId.test.ts +7 -0
  168. package/src/createRequestId.ts +9 -0
  169. package/src/getRawRequest.ts +21 -0
  170. package/src/glossary.ts +37 -0
  171. package/src/index.ts +15 -0
  172. package/src/interceptors/ClientRequest/MockHttpSocket.ts +724 -0
  173. package/src/interceptors/ClientRequest/agents.ts +110 -0
  174. package/src/interceptors/ClientRequest/index.test.ts +75 -0
  175. package/src/interceptors/ClientRequest/index.ts +193 -0
  176. package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.test.ts +54 -0
  177. package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.ts +45 -0
  178. package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.test.ts +427 -0
  179. package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.ts +268 -0
  180. package/src/interceptors/ClientRequest/utils/parserUtils.ts +48 -0
  181. package/src/interceptors/ClientRequest/utils/recordRawHeaders.test.ts +258 -0
  182. package/src/interceptors/ClientRequest/utils/recordRawHeaders.ts +262 -0
  183. package/src/interceptors/Socket/MockSocket.test.ts +264 -0
  184. package/src/interceptors/Socket/MockSocket.ts +58 -0
  185. package/src/interceptors/Socket/utils/baseUrlFromConnectionOptions.ts +26 -0
  186. package/src/interceptors/Socket/utils/normalizeSocketWriteArgs.test.ts +52 -0
  187. package/src/interceptors/Socket/utils/normalizeSocketWriteArgs.ts +33 -0
  188. package/src/interceptors/WebSocket/WebSocketClassTransport.ts +116 -0
  189. package/src/interceptors/WebSocket/WebSocketClientConnection.ts +152 -0
  190. package/src/interceptors/WebSocket/WebSocketOverride.ts +252 -0
  191. package/src/interceptors/WebSocket/WebSocketServerConnection.ts +420 -0
  192. package/src/interceptors/WebSocket/WebSocketTransport.ts +39 -0
  193. package/src/interceptors/WebSocket/index.ts +191 -0
  194. package/src/interceptors/WebSocket/utils/bindEvent.test.ts +27 -0
  195. package/src/interceptors/WebSocket/utils/bindEvent.ts +21 -0
  196. package/src/interceptors/WebSocket/utils/events.test.ts +101 -0
  197. package/src/interceptors/WebSocket/utils/events.ts +94 -0
  198. package/src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts +746 -0
  199. package/src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts +121 -0
  200. package/src/interceptors/XMLHttpRequest/index.ts +61 -0
  201. package/src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts +51 -0
  202. package/src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts +17 -0
  203. package/src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts +12 -0
  204. package/src/interceptors/XMLHttpRequest/utils/concateArrayBuffer.test.ts +12 -0
  205. package/src/interceptors/XMLHttpRequest/utils/createEvent.test.ts +26 -0
  206. package/src/interceptors/XMLHttpRequest/utils/createEvent.ts +41 -0
  207. package/src/interceptors/XMLHttpRequest/utils/createResponse.ts +49 -0
  208. package/src/interceptors/XMLHttpRequest/utils/getBodyByteLength.test.ts +164 -0
  209. package/src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts +16 -0
  210. package/src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts +14 -0
  211. package/src/interceptors/fetch/index.ts +214 -0
  212. package/src/interceptors/fetch/utils/brotli-decompress.browser.ts +14 -0
  213. package/src/interceptors/fetch/utils/brotli-decompress.ts +31 -0
  214. package/src/interceptors/fetch/utils/createNetworkError.ts +5 -0
  215. package/src/interceptors/fetch/utils/decompression.ts +85 -0
  216. package/src/interceptors/fetch/utils/followRedirect.ts +114 -0
  217. package/src/presets/browser.ts +11 -0
  218. package/src/presets/node.ts +13 -0
  219. package/src/utils/bufferUtils.test.ts +21 -0
  220. package/src/utils/bufferUtils.ts +22 -0
  221. package/src/utils/canParseUrl.ts +13 -0
  222. package/src/utils/cloneObject.test.ts +94 -0
  223. package/src/utils/cloneObject.ts +36 -0
  224. package/src/utils/createProxy.test.ts +164 -0
  225. package/src/utils/createProxy.ts +104 -0
  226. package/src/utils/emitAsync.ts +25 -0
  227. package/src/utils/fetchUtils.ts +119 -0
  228. package/src/utils/findPropertySource.test.ts +27 -0
  229. package/src/utils/findPropertySource.ts +20 -0
  230. package/src/utils/getCleanUrl.test.ts +32 -0
  231. package/src/utils/getCleanUrl.ts +6 -0
  232. package/src/utils/getUrlByRequestOptions.test.ts +163 -0
  233. package/src/utils/getUrlByRequestOptions.ts +152 -0
  234. package/src/utils/getValueBySymbol.test.ts +14 -0
  235. package/src/utils/getValueBySymbol.ts +19 -0
  236. package/src/utils/handleRequest.ts +205 -0
  237. package/src/utils/hasConfigurableGlobal.test.ts +83 -0
  238. package/src/utils/hasConfigurableGlobal.ts +34 -0
  239. package/src/utils/isNodeLikeError.ts +13 -0
  240. package/src/utils/isObject.test.ts +21 -0
  241. package/src/utils/isObject.ts +8 -0
  242. package/src/utils/isPropertyAccessible.ts +19 -0
  243. package/src/utils/nextTick.ts +11 -0
  244. package/src/utils/node/index.ts +39 -0
  245. package/src/utils/parseJson.test.ts +10 -0
  246. package/src/utils/parseJson.ts +12 -0
  247. package/src/utils/resolveWebSocketUrl.ts +45 -0
  248. 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
+ }