Skip to content

Commit 9ec64a0

Browse files
committed
refactor: avoid http2 dynamic dispatch in socket handlers
Steps towards more clean separation between h1 and h2. Refs: #2816
1 parent 2a368b2 commit 9ec64a0

File tree

2 files changed

+82
-64
lines changed

2 files changed

+82
-64
lines changed

lib/core/symbols.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,6 @@ module.exports = {
5959
kHTTP2CopyHeaders: Symbol('http2 copy headers'),
6060
kHTTPConnVersion: Symbol('http connection version'),
6161
kRetryHandlerDefaultRetry: Symbol('retry agent default retry'),
62-
kConstruct: Symbol('constructable')
62+
kConstruct: Symbol('constructable'),
63+
kListeners: Symbol('listeners')
6364
}

lib/dispatcher/client.js

Lines changed: 80 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ const {
7272
kLocalAddress,
7373
kMaxResponseSize,
7474
kHTTPConnVersion,
75+
kListeners,
7576
// HTTP2
7677
kHost,
7778
kHTTP2Session,
@@ -111,6 +112,20 @@ const FastBuffer = Buffer[Symbol.species]
111112

112113
const kClosedResolve = Symbol('kClosedResolve')
113114

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+
114129
/**
115130
* @type {import('../../types/client.js').default}
116131
*/
@@ -803,11 +818,8 @@ class Parser {
803818

804819
socket[kClient] = null
805820
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)
811823

812824
client[kSocket] = null
813825
client[kHTTP2Session] = null
@@ -1050,33 +1062,6 @@ function onParserTimeout (parser) {
10501062
}
10511063
}
10521064

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-
10801065
function onError (client, err) {
10811066
if (
10821067
client[kRunning] === 0 &&
@@ -1097,32 +1082,8 @@ function onError (client, err) {
10971082
}
10981083
}
10991084

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-
11141085
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
11261087

11271088
const err = this[kError] || new SocketError('closed', util.getSocketInfo(this))
11281089

@@ -1241,6 +1202,18 @@ async function connect (client) {
12411202

12421203
client[kHTTP2Session] = session
12431204
socket[kHTTP2Session] = session
1205+
1206+
addListener(socket, 'error', function (err) {
1207+
assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')
1208+
1209+
this[kError] = err
1210+
1211+
onError(this[kClient], err)
1212+
})
1213+
addListener(socket, 'end', function () {
1214+
util.destroy(this, new SocketError('other side closed', util.getSocketInfo(this)))
1215+
})
1216+
addListener(socket, 'close', onSocketClose)
12441217
} else {
12451218
if (!llhttpInstance) {
12461219
llhttpInstance = await llhttpPromise
@@ -1252,19 +1225,63 @@ async function connect (client) {
12521225
socket[kReset] = false
12531226
socket[kBlocking] = false
12541227
socket[kParser] = new Parser(client, socket, llhttpInstance)
1228+
1229+
addListener(socket, 'error', function (err) {
1230+
const { [kParser]: parser } = this
1231+
1232+
assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')
1233+
1234+
// On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded
1235+
// to the user.
1236+
if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) {
1237+
// We treat all incoming data so for as a valid response.
1238+
parser.onMessageComplete()
1239+
return
1240+
}
1241+
1242+
this[kError] = err
1243+
1244+
onError(this[kClient], err)
1245+
})
1246+
addListener(socket, 'readable', function () {
1247+
const { [kParser]: parser } = this
1248+
if (parser) {
1249+
parser.readMore()
1250+
}
1251+
})
1252+
addListener(socket, 'end', function () {
1253+
const { [kParser]: parser } = this
1254+
1255+
if (parser.statusCode && !parser.shouldKeepAlive) {
1256+
// We treat all incoming data so far as a valid response.
1257+
parser.onMessageComplete()
1258+
return
1259+
}
1260+
1261+
util.destroy(this, new SocketError('other side closed', util.getSocketInfo(this)))
1262+
})
1263+
addListener(socket, 'close', function () {
1264+
const { [kParser]: parser } = this
1265+
1266+
if (parser) {
1267+
if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) {
1268+
// We treat all incoming data so far as a valid response.
1269+
parser.onMessageComplete()
1270+
}
1271+
1272+
this[kParser].destroy()
1273+
this[kParser] = null
1274+
}
1275+
1276+
onSocketClose.call(this)
1277+
})
12551278
}
12561279

12571280
socket[kCounter] = 0
12581281
socket[kMaxRequests] = client[kMaxRequests]
12591282
socket[kClient] = client
12601283
socket[kError] = null
12611284

1262-
socket
1263-
.on('error', onSocketError)
1264-
.on('readable', onSocketReadable)
1265-
.on('end', onSocketEnd)
1266-
.on('close', onSocketClose)
1267-
12681285
client[kSocket] = socket
12691286

12701287
if (channels.connected.hasSubscribers) {

0 commit comments

Comments
 (0)