@@ -72,6 +72,7 @@ const {
72
72
kLocalAddress,
73
73
kMaxResponseSize,
74
74
kHTTPConnVersion,
75
+ kListeners,
75
76
// HTTP2
76
77
kHost,
77
78
kHTTP2Session,
@@ -111,6 +112,20 @@ const FastBuffer = Buffer[Symbol.species]
111
112
112
113
const kClosedResolve = Symbol ( 'kClosedResolve' )
113
114
115
+ function addListener ( obj , name , listener ) {
116
+ const listeners = ( obj [ kListeners ] ??= [ ] )
117
+ listeners . push ( [ name , listener ] )
118
+ obj . on ( name , listener )
119
+ return obj
120
+ }
121
+
122
+ function removeAllListeners ( obj ) {
123
+ for ( const [ name , listener ] of obj [ kListeners ] ?? [ ] ) {
124
+ obj . removeListener ( name , listener )
125
+ }
126
+ obj [ kListeners ] = null
127
+ }
128
+
114
129
/**
115
130
* @type {import('../../types/client.js').default }
116
131
*/
@@ -276,7 +291,7 @@ class Client extends DispatcherBase {
276
291
this [ kMaxRequests ] = maxRequestsPerClient
277
292
this [ kClosedResolve ] = null
278
293
this [ kMaxResponseSize ] = maxResponseSize > - 1 ? maxResponseSize : - 1
279
- this [ kHTTPConnVersion ] = 'h1'
294
+ this [ kHTTPConnVersion ] = null
280
295
281
296
// HTTP/2
282
297
this [ kHTTP2Session ] = null
@@ -803,11 +818,8 @@ class Parser {
803
818
804
819
socket [ kClient ] = null
805
820
socket [ kError ] = null
806
- socket
807
- . removeListener ( 'error' , onSocketError )
808
- . removeListener ( 'readable' , onSocketReadable )
809
- . removeListener ( 'end' , onSocketEnd )
810
- . removeListener ( 'close' , onSocketClose )
821
+
822
+ removeAllListeners ( socket )
811
823
812
824
client [ kSocket ] = null
813
825
client [ kHTTP2Session ] = null
@@ -1050,33 +1062,6 @@ function onParserTimeout (parser) {
1050
1062
}
1051
1063
}
1052
1064
1053
- function onSocketReadable ( ) {
1054
- const { [ kParser ] : parser } = this
1055
- if ( parser ) {
1056
- parser . readMore ( )
1057
- }
1058
- }
1059
-
1060
- function onSocketError ( err ) {
1061
- const { [ kClient ] : client , [ kParser ] : parser } = this
1062
-
1063
- assert ( err . code !== 'ERR_TLS_CERT_ALTNAME_INVALID' )
1064
-
1065
- if ( client [ kHTTPConnVersion ] !== 'h2' ) {
1066
- // On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded
1067
- // to the user.
1068
- if ( err . code === 'ECONNRESET' && parser . statusCode && ! parser . shouldKeepAlive ) {
1069
- // We treat all incoming data so for as a valid response.
1070
- parser . onMessageComplete ( )
1071
- return
1072
- }
1073
- }
1074
-
1075
- this [ kError ] = err
1076
-
1077
- onError ( this [ kClient ] , err )
1078
- }
1079
-
1080
1065
function onError ( client , err ) {
1081
1066
if (
1082
1067
client [ kRunning ] === 0 &&
@@ -1097,32 +1082,8 @@ function onError (client, err) {
1097
1082
}
1098
1083
}
1099
1084
1100
- function onSocketEnd ( ) {
1101
- const { [ kParser ] : parser , [ kClient ] : client } = this
1102
-
1103
- if ( client [ kHTTPConnVersion ] !== 'h2' ) {
1104
- if ( parser . statusCode && ! parser . shouldKeepAlive ) {
1105
- // We treat all incoming data so far as a valid response.
1106
- parser . onMessageComplete ( )
1107
- return
1108
- }
1109
- }
1110
-
1111
- util . destroy ( this , new SocketError ( 'other side closed' , util . getSocketInfo ( this ) ) )
1112
- }
1113
-
1114
1085
function onSocketClose ( ) {
1115
- const { [ kClient ] : client , [ kParser ] : parser } = this
1116
-
1117
- if ( client [ kHTTPConnVersion ] === 'h1' && parser ) {
1118
- if ( ! this [ kError ] && parser . statusCode && ! parser . shouldKeepAlive ) {
1119
- // We treat all incoming data so far as a valid response.
1120
- parser . onMessageComplete ( )
1121
- }
1122
-
1123
- this [ kParser ] . destroy ( )
1124
- this [ kParser ] = null
1125
- }
1086
+ const { [ kClient ] : client } = this
1126
1087
1127
1088
const err = this [ kError ] || new SocketError ( 'closed' , util . getSocketInfo ( this ) )
1128
1089
@@ -1215,56 +1176,19 @@ async function connect (client) {
1215
1176
1216
1177
assert ( socket )
1217
1178
1218
- const isH2 = socket . alpnProtocol === 'h2'
1219
- if ( isH2 ) {
1220
- if ( ! h2ExperimentalWarned ) {
1221
- h2ExperimentalWarned = true
1222
- process . emitWarning ( 'H2 support is experimental, expect them to change at any time.' , {
1223
- code : 'UNDICI-H2'
1224
- } )
1225
- }
1226
-
1227
- const session = http2 . connect ( client [ kUrl ] , {
1228
- createConnection : ( ) => socket ,
1229
- peerMaxConcurrentStreams : client [ kHTTP2SessionState ] . maxConcurrentStreams
1230
- } )
1231
-
1232
- client [ kHTTPConnVersion ] = 'h2'
1233
- session [ kClient ] = client
1234
- session [ kSocket ] = socket
1235
- session . on ( 'error' , onHttp2SessionError )
1236
- session . on ( 'frameError' , onHttp2FrameError )
1237
- session . on ( 'end' , onHttp2SessionEnd )
1238
- session . on ( 'goaway' , onHTTP2GoAway )
1239
- session . on ( 'close' , onSocketClose )
1240
- session . unref ( )
1241
-
1242
- client [ kHTTP2Session ] = session
1243
- socket [ kHTTP2Session ] = session
1179
+ if ( socket . alpnProtocol === 'h2' ) {
1180
+ await connectH2 ( client , socket )
1244
1181
} else {
1245
- if ( ! llhttpInstance ) {
1246
- llhttpInstance = await llhttpPromise
1247
- llhttpPromise = null
1248
- }
1249
-
1250
- socket [ kNoRef ] = false
1251
- socket [ kWriting ] = false
1252
- socket [ kReset ] = false
1253
- socket [ kBlocking ] = false
1254
- socket [ kParser ] = new Parser ( client , socket , llhttpInstance )
1182
+ await connectH1 ( client , socket )
1255
1183
}
1256
1184
1185
+ addListener ( socket , 'close' , onSocketClose )
1186
+
1257
1187
socket [ kCounter ] = 0
1258
1188
socket [ kMaxRequests ] = client [ kMaxRequests ]
1259
1189
socket [ kClient ] = client
1260
1190
socket [ kError ] = null
1261
1191
1262
- socket
1263
- . on ( 'error' , onSocketError )
1264
- . on ( 'readable' , onSocketReadable )
1265
- . on ( 'end' , onSocketEnd )
1266
- . on ( 'close' , onSocketClose )
1267
-
1268
1192
client [ kSocket ] = socket
1269
1193
1270
1194
if ( channels . connected . hasSubscribers ) {
@@ -1475,10 +1399,15 @@ function shouldSendContentLength (method) {
1475
1399
1476
1400
function write ( client , request ) {
1477
1401
if ( client [ kHTTPConnVersion ] === 'h2' ) {
1478
- writeH2 ( client , client [ kHTTP2Session ] , request )
1479
- return
1402
+ // TODO (fix): Why does this not return the value
1403
+ // from writeH2.
1404
+ writeH2 ( client , request )
1405
+ } else {
1406
+ return writeH1 ( client , request )
1480
1407
}
1408
+ }
1481
1409
1410
+ function writeH1 ( client , request ) {
1482
1411
const { method, path, host, upgrade, blocking, reset } = request
1483
1412
1484
1413
let { body, headers, contentLength } = request
@@ -1656,7 +1585,8 @@ function write (client, request) {
1656
1585
return true
1657
1586
}
1658
1587
1659
- function writeH2 ( client , session , request ) {
1588
+ function writeH2 ( client , request ) {
1589
+ const session = client [ kHTTP2Session ]
1660
1590
const { body, method, path, host, upgrade, expectContinue, signal, headers : reqHeaders } = request
1661
1591
1662
1592
let headers
@@ -2341,4 +2271,106 @@ function errorRequest (client, request, err) {
2341
2271
}
2342
2272
}
2343
2273
2274
+ async function connectH1 ( client , socket ) {
2275
+ client [ kHTTPConnVersion ] = 'h1'
2276
+
2277
+ if ( ! llhttpInstance ) {
2278
+ llhttpInstance = await llhttpPromise
2279
+ llhttpPromise = null
2280
+ }
2281
+
2282
+ socket [ kNoRef ] = false
2283
+ socket [ kWriting ] = false
2284
+ socket [ kReset ] = false
2285
+ socket [ kBlocking ] = false
2286
+ socket [ kParser ] = new Parser ( client , socket , llhttpInstance )
2287
+
2288
+ addListener ( socket , 'error' , function ( err ) {
2289
+ const { [ kParser ] : parser } = this
2290
+
2291
+ assert ( err . code !== 'ERR_TLS_CERT_ALTNAME_INVALID' )
2292
+
2293
+ // On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded
2294
+ // to the user.
2295
+ if ( err . code === 'ECONNRESET' && parser . statusCode && ! parser . shouldKeepAlive ) {
2296
+ // We treat all incoming data so for as a valid response.
2297
+ parser . onMessageComplete ( )
2298
+ return
2299
+ }
2300
+
2301
+ this [ kError ] = err
2302
+
2303
+ onError ( this [ kClient ] , err )
2304
+ } )
2305
+ addListener ( socket , 'readable' , function ( ) {
2306
+ const { [ kParser ] : parser } = this
2307
+ if ( parser ) {
2308
+ parser . readMore ( )
2309
+ }
2310
+ } )
2311
+ addListener ( socket , 'end' , function ( ) {
2312
+ const { [ kParser ] : parser } = this
2313
+
2314
+ if ( parser . statusCode && ! parser . shouldKeepAlive ) {
2315
+ // We treat all incoming data so far as a valid response.
2316
+ parser . onMessageComplete ( )
2317
+ return
2318
+ }
2319
+
2320
+ util . destroy ( this , new SocketError ( 'other side closed' , util . getSocketInfo ( this ) ) )
2321
+ } )
2322
+ addListener ( socket , 'close' , function ( ) {
2323
+ const { [ kParser ] : parser } = this
2324
+
2325
+ if ( parser ) {
2326
+ if ( ! this [ kError ] && parser . statusCode && ! parser . shouldKeepAlive ) {
2327
+ // We treat all incoming data so far as a valid response.
2328
+ parser . onMessageComplete ( )
2329
+ }
2330
+
2331
+ this [ kParser ] . destroy ( )
2332
+ this [ kParser ] = null
2333
+ }
2334
+ } )
2335
+ }
2336
+
2337
+ async function connectH2 ( client , socket ) {
2338
+ client [ kHTTPConnVersion ] = 'h2'
2339
+
2340
+ if ( ! h2ExperimentalWarned ) {
2341
+ h2ExperimentalWarned = true
2342
+ process . emitWarning ( 'H2 support is experimental, expect them to change at any time.' , {
2343
+ code : 'UNDICI-H2'
2344
+ } )
2345
+ }
2346
+
2347
+ const session = http2 . connect ( client [ kUrl ] , {
2348
+ createConnection : ( ) => socket ,
2349
+ peerMaxConcurrentStreams : client [ kHTTP2SessionState ] . maxConcurrentStreams
2350
+ } )
2351
+
2352
+ session [ kClient ] = client
2353
+ session [ kSocket ] = socket
2354
+ session . on ( 'error' , onHttp2SessionError )
2355
+ session . on ( 'frameError' , onHttp2FrameError )
2356
+ session . on ( 'end' , onHttp2SessionEnd )
2357
+ session . on ( 'goaway' , onHTTP2GoAway )
2358
+ session . on ( 'close' , onSocketClose )
2359
+ session . unref ( )
2360
+
2361
+ client [ kHTTP2Session ] = session
2362
+ socket [ kHTTP2Session ] = session
2363
+
2364
+ addListener ( socket , 'error' , function ( err ) {
2365
+ assert ( err . code !== 'ERR_TLS_CERT_ALTNAME_INVALID' )
2366
+
2367
+ this [ kError ] = err
2368
+
2369
+ onError ( this [ kClient ] , err )
2370
+ } )
2371
+ addListener ( socket , 'end' , function ( ) {
2372
+ util . destroy ( this , new SocketError ( 'other side closed' , util . getSocketInfo ( this ) ) )
2373
+ } )
2374
+ }
2375
+
2344
2376
module . exports = Client
0 commit comments