Skip to content

Commit 6ae202f

Browse files
haramjtargos
authored andcommitted
http: add Agent.agentKeepAliveTimeoutBuffer option
PR-URL: #59315 Reviewed-By: Jason Zhang <xzha4350@gmail.com>
1 parent 64ffde6 commit 6ae202f

File tree

3 files changed

+64
-3
lines changed

3 files changed

+64
-3
lines changed

doc/api/http.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ http.get({
116116
<!-- YAML
117117
added: v0.3.4
118118
changes:
119+
- version:
120+
- REPLACEME
121+
pr-url: https://github.com/nodejs/node/pull/59315
122+
description: Add support for `agentKeepAliveTimeoutBuffer`.
119123
- version:
120124
- v24.5.0
121125
pr-url: https://github.com/nodejs/node/pull/58980
@@ -156,6 +160,12 @@ changes:
156160
the [initial delay][]
157161
for TCP Keep-Alive packets. Ignored when the
158162
`keepAlive` option is `false` or `undefined`. **Default:** `1000`.
163+
* `agentKeepAliveTimeoutBuffer` {number} Milliseconds to subtract from
164+
the server-provided `keep-alive: timeout=...` hint when determining socket
165+
expiration time. This buffer helps ensure the agent closes the socket
166+
slightly before the server does, reducing the chance of sending a request
167+
on a socket that’s about to be closed by the server.
168+
**Default:** `1000`.
159169
* `maxSockets` {number} Maximum number of sockets to allow per host.
160170
If the same host opens multiple concurrent connections, each request
161171
will use new socket until the `maxSockets` value is reached.

lib/_http_agent.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
'use strict';
2323

2424
const {
25+
NumberIsFinite,
2526
NumberParseInt,
2627
ObjectKeys,
2728
ObjectSetPrototypeOf,
@@ -60,8 +61,6 @@ const kOnKeylog = Symbol('onkeylog');
6061
const kRequestOptions = Symbol('requestOptions');
6162
const kRequestAsyncResource = Symbol('requestAsyncResource');
6263

63-
// TODO(jazelly): make this configurable
64-
const HTTP_AGENT_KEEP_ALIVE_TIMEOUT_BUFFER = 1000;
6564
// New Agent code.
6665

6766
// The largest departure from the previous implementation is that
@@ -114,6 +113,14 @@ function Agent(options) {
114113
this.scheduling = this.options.scheduling || 'lifo';
115114
this.maxTotalSockets = this.options.maxTotalSockets;
116115
this.totalSocketCount = 0;
116+
117+
this.agentKeepAliveTimeoutBuffer =
118+
typeof this.options.agentKeepAliveTimeoutBuffer === 'number' &&
119+
this.options.agentKeepAliveTimeoutBuffer >= 0 &&
120+
NumberIsFinite(this.options.agentKeepAliveTimeoutBuffer) ?
121+
this.options.agentKeepAliveTimeoutBuffer :
122+
1000;
123+
117124
const proxyEnv = this.options.proxyEnv;
118125
if (typeof proxyEnv === 'object' && proxyEnv !== null) {
119126
this[kProxyConfig] = parseProxyConfigFromEnv(proxyEnv, this.protocol, this.keepAlive);
@@ -559,7 +566,7 @@ Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) {
559566
if (hint) {
560567
// Let the timer expire before the announced timeout to reduce
561568
// the likelihood of ECONNRESET errors
562-
let serverHintTimeout = (NumberParseInt(hint) * 1000) - HTTP_AGENT_KEEP_ALIVE_TIMEOUT_BUFFER;
569+
let serverHintTimeout = (NumberParseInt(hint) * 1000) - this.agentKeepAliveTimeoutBuffer;
563570
serverHintTimeout = serverHintTimeout > 0 ? serverHintTimeout : 0;
564571
if (serverHintTimeout === 0) {
565572
// Cannot safely reuse the socket because the server timeout is
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const http = require('http');
6+
7+
// Ensure agentKeepAliveTimeoutBuffer option sets the correct value or falls back to default.
8+
{
9+
const agent1 = new http.Agent({ agentKeepAliveTimeoutBuffer: 1500, keepAlive: true });
10+
assert.strictEqual(agent1.agentKeepAliveTimeoutBuffer, 1500);
11+
12+
const agent2 = new http.Agent({ agentKeepAliveTimeoutBuffer: -100, keepAlive: true });
13+
assert.strictEqual(agent2.agentKeepAliveTimeoutBuffer, 1000);
14+
15+
const agent3 = new http.Agent({ agentKeepAliveTimeoutBuffer: Infinity, keepAlive: true });
16+
assert.strictEqual(agent3.agentKeepAliveTimeoutBuffer, 1000);
17+
18+
const agent4 = new http.Agent({ keepAlive: true });
19+
assert.strictEqual(agent4.agentKeepAliveTimeoutBuffer, 1000);
20+
}
21+
22+
// Integration test with server sending Keep-Alive timeout header.
23+
{
24+
const SERVER_TIMEOUT = 3;
25+
const BUFFER = 1500;
26+
27+
const server = http.createServer((req, res) => {
28+
res.setHeader('Keep-Alive', `timeout=${SERVER_TIMEOUT}`);
29+
res.end('ok');
30+
});
31+
32+
server.listen(0, common.mustCall(() => {
33+
const agent = new http.Agent({ agentKeepAliveTimeoutBuffer: BUFFER, keepAlive: true });
34+
assert.strictEqual(agent.agentKeepAliveTimeoutBuffer, BUFFER);
35+
36+
http.get({ port: server.address().port, agent }, (res) => {
37+
res.resume();
38+
res.on('end', () => {
39+
agent.destroy();
40+
server.close();
41+
});
42+
});
43+
}));
44+
}

0 commit comments

Comments
 (0)