Compare commits

...

3 Commits

Author SHA1 Message Date
Taylor McKinnon c858c813e2 Merge remote-tracking branch 'origin/hotfix/7.7.0' into HEAD 2020-11-30 11:33:08 -08:00
Jonathan Gramain f25a8e57f0 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.
2020-11-30 11:29:25 -08:00
Jonathan Gramain 8e309cf569 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.
2020-11-30 11:28:36 -08:00
3 changed files with 64 additions and 0 deletions

View File

@ -72,6 +72,18 @@ 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,
// Default expiration value of the S3 pre-signed URL duration
// 604800 seconds (seven days).
defaultPreSignedURLExpiry: 7 * 24 * 60 * 60,

View File

@ -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',

View File

@ -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();
});
});