From b508db19c55aa5a768ed15a4bdde59cc16711761 Mon Sep 17 00:00:00 2001 From: Robert Nagy Date: Sun, 25 Feb 2024 11:15:23 +0100 Subject: [PATCH] refactor: split h1 and h2 connect into own methods --- lib/dispatcher/client.js | 202 ++++++++++++++++++++------------------- 1 file changed, 104 insertions(+), 98 deletions(-) diff --git a/lib/dispatcher/client.js b/lib/dispatcher/client.js index 2e05169d7ce..92e7dbd6a77 100644 --- a/lib/dispatcher/client.js +++ b/lib/dispatcher/client.js @@ -1176,107 +1176,14 @@ async function connect (client) { assert(socket) - const isH2 = socket.alpnProtocol === 'h2' - if (isH2) { - if (!h2ExperimentalWarned) { - h2ExperimentalWarned = true - process.emitWarning('H2 support is experimental, expect them to change at any time.', { - code: 'UNDICI-H2' - }) - } - - const session = http2.connect(client[kUrl], { - createConnection: () => socket, - peerMaxConcurrentStreams: client[kHTTP2SessionState].maxConcurrentStreams - }) - - client[kHTTPConnVersion] = 'h2' - session[kClient] = client - session[kSocket] = socket - session.on('error', onHttp2SessionError) - session.on('frameError', onHttp2FrameError) - session.on('end', onHttp2SessionEnd) - session.on('goaway', onHTTP2GoAway) - session.on('close', onSocketClose) - session.unref() - - client[kHTTP2Session] = session - socket[kHTTP2Session] = session - - addListener(socket, 'error', function (err) { - assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID') - - this[kError] = err - - onError(this[kClient], err) - }) - addListener(socket, 'end', function () { - util.destroy(this, new SocketError('other side closed', util.getSocketInfo(this))) - }) - addListener(socket, 'close', onSocketClose) + if (socket.alpnProtocol === 'h2') { + await onHTTP2Connect(client, socket) } else { - if (!llhttpInstance) { - llhttpInstance = await llhttpPromise - llhttpPromise = null - } - - socket[kNoRef] = false - socket[kWriting] = false - socket[kReset] = false - socket[kBlocking] = false - socket[kParser] = new Parser(client, socket, llhttpInstance) - - addListener(socket, 'error', function (err) { - const { [kParser]: parser } = this - - assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID') - - // On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded - // to the user. - if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) { - // We treat all incoming data so for as a valid response. - parser.onMessageComplete() - return - } - - this[kError] = err - - onError(this[kClient], err) - }) - addListener(socket, 'readable', function () { - const { [kParser]: parser } = this - if (parser) { - parser.readMore() - } - }) - addListener(socket, 'end', function () { - const { [kParser]: parser } = this - - if (parser.statusCode && !parser.shouldKeepAlive) { - // We treat all incoming data so far as a valid response. - parser.onMessageComplete() - return - } - - util.destroy(this, new SocketError('other side closed', util.getSocketInfo(this))) - }) - addListener(socket, 'close', function () { - const { [kParser]: parser } = this - - if (parser) { - if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) { - // We treat all incoming data so far as a valid response. - parser.onMessageComplete() - } - - this[kParser].destroy() - this[kParser] = null - } - - onSocketClose.call(this) - }) + await onHTTP1Connect(client, socket) } + addListener(socket, 'close', onSocketClose) + socket[kCounter] = 0 socket[kMaxRequests] = client[kMaxRequests] socket[kClient] = client @@ -2358,4 +2265,103 @@ function errorRequest (client, request, err) { } } +async function onHTTP1Connect (client, socket) { + if (!llhttpInstance) { + llhttpInstance = await llhttpPromise + llhttpPromise = null + } + + socket[kNoRef] = false + socket[kWriting] = false + socket[kReset] = false + socket[kBlocking] = false + socket[kParser] = new Parser(client, socket, llhttpInstance) + + addListener(socket, 'error', function (err) { + const { [kParser]: parser } = this + + assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID') + + // On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded + // to the user. + if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) { + // We treat all incoming data so for as a valid response. + parser.onMessageComplete() + return + } + + this[kError] = err + + onError(this[kClient], err) + }) + addListener(socket, 'readable', function () { + const { [kParser]: parser } = this + if (parser) { + parser.readMore() + } + }) + addListener(socket, 'end', function () { + const { [kParser]: parser } = this + + if (parser.statusCode && !parser.shouldKeepAlive) { + // We treat all incoming data so far as a valid response. + parser.onMessageComplete() + return + } + + util.destroy(this, new SocketError('other side closed', util.getSocketInfo(this))) + }) + addListener(socket, 'close', function () { + const { [kParser]: parser } = this + + if (parser) { + if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) { + // We treat all incoming data so far as a valid response. + parser.onMessageComplete() + } + + this[kParser].destroy() + this[kParser] = null + } + }) +} + +async function onHTTP2Connect (client, socket) { + if (!h2ExperimentalWarned) { + h2ExperimentalWarned = true + process.emitWarning('H2 support is experimental, expect them to change at any time.', { + code: 'UNDICI-H2' + }) + } + + const session = http2.connect(client[kUrl], { + createConnection: () => socket, + peerMaxConcurrentStreams: client[kHTTP2SessionState].maxConcurrentStreams + }) + + client[kHTTPConnVersion] = 'h2' + session[kClient] = client + session[kSocket] = socket + session.on('error', onHttp2SessionError) + session.on('frameError', onHttp2FrameError) + session.on('end', onHttp2SessionEnd) + session.on('goaway', onHTTP2GoAway) + session.on('close', onSocketClose) + session.unref() + + client[kHTTP2Session] = session + socket[kHTTP2Session] = session + + addListener(socket, 'error', function (err) { + assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID') + + this[kError] = err + + onError(this[kClient], err) + }) + addListener(socket, 'end', function () { + util.destroy(this, new SocketError('other side closed', util.getSocketInfo(this))) + }) +} + module.exports = Client