Skip to content

Commit 1dbad20

Browse files
haramjrichardlau
authored andcommitted
http: add Agent.agentKeepAliveTimeoutBuffer option
PR-URL: #59315 Reviewed-By: Jason Zhang <[email protected]>
1 parent 20aec23 commit 1dbad20

File tree

3 files changed

+63
-3
lines changed

3 files changed

+63
-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
- v15.6.0
121125
- v14.17.0
@@ -148,6 +152,12 @@ changes:
148152
the [initial delay][]
149153
for TCP Keep-Alive packets. Ignored when the
150154
`keepAlive` option is `false` or `undefined`. **Default:** `1000`.
155+
* `agentKeepAliveTimeoutBuffer` {number} Milliseconds to subtract from
156+
the server-provided `keep-alive: timeout=...` hint when determining socket
157+
expiration time. This buffer helps ensure the agent closes the socket
158+
slightly before the server does, reducing the chance of sending a request
159+
on a socket that’s about to be closed by the server.
160+
**Default:** `1000`.
151161
* `maxSockets` {number} Maximum number of sockets to allow per host.
152162
If the same host opens multiple concurrent connections, each request
153163
will use new socket until the `maxSockets` value is reached.

lib/_http_agent.js

Lines changed: 9 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,
@@ -50,8 +51,6 @@ const kOnKeylog = Symbol('onkeylog');
5051
const kRequestOptions = Symbol('requestOptions');
5152
const kRequestAsyncResource = Symbol('requestAsyncResource');
5253

53-
// TODO(jazelly): make this configurable
54-
const HTTP_AGENT_KEEP_ALIVE_TIMEOUT_BUFFER = 1000;
5554
// New Agent code.
5655

5756
// The largest departure from the previous implementation is that
@@ -105,6 +104,13 @@ function Agent(options) {
105104
this.maxTotalSockets = this.options.maxTotalSockets;
106105
this.totalSocketCount = 0;
107106

107+
this.agentKeepAliveTimeoutBuffer =
108+
typeof this.options.agentKeepAliveTimeoutBuffer === 'number' &&
109+
this.options.agentKeepAliveTimeoutBuffer >= 0 &&
110+
NumberIsFinite(this.options.agentKeepAliveTimeoutBuffer) ?
111+
this.options.agentKeepAliveTimeoutBuffer :
112+
1000;
113+
108114
validateOneOf(this.scheduling, 'scheduling', ['fifo', 'lifo']);
109115

110116
if (this.maxTotalSockets !== undefined) {
@@ -481,7 +487,7 @@ Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) {
481487
if (hint) {
482488
// Let the timer expire before the announced timeout to reduce
483489
// the likelihood of ECONNRESET errors
484-
let serverHintTimeout = (NumberParseInt(hint) * 1000) - HTTP_AGENT_KEEP_ALIVE_TIMEOUT_BUFFER;
490+
let serverHintTimeout = (NumberParseInt(hint) * 1000) - this.agentKeepAliveTimeoutBuffer;
485491
serverHintTimeout = serverHintTimeout > 0 ? serverHintTimeout : 0;
486492
if (serverHintTimeout === 0) {
487493
// 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)