Reputation: 19664
I am trying to 'gracefully' close a net.Server instance (created with app.listen()) if an un-handled error is thrown. Server creation occurs in my bin/www script. All error handling and routing middleware configuration is defined in index.js.
In my application configuration module (index.js) I have error handling middleware that checks that each error is handled. If the error is not handled then a 'close' event is emitted.
Note: Each req and res is wrapped in a domain. I am using express-domain-middleware middleware module to listen for error events on each req domain and route the error to my error handling. I only mention this in case it might be the culprit.
The 'close_server' event handler should:
The optional callback provided to server.close() never seems to be invoked and I'm not sure why. To test this I am making a single request which throws an error. The process is only closed after the timer expires (10 seconds has elapsed).
Could there be something holding open a connection in the server? Why is the server.close() callback never called?
Thanks!
Update I was using Chrome to make a request to the server. It appears that the browser is holding open a connection. If I make the request using curl it works as expected.
See this issue
index.js
app.use(function (err, req, res, next) {
if (typeof err.statusCode !== 'undefined') {
if (err.statusCode >= 500) {
Logger.error({error: err});
return next(err);
} else {
Logger.warn({warn: err});
return next(err);
}
} else {
//The error is un-handled and the server needs to go bye, bye
var unhandledError = new UnhandledError(util.format('%s:%s', req.method, req.originalUrl), 'Server shutting down!', err.stack, 500);
Logger.fatal({fatal: unhandledError});
res.status(500).send('Server Error');
app.emit('close_server', unhandledError);
}
});
bin/www
#!/usr/bin/env node
var app = require('../index');
var port = config.applicationPort;
app.set('port', port);
var server = app.listen(app.get('port'));
/*
* Wait for open connections to complete and shut server down.
* After 10 seconds force process to close.
* */
app.on('close_server', function () {
server.close(function () {
console.log('Server Closed.');
process.exit()
});
setTimeout(function () {
console.log('Force Close.');
process.exit()
}, 10 * 1000);
});
Upvotes: 7
Views: 3053
Reputation: 16233
As @mscdex mentioned server.close
never runs its callback when the browser sends the request Connection: keep-alive
, because server.close
only stops the server from accepting new connections.
Node v18.2.0 introduced server.closeAllConnections()
and server.closeIdleConnections()
server.closeAllConnections()
closes all connections connected to the server, and server.closeIdleConnections()
closes all connections connected to the server but only the ones which are not sending a request or waiting for a response.
Before Node v18.2.0 I tackled this problem by waiting 5 seconds for the server to shutdown, after which it would force exit.
The following code contemplates both situations
process.on('SIGINT', gracefulShutdown)
process.on('SIGTERM', gracefulShutdown)
function gracefulShutdown (signal) {
if (signal) {
console.log(`\nReceived signal ${signal}`)
}
console.log('Gracefully closing http server')
// closeAllConnections() is only available after Node v18.02
if (server.closeAllConnections) server.closeAllConnections()
else setTimeout(() => process.exit(0), 5000)
try {
server.close((err) => {
if (err) {
console.error(err)
process.exit(1)
} else {
console.log('http server closed successfully. Exiting!')
process.exit(0)
}
})
} catch (err) {
console.error('There was an error', err)
setTimeout(() => process.exit(1), 500)
}
}
Upvotes: 3
Reputation: 106698
server.close()
does not close open client connections, it only stops accepting new connections.
So most likely Chrome is sending a request with Connection: keep-alive
which means the connection stays open for some time for efficiency reasons (to be able to make multiple requests on the same connection), whereas curl is probably using Connection: close
where the connection is severed immediately after the server's response.
Upvotes: 6