http: add server.keepAliveTimeoutBuffer option

PR-URL: https://github.com/nodejs/node/pull/59243
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Jason Zhang <xzha4350@gmail.com>
This commit is contained in:
Haram Jeong 2025-08-04 22:47:09 +09:00 committed by GitHub
parent 5bea645e4b
commit f7c2a7ed4a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 83 additions and 8 deletions

View file

@ -1942,18 +1942,39 @@ added: v8.0.0
The number of milliseconds of inactivity a server needs to wait for additional
incoming data, after it has finished writing the last response, before a socket
will be destroyed. If the server receives new data before the keep-alive
timeout has fired, it will reset the regular inactivity timeout, i.e.,
[`server.timeout`][].
will be destroyed.
This timeout value is combined with the
\[`server.keepAliveTimeoutBuffer`]\[] option to determine the actual socket
timeout, calculated as:
socketTimeout = keepAliveTimeout + keepAliveTimeoutBuffer
If the server receives new data before the keep-alive timeout has fired, it
will reset the regular inactivity timeout, i.e., [`server.timeout`][].
A value of `0` will disable the keep-alive timeout behavior on incoming
connections.
A value of `0` makes the http server behave similarly to Node.js versions prior
A value of `0` makes the HTTP server behave similarly to Node.js versions prior
to 8.0.0, which did not have a keep-alive timeout.
The socket timeout logic is set up on connection, so changing this value only
affects new connections to the server, not any existing connections.
### `server.keepAliveTimeoutBuffer`
<!-- YAML
added: REPLACEME
-->
* Type: {number} Timeout in milliseconds. **Default:** `1000` (1 second).
An additional buffer time added to the
[`server.keepAliveTimeout`][] to extend the internal socket timeout.
This buffer helps reduce connection reset (`ECONNRESET`) errors by increasing
the socket timeout slightly beyond the advertised keep-alive timeout.
This option applies only to new incoming connections.
### `server[Symbol.asyncDispose]()`
<!-- YAML

View file

@ -25,6 +25,7 @@ const {
ArrayIsArray,
Error,
MathMin,
NumberIsFinite,
ObjectKeys,
ObjectSetPrototypeOf,
ReflectApply,
@ -185,8 +186,6 @@ const kConnections = Symbol('http.server.connections');
const kConnectionsCheckingInterval = Symbol('http.server.connectionsCheckingInterval');
const HTTP_SERVER_TRACE_EVENT_NAME = 'http.server.request';
// TODO(jazelly): make this configurable
const HTTP_SERVER_KEEP_ALIVE_TIMEOUT_BUFFER = 1000;
class HTTPServerAsyncResource {
constructor(type, socket) {
@ -486,6 +485,14 @@ function storeHTTPOptions(options) {
this.keepAliveTimeout = 5_000; // 5 seconds;
}
const keepAliveTimeoutBuffer = options.keepAliveTimeoutBuffer;
if (keepAliveTimeoutBuffer !== undefined) {
validateInteger(keepAliveTimeoutBuffer, 'keepAliveTimeoutBuffer', 0);
this.keepAliveTimeoutBuffer = keepAliveTimeoutBuffer;
} else {
this.keepAliveTimeoutBuffer = 1000;
}
const connectionsCheckingInterval = options.connectionsCheckingInterval;
if (connectionsCheckingInterval !== undefined) {
validateInteger(connectionsCheckingInterval, 'connectionsCheckingInterval', 0);
@ -548,6 +555,13 @@ function Server(options, requestListener) {
}
storeHTTPOptions.call(this, options);
// Optional buffer added to the keep-alive timeout when setting socket timeouts.
// Helps reduce ECONNRESET errors from clients by extending the internal timeout.
// Default is 1000ms if not specified.
const buf = options.keepAliveTimeoutBuffer;
this.keepAliveTimeoutBuffer =
(typeof buf === 'number' && NumberIsFinite(buf) && buf >= 0) ? buf : 1000;
net.Server.call(
this,
{ allowHalfOpen: true, noDelay: options.noDelay ?? true,
@ -1014,9 +1028,10 @@ function resOnFinish(req, res, socket, state, server) {
}
} else if (state.outgoing.length === 0) {
if (server.keepAliveTimeout && typeof socket.setTimeout === 'function') {
// Increase the internal timeout wrt the advertised value to reduce
// Extend the internal timeout by the configured buffer to reduce
// the likelihood of ECONNRESET errors.
socket.setTimeout(server.keepAliveTimeout + HTTP_SERVER_KEEP_ALIVE_TIMEOUT_BUFFER);
// This allows fine-tuning beyond the advertised keepAliveTimeout.
socket.setTimeout(server.keepAliveTimeout + server.keepAliveTimeoutBuffer);
state.keepAliveTimeoutSet = true;
}
} else {

View file

@ -0,0 +1,39 @@
'use strict';
const common = require('../common');
const http = require('http');
const assert = require('assert');
const server = http.createServer(common.mustCall((req, res) => {
const body = 'buffer test\n';
res.writeHead(200, { 'Content-Length': body.length });
res.write(body);
res.end();
}));
server.keepAliveTimeout = 100;
if (server.keepAliveTimeoutBuffer === undefined) {
server.keepAliveTimeoutBuffer = 1000;
}
assert.strictEqual(server.keepAliveTimeoutBuffer, 1000);
server.listen(0, () => {
http.get({
port: server.address().port,
path: '/',
}, (res) => {
res.resume();
server.close();
});
});
{
const customBuffer = 3000;
const server = http.createServer(() => {});
server.keepAliveTimeout = 200;
server.keepAliveTimeoutBuffer = customBuffer;
assert.strictEqual(server.keepAliveTimeoutBuffer, customBuffer);
server.close();
}