From 2124b55046f12deb8688f9bc1f18c6421d5e5dbf Mon Sep 17 00:00:00 2001 From: Arrigo Zanette Date: Mon, 22 Apr 2024 15:40:12 +0200 Subject: [PATCH] net: reduce likelihood of race conditions on keep-alive timeout calculation between http1.1 servers and clients reduced likelihood of race conditions on keep-alive timeout calculation between http1.1 servers and clients and honor server keep-alive timeout when agentTimeout is not set Fixes: https://github.com/nodejs/node/issues/47130 Fixes: https://github.com/nodejs/node/issues/52649 --- lib/_http_agent.js | 14 ++++++++++---- lib/_http_server.js | 4 +++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/_http_agent.js b/lib/_http_agent.js index a4829526f6e138..629601885cab74 100644 --- a/lib/_http_agent.js +++ b/lib/_http_agent.js @@ -488,6 +488,7 @@ Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) { socket.unref(); let agentTimeout = this.options.timeout || 0; + let canKeepSocketAlive = true; if (socket._httpMessage?.res) { const keepAliveHint = socket._httpMessage.res.headers['keep-alive']; @@ -496,9 +497,14 @@ Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) { const hint = RegExpPrototypeExec(/^timeout=(\d+)/, keepAliveHint)?.[1]; if (hint) { - const serverHintTimeout = NumberParseInt(hint) * 1000; - - if (serverHintTimeout < agentTimeout) { + // Let the timer expires before the announced timeout to reduce + // the likelihood of ECONNRESET errors + let serverHintTimeout = (NumberParseInt(hint) * 1000) - 1000; + serverHintTimeout = serverHintTimeout > 0 ? serverHintTimeout : 0; + if (serverHintTimeout === 0) { + // Cannot safely reuse the socket because the server timeout is too short + canKeepSocketAlive = false; + } else if (!agentTimeout || serverHintTimeout < agentTimeout) { agentTimeout = serverHintTimeout; } } @@ -509,7 +515,7 @@ Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) { socket.setTimeout(agentTimeout); } - return true; + return canKeepSocketAlive; }; Agent.prototype.reuseSocket = function reuseSocket(socket, req) { diff --git a/lib/_http_server.js b/lib/_http_server.js index d67669fea9433b..bbfda91401d26f 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -1010,7 +1010,9 @@ function resOnFinish(req, res, socket, state, server) { } } else if (state.outgoing.length === 0) { if (server.keepAliveTimeout && typeof socket.setTimeout === 'function') { - socket.setTimeout(server.keepAliveTimeout); + // Increase the internal timeout wrt the advertised value to reduce likeliwood of ECONNRESET errors + // due to race conditions between the client and server timeout calculation + socket.setTimeout(server.keepAliveTimeout + 1000); state.keepAliveTimeoutSet = true; } } else {