express-graceful-exit
Advanced tools
Comparing version 0.1.0 to 0.2.1
var _ = require('underscore') | ||
var _ = require('underscore'); | ||
var sockets = []; | ||
exports.middleware = function(app) { | ||
// This flag is used to tell the middleware we create that the server wants | ||
// to stop, so we do not allow anymore connections. This is done for all new | ||
// connections for us by Node, but we need to handle the connections that are | ||
// using the Keep-Alive header to stay on. | ||
app.set('graceful_exit', false) | ||
/** | ||
* Keep track of open connections so we can forcibly close sockets when the suicide timeout elapses | ||
* @param server HTTP server | ||
*/ | ||
exports.init = function (server) { | ||
server.on('connection', function (socket) { | ||
sockets.push(socket); | ||
return function(req, res, next) { | ||
// Sorry Keep-Alive connections, but we need to part ways | ||
if (app.settings.graceful_exit === true) { | ||
req.connection.setTimeout(1) | ||
} | ||
socket.on('close', function () { | ||
sockets.splice(sockets.indexOf(socket), 1); | ||
}); | ||
}); | ||
}; | ||
next() | ||
} | ||
} | ||
exports.gracefulExitHandler = function(app, server, _options) { | ||
// Get the options set up | ||
if (!_options) _options = {} | ||
if (!_options) { | ||
_options = {}; | ||
} | ||
var options = _.defaults(_options, { | ||
log : false | ||
, logger : console.log | ||
, suicideTimeout : 2*60*1000 + 10*1000 // 2m10s (nodejs default is 2m) | ||
}) | ||
log : false, | ||
logger : console.log, | ||
suicideTimeout : 2*60*1000 + 10*1000, // 2m10s (nodejs default is 2m) | ||
exitProcess : true, | ||
force : false | ||
}); | ||
var suicideTimeout; | ||
var connectionsClosed = false; | ||
function logger(str) { | ||
if (options.log) | ||
options.logger(str) | ||
if (options.log) { | ||
options.logger(str); | ||
} | ||
} | ||
logger('Closing down the server') | ||
function exit(code) { | ||
if (options.exitProcess) { | ||
process.exit(code); | ||
} else if (options.callback) { | ||
if (_.isFunction(options.callback)) { | ||
options.callback(code); | ||
} else { | ||
logger("Registered callback is not a function"); | ||
} | ||
} | ||
} | ||
if (options.callback && options.exitProcess) { | ||
logger("Set exitProcess option to false, otherwise callback is ignored. " + | ||
"Register a callback when process exit is handled by the caller."); | ||
} | ||
logger('Closing down the http server'); | ||
// Let everything know that we wish to exit gracefully | ||
app.set('graceful_exit', true) | ||
app.set('graceful_exit', true); | ||
@@ -43,12 +65,15 @@ // Time to stop accepting new connections | ||
// Everything was closed successfully, mission accomplished! | ||
logger('All connections done, stopping process') | ||
process.exit(0) | ||
}) | ||
connectionsClosed = true; | ||
clearTimeout(suicideTimeout); | ||
logger('All connections done, stopping process'); | ||
exit(0); | ||
}); | ||
// Disconnect all the socket.io clients | ||
if (options.socketio) { | ||
options.socketio.sockets.clients().forEach(function(socket) { | ||
logger('Killing socketio socket') | ||
socket.disconnect() | ||
}) | ||
logger('Killing socketio socket'); | ||
socket.disconnect(); | ||
}); | ||
} | ||
@@ -59,6 +84,42 @@ | ||
// be graceful, but failed. | ||
setTimeout(function() { | ||
logger('Exiting process with some open connections left') | ||
process.exit(1) | ||
}, options.suicideTimeout) | ||
} | ||
suicideTimeout = setTimeout(function() { | ||
if (connectionsClosed) { | ||
// this condition should never occur, see server.close() above | ||
// user callback, if any, has already been called | ||
if (options.exitProcess) { | ||
process.exit(1); | ||
} | ||
return; | ||
} | ||
if (options.force) { | ||
logger('Destroying ' + sockets.length + ' open sockets'); | ||
sockets.forEach(function (socket) { | ||
socket.destroy(); | ||
}); | ||
} else { | ||
logger('Exiting process before all connections are closed'); | ||
} | ||
setTimeout(function() { | ||
// leave a bit of time to write logs | ||
process.exit(1); | ||
}, 10); | ||
}, options.suicideTimeout); | ||
}; | ||
exports.middleware = function(app) { | ||
// This flag is used to tell the middleware we create that the server wants | ||
// to stop, so we do not allow anymore connections. This is done for all new | ||
// connections for us by Node, but we need to handle the connections that are | ||
// using the Keep-Alive header to stay on. | ||
app.set('graceful_exit', false); | ||
return function(req, res, next) { | ||
// Sorry Keep-Alive connections, but we need to part ways | ||
if (app.settings.graceful_exit === true) { | ||
req.connection.setTimeout(1); | ||
} | ||
next(); | ||
}; | ||
}; |
{ | ||
"name": "express-graceful-exit", | ||
"version": "0.1.0", | ||
"description": "Allow express to support graceful exits to achieve zero downtime deploys", | ||
"version": "0.2.1", | ||
"description": "Allow graceful exits for express apps, supporting zero downtime deploys", | ||
"keywords": ["express", "graceful", "exit", "shutdown", "clean", "tidy"], | ||
"main": "index.js", | ||
@@ -12,6 +13,9 @@ "scripts": { | ||
"author": "Jon Keating <jon@licq.org>", | ||
"contributors": [ | ||
"Ivo Havener <ivolucien@gmail.com>" | ||
], | ||
"license": "MIT", | ||
"dependencies": { | ||
"underscore": "~1.4.4" | ||
"underscore": "^1.4.4" | ||
} | ||
} |
@@ -23,7 +23,7 @@ # express-graceful-exit | ||
```` javascript | ||
var express = require('express') | ||
, app = express() | ||
, gracefulExit = require('express-graceful-exit') | ||
var express = require('express'); | ||
var app = express(); | ||
var gracefulExit = require('express-graceful-exit'); | ||
app.use(gracefulExit.middleware(app)) | ||
app.use(gracefulExit.middleware(app)); | ||
```` | ||
@@ -39,3 +39,3 @@ | ||
if (message === 'shutdown') { | ||
gracefulExit.gracefulExitHandler(app, server { | ||
gracefulExit.gracefulExitHandler(app, server, { | ||
socketio: app.settings.socketio | ||
@@ -57,6 +57,9 @@ }) | ||
* __log:__ Shows some messages about what is going on (default false). | ||
* __log:__ Print status messages and errors to the logger (default false). | ||
* __logger:__ Function that accepts a string to output a log message (default console.log). | ||
* __suicideTimeout:__ The timeout to forcefully exit the process with a return code of 1 (default 3 minutes). | ||
* __socketio:__ An instance of socket.io, that will close all open socket.io connections (default none) | ||
* __suicideTimeout:__ How long to wait before giving up on graceful shutdown, then returns exit code of 1 (default 2m 10s). | ||
* __socketio:__ An instance of `socket.io`, used to close all open connections after timeout (default none) | ||
* __exitProcess:__ If true, the module calls `process.exit()` when express has shutdown, gracefully or not (default true). | ||
* __callback:__ Optional function that is called with the exit status code once express has shutdown, gracefully or not - use in conjunction with exitProcess=false, when the caller handles process shutdown (default none) | ||
* __force:__ Instructs the module to forcibly close sockets once the suicide timeout elapses. Requires that `gracefulExit.init(server)` be called when initializing the HTTP server (default: false) | ||
@@ -67,7 +70,7 @@ ## Details | ||
1. Close the server so no new connections get accepted | ||
2. Mark that the server will gracefully exit, so if a connection that is using the Keep-Alive header is still active, it will be told to close the connection. The HTTP status code of 502 is returned, so nginx, ELB, etc will try again with a working server. | ||
3. If a socket.io instance is passed in the options, it enumerates all connected clients and disconnects them. The client should have code to reconnect on disconnect. | ||
5. Once all connected clients are disconnected, the server exits with an error code of 0. | ||
6. If there are still some remaining connections after the `suicideTimeout`, the server ungracefully exits with an error code of 1. | ||
1. Close the http server so no new connections are accepted | ||
2. Mark that the server will gracefully exit, so if a connection that is using the Keep-Alive header is still active, it will be told to close the connection. The HTTP status code of 502 is returned, so nginx, ELB, etc will try again with a working server | ||
3. If a socket.io instance is passed in the options, it enumerates all connected clients and disconnects them. The client should have code to reconnect on disconnect | ||
4. Once all connected clients are disconnected, the server exits with an error code of 0 | ||
5. If there are still some remaining connections after the `suicideTimeout`, it stops waiting and returns exit code of 1 | ||
@@ -79,2 +82,2 @@ ## Getting zero downtime deploys | ||
#### Author: [Jon Keating](http://twitter.com/emostar) | ||
#### Maintainer: [Ivo Havener](https://github.com/ivolucien) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
8961
106
79
1
+ Addedunderscore@1.13.7(transitive)
- Removedunderscore@1.4.4(transitive)
Updatedunderscore@^1.4.4