http: add Agent.agentKeepAliveTimeoutBuffer option · nodejs/node@6ae202f

3 files changed

lines changed

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.

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

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+

}