11import net from 'node:net'
22import { Readable , Writable } from 'node:stream'
33import { invariant } from 'outvariant'
4- import { Interceptor , INTERNAL_REQUEST_ID_HEADER_NAME } from '../../Interceptor'
4+ import { Interceptor } from '../../Interceptor'
55import { type HttpRequestEventMap } from '../../glossary'
66import { SocketInterceptor } from '../net'
77import { FetchResponse } from '../../utils/fetchUtils'
@@ -81,32 +81,44 @@ export class HttpRequestInterceptor extends Interceptor<HttpRequestEventMap> {
8181 controller,
8282 emitter : this . emitter ,
8383 onResponse : async ( response ) => {
84+ if ( this . emitter . listenerCount ( 'response' ) > 0 ) {
85+ const responseClone = response . clone ( )
86+ process . nextTick ( ( ) => {
87+ emitAsync ( this . emitter , 'response' , {
88+ requestId,
89+ request,
90+ response : responseClone ,
91+ isMockedResponse : true ,
92+ } )
93+ } )
94+ }
95+
8496 await respondWith ( {
8597 socket,
8698 connectionOptions : options ,
8799 request,
88100 response,
89101 } )
90- await emitAsync ( this . emitter , 'response' , {
91- requestId,
92- request,
93- response,
94- isMockedResponse : true ,
95- } )
96102 } ,
97103 async onRequestError ( response ) {
104+ if ( this . emitter . listenerCount ( 'response' ) > 0 ) {
105+ const responseClone = response . clone ( )
106+ process . nextTick ( ( ) => {
107+ emitAsync ( this . emitter , 'response' , {
108+ requestId,
109+ request,
110+ response : responseClone ,
111+ isMockedResponse : true ,
112+ } )
113+ } )
114+ }
115+
98116 await respondWith ( {
99117 socket,
100118 connectionOptions : options ,
101119 request,
102120 response,
103121 } )
104- await emitAsync ( this . emitter , 'response' , {
105- requestId,
106- request,
107- response,
108- isMockedResponse : true ,
109- } )
110122 } ,
111123 onError ( error ) {
112124 if ( error instanceof Error ) {
@@ -116,22 +128,6 @@ export class HttpRequestInterceptor extends Interceptor<HttpRequestEventMap> {
116128 } )
117129
118130 if ( ! isRequestHandled ) {
119- // If the user didn't register any response listeners, no need to pay the
120- // price of routing the entire response message through the parser.
121- if ( this . emitter . listenerCount ( 'response' ) > 0 ) {
122- createHttpResponseParserStream ( {
123- socket,
124- onResponse : async ( response ) => {
125- await emitAsync ( this . emitter , 'response' , {
126- requestId,
127- request,
128- response,
129- isMockedResponse : false ,
130- } )
131- } ,
132- } )
133- }
134-
135131 const passthroughSocket = socket . passthrough ( )
136132
137133 /**
@@ -147,14 +143,39 @@ export class HttpRequestInterceptor extends Interceptor<HttpRequestEventMap> {
147143 passthroughSocket . parser = socket . parser
148144 // @ts -expect-error Node.js internals.
149145 passthroughSocket . parser . socket = passthroughSocket
146+
147+ // If the user didn't register any response listeners, no need to pay the
148+ // price of routing the entire response message through the parser.
149+ if ( this . emitter . listenerCount ( 'response' ) > 0 ) {
150+ const responseStream = createHttpResponseParserStream ( {
151+ onResponse : async ( response ) => {
152+ await emitAsync ( this . emitter , 'response' , {
153+ requestId,
154+ request,
155+ response,
156+ isMockedResponse : false ,
157+ } )
158+ } ,
159+ } )
160+
161+ passthroughSocket
162+ . on ( 'data' , ( chunk ) => responseStream . write ( chunk ) )
163+ . on ( 'close' , ( ) => responseStream . end ( ) )
164+ }
150165 }
151166 } ,
152167 } )
153168
154169 // Write the message header to the parser manually because it's already been written
155170 // on the socket so it won't get piped.
156171 requestParser . write ( toBuffer ( chunk , encoding ) )
157- socket . pipe ( requestParser )
172+
173+ socket
174+ . on ( 'write' , ( chunk , encoding , callback ) => {
175+ requestParser . write ( chunk , encoding , callback )
176+ } )
177+ . on ( 'finish' , ( ) => requestParser . end ( ) )
178+ . on ( 'error' , ( ) => requestParser . end ( ) )
158179 } )
159180 } )
160181 } )
@@ -211,13 +232,7 @@ function createHttpRequestParserStream(options: {
211232 * @note Provide the `read()` method so a `Readable` could be
212233 * used as the actual request body (the stream calls "read()").
213234 */
214- read ( ) {
215- // If the user attempts to read the request body,
216- // flush the write buffer to trigger the callbacks.
217- // This way, if the request stream ends in the write callback,
218- // it will indeed end correctly.
219- // flushWriteBuffer()
220- } ,
235+ read ( ) { } ,
221236 } )
222237
223238 const request = new Request ( url , {
@@ -254,25 +269,18 @@ function createHttpRequestParserStream(options: {
254269 } ,
255270 } )
256271
257- const parserStream = new Writable ( {
272+ return new Writable ( {
258273 write ( chunk , encoding , callback ) {
259274 parser . execute ( toBuffer ( chunk , encoding ) )
260275 callback ( )
261276 } ,
262277 } )
263-
264- parserStream
265- . once ( 'finish' , ( ) => parser . free ( ) )
266- . once ( 'close' , ( ) => parser . free ( ) )
267-
268- return parserStream
269278}
270279
271280function createHttpResponseParserStream ( options : {
272- socket : MockSocket
273281 onResponse : ( response : Response ) => void
274282} ) {
275- const { socket , onResponse } = options
283+ const { onResponse } = options
276284 const responseRawHeadersBuffer : Array < string > = [ ]
277285 let responseBodyStream : Readable | undefined
278286
@@ -323,11 +331,12 @@ function createHttpResponseParserStream(options: {
323331 } ,
324332 } )
325333
326- socket
327- . on ( 'push' , ( chunk , encoding ) => {
334+ return new Writable ( {
335+ write ( chunk , encoding , callback ) {
328336 parser . execute ( toBuffer ( chunk , encoding ) )
329- } )
330- . once ( 'close' , ( ) => parser . free ( ) )
337+ callback ( )
338+ } ,
339+ } )
331340}
332341
333342/**
0 commit comments