Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions doc/api/http.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ http.get({
<!-- YAML
added: v0.3.4
changes:
- version:
- REPLACEME
pr-url: https://github.com/nodejs/node/pull/59315
description: Add support for `agentKeepAliveTimeoutBuffer`.
- version:
- v24.5.0
pr-url: https://github.com/nodejs/node/pull/58980
Expand Down Expand Up @@ -156,6 +160,12 @@ changes:
the [initial delay][]
for TCP Keep-Alive packets. Ignored when the
`keepAlive` option is `false` or `undefined`. **Default:** `1000`.
* `agentKeepAliveTimeoutBuffer` {number} Milliseconds to subtract from
the server-provided `keep-alive: timeout=...` hint when determining socket
expiration time. This buffer helps ensure the agent closes the socket
slightly before the server does, reducing the chance of sending a request
on a socket that’s about to be closed by the server.
**Default:** `1000`.
* `maxSockets` {number} Maximum number of sockets to allow per host.
If the same host opens multiple concurrent connections, each request
will use new socket until the `maxSockets` value is reached.
Expand Down
13 changes: 10 additions & 3 deletions lib/_http_agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
'use strict';

const {
NumberIsFinite,
NumberParseInt,
ObjectKeys,
ObjectSetPrototypeOf,
Expand Down Expand Up @@ -60,8 +61,6 @@ const kOnKeylog = Symbol('onkeylog');
const kRequestOptions = Symbol('requestOptions');
const kRequestAsyncResource = Symbol('requestAsyncResource');

// TODO(jazelly): make this configurable
const HTTP_AGENT_KEEP_ALIVE_TIMEOUT_BUFFER = 1000;
// New Agent code.

// The largest departure from the previous implementation is that
Expand Down Expand Up @@ -114,6 +113,14 @@ function Agent(options) {
this.scheduling = this.options.scheduling || 'lifo';
this.maxTotalSockets = this.options.maxTotalSockets;
this.totalSocketCount = 0;

this.agentKeepAliveTimeoutBuffer =
typeof this.options.agentKeepAliveTimeoutBuffer === 'number' &&
this.options.agentKeepAliveTimeoutBuffer >= 0 &&
NumberIsFinite(this.options.agentKeepAliveTimeoutBuffer) ?
this.options.agentKeepAliveTimeoutBuffer :
1000;

const proxyEnv = this.options.proxyEnv;
if (typeof proxyEnv === 'object' && proxyEnv !== null) {
this[kProxyConfig] = parseProxyConfigFromEnv(proxyEnv, this.protocol, this.keepAlive);
Expand Down Expand Up @@ -559,7 +566,7 @@ Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) {
if (hint) {
// Let the timer expire before the announced timeout to reduce
// the likelihood of ECONNRESET errors
let serverHintTimeout = (NumberParseInt(hint) * 1000) - HTTP_AGENT_KEEP_ALIVE_TIMEOUT_BUFFER;
let serverHintTimeout = (NumberParseInt(hint) * 1000) - this.agentKeepAliveTimeoutBuffer;
serverHintTimeout = serverHintTimeout > 0 ? serverHintTimeout : 0;
if (serverHintTimeout === 0) {
// Cannot safely reuse the socket because the server timeout is
Expand Down
44 changes: 44 additions & 0 deletions test/parallel/test-http-agent-keep-alive-timeout-buffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use strict';

const common = require('../common');
const assert = require('assert');
const http = require('http');

// Ensure agentKeepAliveTimeoutBuffer option sets the correct value or falls back to default.
{
const agent1 = new http.Agent({ agentKeepAliveTimeoutBuffer: 1500, keepAlive: true });
assert.strictEqual(agent1.agentKeepAliveTimeoutBuffer, 1500);

const agent2 = new http.Agent({ agentKeepAliveTimeoutBuffer: -100, keepAlive: true });
assert.strictEqual(agent2.agentKeepAliveTimeoutBuffer, 1000);

const agent3 = new http.Agent({ agentKeepAliveTimeoutBuffer: Infinity, keepAlive: true });
assert.strictEqual(agent3.agentKeepAliveTimeoutBuffer, 1000);

const agent4 = new http.Agent({ keepAlive: true });
assert.strictEqual(agent4.agentKeepAliveTimeoutBuffer, 1000);
}

// Integration test with server sending Keep-Alive timeout header.
{
const SERVER_TIMEOUT = 3;
const BUFFER = 1500;

const server = http.createServer((req, res) => {
res.setHeader('Keep-Alive', `timeout=${SERVER_TIMEOUT}`);
res.end('ok');
});

server.listen(0, common.mustCall(() => {
const agent = new http.Agent({ agentKeepAliveTimeoutBuffer: BUFFER, keepAlive: true });
assert.strictEqual(agent.agentKeepAliveTimeoutBuffer, BUFFER);

http.get({ port: server.address().port, agent }, (res) => {
res.resume();
res.on('end', () => {
agent.destroy();
server.close();
});
});
}));
}
Loading