From 15140cd6bb9d617eb64c310ab54c07278849b19a Mon Sep 17 00:00:00 2001 From: Jonathan Gramain Date: Wed, 14 Oct 2020 19:09:31 -0700 Subject: [PATCH 1/2] bugfix: S3C-3388 network.http.Server.setKeepAliveTimeout() Add a helper function to set the keep-alive timeout of the node.js HTTP server managed by the Server class. --- lib/network/http/server.js | 17 +++++++++++++++ tests/unit/network/http/server.js | 35 +++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/lib/network/http/server.js b/lib/network/http/server.js index 27ea0838e..9fd3698b9 100644 --- a/lib/network/http/server.js +++ b/lib/network/http/server.js @@ -43,6 +43,7 @@ class Server { this._address = checkSupportIPv6() ? '::' : '0.0.0.0'; this._server = null; this._logger = logger; + this._keepAliveTimeout = null; // null: use default node.js value } /** @@ -57,6 +58,19 @@ class Server { return this; } + /** + * Set the keep-alive timeout after which inactive client + * connections are automatically closed (default should be + * 5 seconds in node.js) + * + * @param {number} keepAliveTimeout - keep-alive timeout in milliseconds + * @return {Server} - returns this + */ + setKeepAliveTimeout(keepAliveTimeout) { + this._keepAliveTimeout = keepAliveTimeout; + return this; + } + /** * Getter to access to the http/https server * @@ -401,6 +415,9 @@ class Server { this._server = http.createServer( (req, res) => this._onRequest(req, res)); } + if (this._keepAliveTimeout) { + this._server.keepAliveTimeout = this._keepAliveTimeout; + } this._server.on('error', err => this._onError(err)); this._server.on('secureConnection', diff --git a/tests/unit/network/http/server.js b/tests/unit/network/http/server.js index efd004b35..95f0001c9 100644 --- a/tests/unit/network/http/server.js +++ b/tests/unit/network/http/server.js @@ -180,4 +180,39 @@ describe('network.Server: ', () => { res.end('done'); }).start(); }); + + it('should automatically close idle connections with setKeepAliveTimeout()', done => { + const ws = new Server(3000, log); + ws.setKeepAliveTimeout(1000); + ws.onError(done).onListening(() => { + const options = { + hostname: '127.0.0.1', + port: 3000, + path: '/', + agent: new http.Agent({ keepAlive: true }), + }; + const req = http.request(options, res => { + res.on('data', () => {}); + res.on('end', () => {}); + }); + req.on('error', err => { + assert.ifError(err); + }); + req.end(); + }).onRequest((req, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end(); + assert.strictEqual(ws._server._connections, 1); + setTimeout(() => { + // client connection should remain open after less than 1000ms + assert.strictEqual(ws._server._connections, 1); + setTimeout(() => { + // client connection should have been closed after more than 1000ms + assert.strictEqual(ws._server.connections, 0); + ws.stop(); + ws.onStop(done); + }, 200); + }, 900); + }).start(); + }); }); From 918a1d7c899a7ecddcd1d6e7bbdd49b5becc5bd0 Mon Sep 17 00:00:00 2001 From: Jonathan Gramain Date: Thu, 15 Oct 2020 10:54:09 -0700 Subject: [PATCH 2/2] bugfix: S3C-3388 constants for HTTP connection timeouts Add constants related to HTTP client/server connection timeouts with values avoiding ECONNRESET errors due to the server closing connections that clients are attempting to reuse at the same moment. --- lib/constants.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/constants.js b/lib/constants.js index bca794258..b99b11d9e 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -72,4 +72,16 @@ module.exports = { permittedCapitalizedBuckets: { METADATA: true, }, + // HTTP server keep-alive timeout is set to a higher value than + // client's free sockets timeout to avoid the risk of triggering + // ECONNRESET errors if the server closes the connection at the + // exact moment clients attempt to reuse an established connection + // for a new request. + // + // Note: the ability to close inactive connections on the client + // after httpClientFreeSocketsTimeout milliseconds requires the + // use of "agentkeepalive" module instead of the regular node.js + // http.Agent. + httpServerKeepAliveTimeout: 60000, + httpClientFreeSocketTimeout: 55000, };