-
Notifications
You must be signed in to change notification settings - Fork 7.3k
http server close() doesn't end http keep-alive connections #9066
Comments
as it is written in the docs:
http://nodejs.org/api/net.html#net_server_close_callback this behavior is applied to |
@micnic On a pure socket level the documented functionality holds true. However, on a semantic level it doesn't really. Logically, idle http keep-alive sockets should be closed when the server is closed. As it is, any client connected with http keep-alive can continue making new requests to the closed server indefinitely. Some might even call this a security issue. |
@kanongil , if the keep-alive connections are idle you can set a timeout to close them, this is done by |
@micnic Using |
I'm stuck with code as simple as this running node 0.12.0:
I tried running this from windows console (Windows 8.1). When pressing Ctrl+C w/o requesting on port 3000 the I also tried
What is intended way of having a nodejs-driven service application to be gracefully shut down (probably including to save state to disk after all client connections have been closed - as in my case) without any superdaemon considering service crashed/locked thus killing it no matter what data gets lost? |
@cepharum Because timeout zero has a special meaning: https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback By default the timeout is two minutes. |
I can't believe the But it does make sense, even tasks simple like |
@JexCheng i think this is safer method for closing server,consider of if the server is serving for some clients.what will happen if server is forced down. |
@JexCheng @dayuoba I don't think that all existing connections should be closed, as |
@dayuoba So now how can I close the server immediately? No way. Oh, OK, I know, I need an NPM package. |
@JexCheng
how about |
@dayuoba Hmm... I just found
😆 |
This all is missing some point. All web servers I know - Apache, nginx, cherokee - are supporting keep-alive connections. But none of them takes two minutes or more for shutting down if an administrator wants to stop it for whatever reason. So, what is best practice for doing this in a node-base web application server. Definitely, I know what keep-alive is good for and I'd appreciate to support it for sake of performance while the service is running. And I know why it's bad to drop all connections instantly. However, I'd love to see an easy option for lowering timeout of all currently active connections of a running server in case of I want to shut it down properly. UPDATE: One option for achieving this is obviously given above by @micnic. |
I just published the approach I am using for this as a small npm module: https://github.com/josiasmontag/node-server-graceful-shutdown |
I haven't looked at the node source code yet, but I'm pretty sure that the problem described by @cepharum is not to do with keep-alive connections. What's happening is that the server is set to stop accepting connections, but it is still listening. It will serve exactly 1 more connection, or it will timeout when the socket times out (and then actually close). You can observe this behaviour yourself by calling close() on the server and then send it one more request. It will accept that request and then close immediately. If you don't send it a request it will close when the socket times out (2 minutes after the last request by default). I think this is a bug in Node, but as I've said, I haven't looked at the code to convince myself that it isn't the desired behaviour (despite the documentation ;-) ). |
Well, socket.setTimeout
Try this script: var http = require('http');
var server = http.createServer(function (req,res) {
req.setTimeout(3000);
req.on('data',function (data) { console.log(data.toString()) });
});
server.setTimeout(6000);
server.listen(9900);
var req = http.request({
method:'POST',
hostname:'localhost',
port:9900
});
req.write('Start\n');
setInterval(function () {
req.write('Never close!\n');
},1000);
setTimeout(function () {
server.close();
console.log("Call close");
},2000); |
I think the point is that the timeout is set to a reasonable value. It's just that when you call close() on the server it doesn't actually do what the documentation suggests (i.e. stop accepting connections). It accepts exactly one more connection. I think the easiest workaround is simply sending an http request to the server after you have closed it. This will make it work. But it is slightly inelegant. |
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 |
@JoshuaWise Your solution is the only sane answer I see in this thread, thank you. That does not mean it shouldn't be the default behavior, because it really should be. I'm sorry to say it this bluntly, but all these hacks and workarounds (that do or don't work) should not be needed. Calling close() should close the server and all clients that are not currently in the middle of a request. It should also close all keep-alive connections that are in the middle of a request immediately after the request has been served. This is not rocket science. |
Thanks, @ronkorving. Unfortunately, My solution is useless if you have other things keeping the process open. This solution only works because Node will close when its event loop is empty. If you have other things going on, there is no way to know when all of your sockets connections are idle (without keeping track of every single one of them manually). I find this incredibly unfortunate. EDIT var http = require('http');
var EventEmitter = require('events').EventEmitter;
function createServer(requestListener) {
var state = new EventEmitter;
state.shutdown = false;
var server = http.createServer();
server.shutdown = function () {
server.close();
state.shutdown = true;
state.emit('shutdown');
}
server.on('connection', function (socket) {
function destroy() {
if (socket.HAS_OPEN_REQUESTS === 0) socket.destroy();
}
socket.HAS_OPEN_REQUESTS = 0;
state.once('shutdown', destroy);
socket.once('close', function () {
state.removeListener('shutdown', destroy);
});
});
server.on('request', function (req, res) {
var socket = req.connection;
socket.HAS_OPEN_REQUESTS++;
res.on('finish', function () {
socket.HAS_OPEN_REQUESTS--;
if (state.shutdown && socket.HAS_OPEN_REQUESTS === 0) {socket.destroy();}
});
});
server.on('request', requestListener);
}
var server = createServer(myFancyServerLogic);
server.listen(80); |
This is largely due to a number of factors but primarily the fact that Node attempts to keep the internal state management as simple as possible without having to keep track of everything that would need to be closed. This is something that would be useful as a userland module, however :-). Going to close the issue as it's not actually a bug in node, but feel free to keep discussing :-) |
If it's not a bug in node, what is it a bug in? You can't have this behavior and pretend to be an HTTP server, sorry. Just because it's hard to solve does not make this a bogus issue and I for one will not stick my head in the sand. Anyway, moving on (to io.js). |
Does io.js solve this problem? |
I doubt it, but at least the odds of it getting fixed there are higher. |
Just thought I would clarify that this is a bug in node. The following will remain open as long as the client makes a request within one second intervals making it possible that a server will never gracefully close. With the default of 2 minute timeouts a user or api client would need to stop traffic for two minutes before this will take affect. I propose closing sockets using keep alive immediately after the next request. This would also prevent needing to var http = require('http');
var server = http.createServer(function(req, res) {
res.end('test\n');
server.close()
}).listen(8000, '127.0.0.1');
server.setTimeout(1000); |
Sounds good to me, thanks 👍 |
I agree this deserves another look. Reopening the issue here, however, is not likely to get the attention it would need. Opening a new issue under nodejs/node would be helpful |
Created nodejs/node#2642 |
As @jasnell suggested, this can be solved in userland: nodejs/node#2642 (comment) |
When a http client uses
Connection: keep-alive
,http.Server#close()
doesn't end stale/free keep-alive connections, thus preventing the process from exiting. I can replicate the behavior on both 0.10.x and master.Failing test
server.js:
client.js:
Result on node 0.10.35
Expected result
Server exits immediately after the client has received the response data.
Observed result
Server is kept alive until the client destroys the http keep-alive socket. Similar behavior is observed in master when using this client script:
The text was updated successfully, but these errors were encountered: