Skip to content

Commit eaedd21

Browse files
haramjaduh95
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 d99e125 commit eaedd21

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
@@ -1920,18 +1920,39 @@ added: v8.0.0
19201920

19211921
The number of milliseconds of inactivity a server needs to wait for additional
19221922
incoming data, after it has finished writing the last response, before a socket
1923-
will be destroyed. If the server receives new data before the keep-alive
1924-
timeout has fired, it will reset the regular inactivity timeout, i.e.,
1925-
[`server.timeout`][].
1923+
will be destroyed.
1924+
1925+
This timeout value is combined with the
1926+
\[`server.keepAliveTimeoutBuffer`]\[] option to determine the actual socket
1927+
timeout, calculated as:
1928+
socketTimeout = keepAliveTimeout + keepAliveTimeoutBuffer
1929+
If the server receives new data before the keep-alive timeout has fired, it
1930+
will reset the regular inactivity timeout, i.e., [`server.timeout`][].
19261931

19271932
A value of `0` will disable the keep-alive timeout behavior on incoming
19281933
connections.
1929-
A value of `0` makes the http server behave similarly to Node.js versions prior
1934+
A value of `0` makes the HTTP server behave similarly to Node.js versions prior
19301935
to 8.0.0, which did not have a keep-alive timeout.
19311936

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

1940+
### `server.keepAliveTimeoutBuffer`
1941+
1942+
<!-- YAML
1943+
added: REPLACEME
1944+
-->
1945+
1946+
* Type: {number} Timeout in milliseconds. **Default:** `1000` (1 second).
1947+
1948+
An additional buffer time added to the
1949+
[`server.keepAliveTimeout`][] to extend the internal socket timeout.
1950+
1951+
This buffer helps reduce connection reset (`ECONNRESET`) errors by increasing
1952+
the socket timeout slightly beyond the advertised keep-alive timeout.
1953+
1954+
This option applies only to new incoming connections.
1955+
19351956
### `server[Symbol.asyncDispose]()`
19361957

19371958
<!-- 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)