Jacob Marble
Jacob Marble

Reputation: 30232

How to properly handle SIGINT with Express.js?

I need to do some useful things when my Express.js service is stopped by SIGINT. Using Express.js version 3.0.6, I understand that this should work:

var express = require('express');

var app = express();
var server = app.listen(3000);

process.on('SIGINT', function() {
  console.log('Do something useful here.');
  server.close();
});

But the process doesn't give me back the Bash prompt unless I issue SIGINT (Control-C) twice:

$ node problem.js 
^CDo something useful here.
^CDo something useful here.

net.js:1046
    throw new Error('Not running');
          ^
Error: Not running
    at Server.close (net.js:1046:11)
    at process.<anonymous> (/path/to/problem.js:8:10)
    at process.EventEmitter.emit (events.js:93:17)
    at SignalWatcher.startup.processSignalHandlers.process.on.process.addListener.w.callback (node.js:486:45)
$

One more caveat. If I start this Express.js service and don't send any requests then SIGINT terminates properly.

Clearly there is something fundamental that I'm missing here?

Upvotes: 35

Views: 34210

Answers (3)

nneonneo
nneonneo

Reputation: 179717

By catching the SIGINT handler, you are preventing the default behaviour, which is to quit the process. When you use Ctrl+C the second time, the process dies because server.close throws an uncaught exception.

Just add a process.exit() to the end of your SIGINT handler to quit the process gracefully.

Upvotes: 48

Kirill Husiatyn
Kirill Husiatyn

Reputation: 828

I am using this code:

server.close(() => {
  process.exit(0)
})

Upvotes: 8

Whyhankee
Whyhankee

Reputation: 903

Resurrecting an oldie since the question is not completely (or correctly) answered and people might still find this answer.

The important part of your question is:

One more caveat. If I start this Express.js service and don't send any requests then SIGINT terminates properly. Clearly there is something fundamental that I'm missing here?`

What you are missing is that by default express keeps connections open (HTTP keep-alive) for re-use. So, when there has been a (recent) request there's still something on the event-loop and that's the reason your app is not closing.

Using process.exit() will work, but is like sending another ^C, and may hide the fact that there is still something else open (e.g. database connections). Your app should just close when there's nothing left on the event-loop.

So, I modified your example, setting a 5 second keep-alive on the connection so it will graceful shutdown in a reasonable time.

var express = require('express');

var serverPort = 3000;
var app = express();
var server = app.listen(serverPort);


// HTTP Keep-Alive to a short time to allow graceful shutdown
server.on('connection', function (socket) {
  socket.setTimeout(5 * 1000);
});

// Handle ^C
process.on('SIGINT', shutdown);

// Do graceful shutdown
function shutdown() {
  console.log('graceful shutdown express');
  server.close(function () {
    console.log('closed express');
  });
}

console.log('running server: http://localhost:' + serverPort);

Upvotes: 20

Related Questions