32

Here is a simple webserver I am working on

var server = require("http").createServer(function(req,resp) {
    resp.writeHead(200,{"Content-Type":"text/plain"})
    resp.write("hi")
    resp.end()
    server.close()
})
server.listen(80, 'localhost')
// The shortest webserver you ever did see! Thanks to Node.JS :)

Works great except for keep-alive. When the first request comes in, server.close gets called. But the process does not end. Actually the TCP connection is still open which allows another request to come through which is what I am trying to avoid.

How can I close existing keep-alive connections?

700 Software
  • 85,281
  • 83
  • 234
  • 341

4 Answers4

23

You can control the idle timeout for a connection, so you can set how long a keep-alive connection will remain open. For example:

server=require('http').createServer(function(req,res) {
    //Respond
    if(req.url.match(/^\/end.*/)) {
        server.close();
        res.writeHead(200,{'Content-Type':'text/plain'});
        res.end('Closedown');
    } else {
        res.writeHead(200,{'Content-Type':'text/plain'});
        res.end('Hello World!');
    }
}).listen(1088);
//Set the idle timeout on any new connection
server.addListener("connection",function(stream) {
    stream.setTimeout(4000);
});

We can test this with netcat:

ben@quad-14:~/node$ echo -e "GET /test HTTP/1.1\nConnection: keep-alive\n\n" | netcat -C -q -1 localhost 1088
HTTP/1.1 200 OK
Content-Type: text/plain
Connection: keep-alive
Transfer-Encoding: chunked

c
Hello World!
0

after 4 seconds, the connection closes

And now we can show that closing the server works: after all idle connections are dropped, the server exits:

ben@quad-14:~/node$ echo -e "GET /end HTTP/1.1\nConnection: keep-alive\n\n" | netcat -C -q -1 localhost 1088
HTTP/1.1 200 OK
Content-Type: text/plain
Connection: keep-alive
Transfer-Encoding: chunked

9
Closedown
0

after 4 seconds, the connection closes and the server exits

700 Software
  • 85,281
  • 83
  • 234
  • 341
Ben Last
  • 801
  • 8
  • 6
  • I guess you cold actually get the server to change the timeout during the course of the running time of the server. So if you are not expecting a shutdown, you can lengthen the keep-alive time... – 700 Software Dec 16 '11 at 14:17
19

You can call request.connection.destroy() in the response callback. That will close the request connection.

It will also end your process since there is nothing left to do, the end result is the same as calling process.exit() right there.

Ricardo Tomasi
  • 34,573
  • 2
  • 55
  • 66
  • 1
    Calling destroy on each connection would require me to keep an array and periodically clear out the ones that were automatically destroyed. But that might work. – 700 Software Mar 14 '11 at 13:47
  • Why would that be? The request object is passed along with the response in the callback (the req argument), i.e. just add `req.connection.destroy()` after `resp.end()` – Ricardo Tomasi Mar 15 '11 at 19:02
  • 6
    I do not want to eliminate keep-alive functionality for the sole purpose of easy webserver shutdown. At the time of `resp.end()`, it may not know that it will be shutting down in the next few seconds (before the connection automatically gets destroyed). Therefore it would not know that it should destroy the connection. That is why it would add to an array. But really, it is possible to have a connection that never makes its first request which means my first comment does not work as a solution and exit may be my best bet (short of cloning `http.js` source code). – 700 Software Mar 15 '11 at 19:33
  • I want to unit/functional test my application. I'm able to start the application server but close gives error. If I left the server open, it get's killed on all test completion. So `close()` is not needed as such. But if I'm testing in multiple files. I can't restart the server. Because close() doesn't work to stop the server, and I can't start multiple servers. – Amit Kumar Gupta Feb 03 '17 at 07:21
4

If you're closing the server as part of a graceful shutdown of the process, you just need this:

var server = require('http').createServer(myFancyServerLogic);

server.on('connection', function (socket) {socket.unref();});
server.listen(80);

function myFancyServerLogic(req, res) {
    req.connection.ref();

    res.end('Hello World!', function () {
        req.connection.unref();
    });
}

Basically, the sockets that your server uses will only keep the process alive while they're actually serving a request. While they're just sitting there idly (because of a Keep-Alive connection), a call to server.close() will close the process, as long as there's nothing else keeping the process alive. If you need to do other things after the server closes, as part of your graceful shutdown, you can hook into process.on('beforeExit', callback) to finish your graceful shutdown procedures.

Joshua Wise
  • 613
  • 4
  • 15
  • Thanks for posting `ref` and `unref` are newer options in Node.JS, which were not available at the time the other answers were posted. – 700 Software Jul 20 '15 at 12:08
  • Didn't work out in my case ... see excerpt at https://gist.github.com/soletan/701fefc7727c2bc7c170 ... – Thomas Urban Sep 27 '15 at 14:44
  • In my case using ref() und unref() affected nodejs on properly emitting `close` event. So I had to stick with collecting sockets globally and keeping track of whether they are currently processing request or not so I can clear timeout on trying to shutdown. See gist above for more. – Thomas Urban Sep 28 '15 at 07:59
  • 1
    Isn't `server.on('connection', function (socket) {socket.unref();});` enough? – GingerPlusPlus May 17 '16 at 07:32
  • @GingerPlusPlus I don't think so -- that would allow connections to get killed while myFancyServerLogic was being executed. Unref'ed != inactive. – ZachB Aug 08 '16 at 00:15
  • @ZachB: Code written in Node is running on single thread, so I don't think you can call `server.close()` when request is being processed. – GingerPlusPlus Aug 09 '16 at 18:23
  • @ZachB: I don't think so. Node won't exit on it's own, as long as it has scheduled works to do. Run this in Node: `setTimeout(function() {console.log('done') }, 10000)` -- Node doesn't exits immiedetly, even though `console.log` is called in async code, because it knows it has work left to do. – GingerPlusPlus Aug 10 '16 at 13:23
  • 1
    @GingerPlusPlus correct, but I was talking about `server.close`, not node exiting. https://gist.github.com/zbjornson/e21a82e5abc60d0c57b2ed625a94467d. By the time the `setTimeout` expires, there might be nothing left to write. This is a pokey test (sometimes that gist works; it seems more likely to fail with a new tab in Chrome), but sometimes nothing comes after "Server closed" -- I assume that whatever is sweeping socket connections is on some timer that you have to hit just right. – ZachB Aug 10 '16 at 16:15
  • 1
    @ZachB: ah, I see now. Thanks for explaining. – GingerPlusPlus Aug 10 '16 at 16:18
  • Interestingly, `unref`'ing in `on("connection")` actually seems to prevent the handler from getting invoked at all based on the console logging. Likewise, if I change line 5 of my gist to `req.connection.ref()`, the server still dies before the connection can be `ref`'ed again. -- So, I think @joshua-wise's answer doesn't work at all. :( – ZachB Aug 10 '16 at 16:19
0

How can I close existing keep-alive connections?

Since Node 8.0.0, you can set the server keepAliveTimeout attribute to 1 millisecond (it is set to 5000 milliseconds by default):

server.keepAliveTimeout

Added in: v8.0.0

<number> Timeout in milliseconds. Default: 5000 (5 seconds).

The number of milliseconds of inactivity a server needs to wait for additional incoming data, after it has finished writing the last response, before a socket will be destroyed. If the server receives new data before the keep-alive timeout has fired, it will reset the regular inactivity timeout, i.e., server.timeout.

A value of 0 will disable the keep-alive timeout behavior on incoming connections. A value of 0 makes the http server behave similarly to Node.js versions prior to 8.0.0, which did not have a keep-alive timeout.

The socket timeout logic is set up on connection, so changing this value only affects new connections to the server, not any existing connections.

Alternatively, since Node 18.2.0, you can call the server closeIdleConnections() method:

server.closeIdleConnections()

Added in: v18.2.0

Closes all connections connected to this server which are not sending a request or waiting for a response.

For instance, this is useful for running a test suite of an HTTP server fast instead of waiting 5 seconds for the default keep-alive timeout:

import assert from 'node:assert/strict';
import http from 'node:http';
import test from 'node:test';

test.describe(() => {
  const server = http.createServer((request, response) => {
    response.statusCode = 200;
    response.setHeader('Content-Type', 'text/plain');
    response.setHeader('Content-Length', '12');
    response.end('hello, world');
  });
  server.listen(8000, 'localhost');

  test.after(() => {
    server.closeIdleConnections();
    server.close();
  });

  test.test(async () => {
    const response = await fetch('http://localhost:8000/');
    assert.equal(response.status, 200);
    assert.equal(response.headers.get('Content-Type'), 'text/plain');
    assert.equal(response.headers.get('Content-Length'), '12');
    assert.equal(await response.text(), 'hello, world');
  });
});

Alternatively, since Node 19.0.0, you can rely on the server close() method to implicitly call the server closeIdleConnections() method so the previous suggestions do not apply anymore:

server.close([callback])

History

Version Changes
v19.0.0 The method closes idle connections before returning.
v0.1.90 Added in: v0.1.90
  • callback <Function>

Stops the server from accepting new connections and closes all connections connected to this server which are not sending a request or waiting for a response. See net.Server.close().

Géry Ogam
  • 6,336
  • 4
  • 38
  • 67