@@ -85,8 +85,6 @@ export class SocketInterceptor extends Interceptor<SocketEventMap> {
85
85
// Otherwise, listen to the original response
86
86
// and forward it to the interceptor.
87
87
controller . onResponse = ( response , isMockedResponse ) => {
88
- console . log ( 'onResponse callback' )
89
-
90
88
self . emitter . emit ( 'response' , {
91
89
requestId,
92
90
request,
@@ -130,9 +128,7 @@ class SocketController {
130
128
private shouldSuppressEvents = false
131
129
private suppressedEvents : Array < [ event : string , ...args : Array < unknown > ] > = [ ]
132
130
private request : Request
133
- private requestParser : typeof HTTPParser
134
131
private requestStream ?: Readable
135
- private responseParser : typeof HTTPParser
136
132
private responseStream ?: Readable
137
133
138
134
constructor (
@@ -142,58 +138,35 @@ class SocketController {
142
138
) {
143
139
this . url = parseSocketConnectionUrl ( normalizedOptions )
144
140
145
- // Create the parser later on because a single
146
- // socket can be *reused* for multiple requests.
147
- // The same way, don't free the parser.
148
- this . requestParser = new HTTPParser ( )
149
- this . requestParser [ HTTPParser . kOnHeadersComplete ] = (
150
- verionMajor : number ,
151
- versionMinor : number ,
152
- headers : Array < string > ,
153
- idk : number ,
154
- path : string ,
155
- idk2 : undefined ,
156
- idk3 : undefined ,
157
- idk4 : boolean
158
- ) => {
159
- this . onRequestStart ( path , headers )
160
- }
161
- this . requestParser [ HTTPParser . kOnBody ] = ( chunk : Buffer ) => {
162
- this . onRequestData ( chunk )
163
- }
164
- this . requestParser [ HTTPParser . kOnMessageComplete ] = ( ) => {
165
- this . onRequestEnd ( )
166
- }
167
- this . requestParser . initialize ( HTTPParser . REQUEST , { } )
168
-
169
- this . responseParser = new HTTPParser ( )
170
- this . responseParser [ HTTPParser . kOnHeadersComplete ] = (
171
- verionMajor : number ,
172
- versionMinor : number ,
173
- headers : Array < string > ,
174
- method : string | undefined ,
175
- url : string | undefined ,
176
- status : number ,
177
- statusText : string ,
178
- upgrade : boolean ,
179
- shouldKeepAlive : boolean
180
- ) => {
181
- this . onResponseStart ( status , statusText , headers )
182
- }
183
- this . responseParser [ HTTPParser . kOnBody ] = ( chunk : Buffer ) => {
184
- this . onResponseData ( chunk )
185
- }
186
- this . responseParser [ HTTPParser . kOnMessageComplete ] = ( ) => {
187
- this . onResponseEnd ( )
188
- }
189
- this . responseParser . initialize (
190
- HTTPParser . RESPONSE ,
191
- // Don't create any async resources here.
192
- // This has to be "HTTPINCOMINGMESSAGE" in practice.
193
- // @see https://github.com/nodejs/llhttp/issues/44#issuecomment-582499320
194
- // new HTTPServerAsyncResource('INTERCEPTORINCOMINGMESSAGE', socket)
195
- { }
196
- )
141
+ const requestParser = new HttpMessageParser ( 'request' , {
142
+ onHeadersComplete : ( major , minor , headers , _ , path ) => {
143
+ this . onRequestStart ( path , headers )
144
+ } ,
145
+ onBody : ( chunk ) => {
146
+ this . onRequestData ( chunk )
147
+ } ,
148
+ onMessageComplete : this . onRequestEnd . bind ( this ) ,
149
+ } )
150
+
151
+ const responseParser = new HttpMessageParser ( 'response' , {
152
+ onHeadersComplete : (
153
+ versionMajor ,
154
+ versionMinor ,
155
+ headers ,
156
+ method ,
157
+ url ,
158
+ status ,
159
+ statusText ,
160
+ upgrade ,
161
+ keepalive
162
+ ) => {
163
+ this . onResponseStart ( status , statusText , headers )
164
+ } ,
165
+ onBody : ( chunk ) => {
166
+ this . onResponseData ( chunk )
167
+ } ,
168
+ onMessageComplete : this . onResponseEnd . bind ( this ) ,
169
+ } )
197
170
198
171
socket . emit = new Proxy ( socket . emit , {
199
172
apply : ( target , thisArg , args ) => {
@@ -209,13 +182,13 @@ class SocketController {
209
182
if ( this . shouldSuppressEvents ) {
210
183
if ( args [ 0 ] === 'error' ) {
211
184
Reflect . set ( this . socket , '_hadError' , false )
212
- this . suppressedEvents . push ( [ 'error' , args . slice ( 1 ) ] )
185
+ this . suppressedEvents . push ( [ 'error' , ... args . slice ( 1 ) ] )
213
186
return true
214
187
}
215
188
216
189
// Suppress close events for errored mocked connections.
217
190
if ( args [ 0 ] === 'close' ) {
218
- this . suppressedEvents . push ( [ 'close' , args . slice ( 1 ) ] )
191
+ this . suppressedEvents . push ( [ 'close' , ... args . slice ( 1 ) ] )
219
192
return true
220
193
}
221
194
}
@@ -224,7 +197,7 @@ class SocketController {
224
197
} ,
225
198
} )
226
199
227
- socket . once ( 'ready ' , ( ) => {
200
+ socket . once ( 'connect ' , ( ) => {
228
201
// Notify the interceptor once the socket is ready.
229
202
// The HTTP parser triggers BEFORE that.
230
203
this . onRequest ( this . request )
@@ -234,7 +207,7 @@ class SocketController {
234
207
socket . write = new Proxy ( socket . write , {
235
208
apply : ( target , thisArg , args ) => {
236
209
if ( args [ 0 ] !== null ) {
237
- this . requestParser . execute (
210
+ requestParser . push (
238
211
Buffer . isBuffer ( args [ 0 ] ) ? args [ 0 ] : Buffer . from ( args [ 0 ] )
239
212
)
240
213
}
@@ -246,7 +219,7 @@ class SocketController {
246
219
socket . push = new Proxy ( socket . push , {
247
220
apply : ( target , thisArg , args ) => {
248
221
if ( args [ 0 ] !== null ) {
249
- this . responseParser . execute (
222
+ responseParser . push (
250
223
Buffer . isBuffer ( args [ 0 ] ) ? args [ 0 ] : Buffer . from ( args [ 0 ] )
251
224
)
252
225
}
@@ -304,11 +277,15 @@ class SocketController {
304
277
}
305
278
306
279
private replayErrors ( ) {
280
+ console . log ( 'replay errors...' , this . suppressedEvents )
281
+
307
282
if ( this . suppressedEvents . length === 0 ) {
308
283
return
309
284
}
310
285
311
286
for ( const [ event , ...args ] of this . suppressedEvents ) {
287
+ console . log ( 'replaying event' , event , ...args )
288
+
312
289
if ( event === 'error' ) {
313
290
Reflect . set ( this . socket , '_hadError' , true )
314
291
}
@@ -342,6 +319,7 @@ class SocketController {
342
319
method,
343
320
headers,
344
321
body : methodWithBody ? Readable . toWeb ( this . requestStream ) : null ,
322
+ // @ts -expect-error Not documented fetch property.
345
323
duplex : methodWithBody ? 'half' : undefined ,
346
324
credentials : 'same-origin' ,
347
325
} )
@@ -356,8 +334,6 @@ class SocketController {
356
334
}
357
335
358
336
private onRequestEnd ( ) {
359
- this . requestParser . free ( )
360
-
361
337
invariant (
362
338
this . requestStream ,
363
339
'Failed to handle the request end: request stream is missing'
@@ -376,7 +352,7 @@ class SocketController {
376
352
statusText,
377
353
headers : parseRawHeaders ( rawHeaders ) ,
378
354
} )
379
- this . onResponse ( response )
355
+ this . onResponse ( response , false )
380
356
}
381
357
382
358
private onResponseData ( chunk : Buffer ) {
@@ -388,8 +364,6 @@ class SocketController {
388
364
}
389
365
390
366
private onResponseEnd ( ) {
391
- this . responseParser . free ( )
392
-
393
367
invariant (
394
368
this . responseStream ,
395
369
'Failed to handle the response end: response stream is missing'
@@ -398,6 +372,57 @@ class SocketController {
398
372
}
399
373
}
400
374
375
+ type HttpMessageParserMessageType = 'request' | 'response'
376
+ interface HttpMessageParserCallbacks < T extends HttpMessageParserMessageType > {
377
+ onHeadersComplete ?: T extends 'request'
378
+ ? (
379
+ versionMajor : number ,
380
+ versionMinor : number ,
381
+ headers : Array < string > ,
382
+ idk : number ,
383
+ path : string
384
+ ) => void
385
+ : (
386
+ versionMajor : number ,
387
+ versionMinor : number ,
388
+ headers : Array < string > ,
389
+ method : string | undefined ,
390
+ url : string | undefined ,
391
+ status : number ,
392
+ statusText : string ,
393
+ upgrade : boolean ,
394
+ shouldKeepAlive : boolean
395
+ ) => void
396
+ onBody ?: ( chunk : Buffer ) => void
397
+ onMessageComplete ?: ( ) => void
398
+ }
399
+
400
+ class HttpMessageParser < T extends HttpMessageParserMessageType > {
401
+ private parser : HTTPParser
402
+
403
+ constructor ( messageType : T , callbacks : HttpMessageParserCallbacks < T > ) {
404
+ this . parser = new HTTPParser ( )
405
+ this . parser . initialize (
406
+ messageType === 'request' ? HTTPParser . REQUEST : HTTPParser . RESPONSE ,
407
+ // Don't create any async resources here.
408
+ // This has to be "HTTPINCOMINGMESSAGE" in practice.
409
+ // @see https://github.com/nodejs/llhttp/issues/44#issuecomment-582499320
410
+ // new HTTPServerAsyncResource('INTERCEPTORINCOMINGMESSAGE', socket)
411
+ { }
412
+ )
413
+ this . parser [ HTTPParser . kOnHeadersComplete ] = callbacks . onHeadersComplete
414
+ this . parser [ HTTPParser . kOnMessageComplete ] = callbacks . onMessageComplete
415
+ }
416
+
417
+ public push ( chunk : Buffer ) : void {
418
+ this . parser . execute ( chunk )
419
+ }
420
+
421
+ public destroy ( ) : void {
422
+ this . parser . free ( )
423
+ }
424
+ }
425
+
401
426
function parseSocketConnectionUrl (
402
427
options : NormalizedSocketConnectOptions
403
428
) : URL {
@@ -429,21 +454,3 @@ function parseRawHeaders(rawHeaders: Array<string>): Headers {
429
454
}
430
455
return headers
431
456
}
432
-
433
- // MOCKED REQUEST:
434
- // 1. lookup // mock that's OK
435
- // 2. connect
436
- // 3. ready
437
- // HAS MOCK?
438
- // -> Y: data -> close
439
- // -> N (no response, non-existing host):
440
- // -> replayErrors()
441
- // -> lookup (error), error, close
442
-
443
- // BYPASSED REQUEST TO EXISTING HOST:
444
- // 1. lookup (no errors)
445
- // 2. (skip mockConnect), forward all socket events.
446
- // 3. emit "request" on the interceptor.
447
- // 4. HAS MOCK?
448
- // -> Y: respondWith: data -> close
449
- // -> N: do nothing
0 commit comments