http: reduce likelihood of race conditions on keep-alive timeout

Fixes: https://github.com/nodejs/node/issues/52649
Refs: https://github.com/nodejs/node/issues/54293

Co-authored-by: Arrigo Zanette <zanettea@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/54863
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: LiviaMedeiros <livia@cirno.name>
Reviewed-By: Jake Yuesong Li <jake.yuesong@gmail.com>
Reviewed-By: Ethan Arrowood <ethan@arrowood.dev>
This commit is contained in:
jazelly
2024-09-09 22:47:10 +09:30
committed by James M Snell
parent aff2214ca9
commit 05ad947899
3 changed files with 57 additions and 5 deletions

View File

@@ -49,6 +49,9 @@ const {
const kOnKeylog = Symbol('onkeylog');
const kRequestOptions = Symbol('requestOptions');
const kRequestAsyncResource = Symbol('requestAsyncResource');
// TODO(jazelly): make this configurable
const HTTP_AGENT_KEEP_ALIVE_TIMEOUT_BUFFER = 1000;
// New Agent code.
// The largest departure from the previous implementation is that
@@ -473,6 +476,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'];
@@ -481,9 +485,15 @@ Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) {
const hint = /^timeout=(\d+)/.exec(keepAliveHint)?.[1];
if (hint) {
const serverHintTimeout = NumberParseInt(hint) * 1000;
if (serverHintTimeout < agentTimeout) {
// Let the timer expire before the announced timeout to reduce
// the likelihood of ECONNRESET errors
let serverHintTimeout = (NumberParseInt(hint) * 1000) - HTTP_AGENT_KEEP_ALIVE_TIMEOUT_BUFFER;
serverHintTimeout = serverHintTimeout > 0 ? serverHintTimeout : 0;
if (serverHintTimeout === 0) {
// Cannot safely reuse the socket because the server timeout is
// too short
canKeepSocketAlive = false;
} else if (serverHintTimeout < agentTimeout) {
agentTimeout = serverHintTimeout;
}
}
@@ -494,7 +504,7 @@ Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) {
socket.setTimeout(agentTimeout);
}
return true;
return canKeepSocketAlive;
};
Agent.prototype.reuseSocket = function reuseSocket(socket, req) {