Skip to content

Commit e79c93a

Browse files
haramjRafaelGSS
authored andcommitted
http: add server.keepAliveTimeoutBuffer option
PR-URL: #59243 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Jason Zhang <xzha4350@gmail.com>
1 parent 1e5f632 commit e79c93a

File tree

3 files changed

+83
-8
lines changed

3 files changed

+83
-8
lines changed

doc/api/http.md

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1942,18 +1942,39 @@ added: v8.0.0
19421942

19431943
The number of milliseconds of inactivity a server needs to wait for additional
19441944
incoming data, after it has finished writing the last response, before a socket
1945-
will be destroyed. If the server receives new data before the keep-alive
1946-
timeout has fired, it will reset the regular inactivity timeout, i.e.,
1947-
[`server.timeout`][].
1945+
will be destroyed.
1946+
1947+
This timeout value is combined with the
1948+
\[`server.keepAliveTimeoutBuffer`]\[] option to determine the actual socket
1949+
timeout, calculated as:
1950+
socketTimeout = keepAliveTimeout + keepAliveTimeoutBuffer
1951+
If the server receives new data before the keep-alive timeout has fired, it
1952+
will reset the regular inactivity timeout, i.e., [`server.timeout`][].
19481953

19491954
A value of `0` will disable the keep-alive timeout behavior on incoming
19501955
connections.
1951-
A value of `0` makes the http server behave similarly to Node.js versions prior
1956+
A value of `0` makes the HTTP server behave similarly to Node.js versions prior
19521957
to 8.0.0, which did not have a keep-alive timeout.
19531958

19541959
The socket timeout logic is set up on connection, so changing this value only
19551960
affects new connections to the server, not any existing connections.
19561961

1962+
### `server.keepAliveTimeoutBuffer`
1963+
1964+
<!-- YAML
1965+
added: REPLACEME
1966+
-->
1967+
1968+
* Type: {number} Timeout in milliseconds. **Default:** `1000` (1 second).
1969+
1970+
An additional buffer time added to the
1971+
[`server.keepAliveTimeout`][] to extend the internal socket timeout.
1972+
1973+
This buffer helps reduce connection reset (`ECONNRESET`) errors by increasing
1974+
the socket timeout slightly beyond the advertised keep-alive timeout.
1975+
1976+
This option applies only to new incoming connections.
1977+
19571978
### `server[Symbol.asyncDispose]()`
19581979

19591980
<!-- YAML

lib/_http_server.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const {
2525
ArrayIsArray,
2626
Error,
2727
MathMin,
28+
NumberIsFinite,
2829
ObjectKeys,
2930
ObjectSetPrototypeOf,
3031
ReflectApply,
@@ -184,8 +185,6 @@ const kConnections = Symbol('http.server.connections');
184185
const kConnectionsCheckingInterval = Symbol('http.server.connectionsCheckingInterval');
185186

186187
const HTTP_SERVER_TRACE_EVENT_NAME = 'http.server.request';
187-
// TODO(jazelly): make this configurable
188-
const HTTP_SERVER_KEEP_ALIVE_TIMEOUT_BUFFER = 1000;
189188

190189
class HTTPServerAsyncResource {
191190
constructor(type, socket) {
@@ -484,6 +483,14 @@ function storeHTTPOptions(options) {
484483
this.keepAliveTimeout = 5_000; // 5 seconds;
485484
}
486485

486+
const keepAliveTimeoutBuffer = options.keepAliveTimeoutBuffer;
487+
if (keepAliveTimeoutBuffer !== undefined) {
488+
validateInteger(keepAliveTimeoutBuffer, 'keepAliveTimeoutBuffer', 0);
489+
this.keepAliveTimeoutBuffer = keepAliveTimeoutBuffer;
490+
} else {
491+
this.keepAliveTimeoutBuffer = 1000;
492+
}
493+
487494
const connectionsCheckingInterval = options.connectionsCheckingInterval;
488495
if (connectionsCheckingInterval !== undefined) {
489496
validateInteger(connectionsCheckingInterval, 'connectionsCheckingInterval', 0);
@@ -546,6 +553,13 @@ function Server(options, requestListener) {
546553
}
547554

548555
storeHTTPOptions.call(this, options);
556+
557+
// Optional buffer added to the keep-alive timeout when setting socket timeouts.
558+
// Helps reduce ECONNRESET errors from clients by extending the internal timeout.
559+
// Default is 1000ms if not specified.
560+
const buf = options.keepAliveTimeoutBuffer;
561+
this.keepAliveTimeoutBuffer =
562+
(typeof buf === 'number' && NumberIsFinite(buf) && buf >= 0) ? buf : 1000;
549563
net.Server.call(
550564
this,
551565
{ allowHalfOpen: true, noDelay: options.noDelay ?? true,
@@ -1012,9 +1026,10 @@ function resOnFinish(req, res, socket, state, server) {
10121026
}
10131027
} else if (state.outgoing.length === 0) {
10141028
if (server.keepAliveTimeout && typeof socket.setTimeout === 'function') {
1015-
// Increase the internal timeout wrt the advertised value to reduce
1029+
// Extend the internal timeout by the configured buffer to reduce
10161030
// the likelihood of ECONNRESET errors.
1017-
socket.setTimeout(server.keepAliveTimeout + HTTP_SERVER_KEEP_ALIVE_TIMEOUT_BUFFER);
1031+
// This allows fine-tuning beyond the advertised keepAliveTimeout.
1032+
socket.setTimeout(server.keepAliveTimeout + server.keepAliveTimeoutBuffer);
10181033
state.keepAliveTimeoutSet = true;
10191034
}
10201035
} else {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const http = require('http');
5+
const assert = require('assert');
6+
7+
const server = http.createServer(common.mustCall((req, res) => {
8+
const body = 'buffer test\n';
9+
10+
res.writeHead(200, { 'Content-Length': body.length });
11+
res.write(body);
12+
res.end();
13+
}));
14+
15+
server.keepAliveTimeout = 100;
16+
17+
if (server.keepAliveTimeoutBuffer === undefined) {
18+
server.keepAliveTimeoutBuffer = 1000;
19+
}
20+
assert.strictEqual(server.keepAliveTimeoutBuffer, 1000);
21+
22+
server.listen(0, () => {
23+
http.get({
24+
port: server.address().port,
25+
path: '/',
26+
}, (res) => {
27+
res.resume();
28+
server.close();
29+
});
30+
});
31+
32+
{
33+
const customBuffer = 3000;
34+
const server = http.createServer(() => {});
35+
server.keepAliveTimeout = 200;
36+
server.keepAliveTimeoutBuffer = customBuffer;
37+
assert.strictEqual(server.keepAliveTimeoutBuffer, customBuffer);
38+
server.close();
39+
}

0 commit comments

Comments
 (0)