Permalink
Browse files

Implement net.Server.maxConnections

Simplify EMFILE behavior.
  • Loading branch information...
1 parent cde80d9 commit 4593c049592d1dda6f61279887474bac132bb5ab @ry ry committed Aug 15, 2010
Showing with 106 additions and 30 deletions.
  1. +9 −0 doc/api.markdown
  2. +17 −30 lib/net.js
  3. +1 −0 test/simple/test-net-pingpong.js
  4. +79 −0 test/simple/test-net-server-max-connections.js
View
@@ -2196,6 +2196,15 @@ Stops the server from accepting new connections. This function is
asynchronous, the server is finally closed when the server emits a `'close'`
event.
+### server.maxConnections
+
+Set this property to reject connections when the server's connection count gets high.
+
+### server.connections
+
+The number of concurrent connections on the server.
+
+
## net.Stream
View
@@ -220,29 +220,6 @@ var ioWatchers = new FreeList("iowatcher", 100, function () {
});
-// waitingForFDs stores servers which have experienced EMFILE.
-// When a file descriptor becomes available through closeFD()
-// a server from waitingForFDs is started.
-
-var waitingForFDs = [];
-
-function closeFD(fd) {
- close(fd);
-
- // Try to recover from EMFILE
-
- var server, serverFD;
- while (server = waitingForFDs.shift()) {
- serverFD = parseInt(server.fd);
- if (serverFD && serverFD > 0) {
- server.watcher.set(serverFD, true, false);
- server.watcher.start();
- return;
- }
- }
-}
-
-
// Allocated on demand.
var pool = null;
function allocNewPool () {
@@ -997,9 +974,13 @@ Stream.prototype.destroy = function (exception) {
this.secureStream.close();
}
+ if (this.server) {
+ this.server.connections--;
+ }
+
// FIXME Bug when this.fd == 0
if (typeof this.fd == 'number') {
- closeFD(this.fd);
+ close(this.fd);
this.fd = null;
process.nextTick(function () {
if (exception) self.emit('error', exception);
@@ -1058,22 +1039,28 @@ function Server (listener) {
self.addListener('connection', listener);
}
+ self.connections = 0;
+
self.watcher = new IOWatcher();
self.watcher.host = self;
self.watcher.callback = function () {
while (self.fd) {
try {
var peerInfo = accept(self.fd);
} catch (e) {
- if (e.errno == EMFILE) {
- waitingForFDs.push(self);
- self.watcher.stop();
- return;
- }
+ if (e.errno == EMFILE) return;
throw e;
}
if (!peerInfo) return;
+ if (self.maxConnections && self.connections >= self.maxConnections) {
+ // Accept and close the connection.
+ close(peerInfo.fd);
+ return;
+ }
+
+ self.connections++;
+
var s = new Stream(peerInfo.fd, self.type);
s.remoteAddress = peerInfo.address;
s.remotePort = peerInfo.port;
@@ -1209,7 +1196,7 @@ Server.prototype.close = function () {
self.watcher.stop();
- closeFD(self.fd);
+ close(self.fd);
self.fd = null;
if (self.type === "unix") {
@@ -13,6 +13,7 @@ function pingPongTest (port, host) {
var server = net.createServer(function (socket) {
console.log("connection: " + socket.remoteAddress);
assert.equal(server, socket.server);
+ assert.equal(1, server.connections);
socket.setNoDelay();
socket.timeout = 0;
@@ -0,0 +1,79 @@
+common = require("../common");
+assert = common.assert;
+net = require('net');
+
+// This test creates 200 connections to a server and sets the server's
+// maxConnections property to 100. The first 100 connections make it through
+// and the last 100 connections are rejected.
+// TODO: test that the server can accept more connections after it reaches
+// its maximum and some are closed.
+
+N = 200;
+count = 0;
+closes = 0;
+waits = [];
+
+server = net.createServer(function (connection) {
+ console.error("connect %d", count++);
+ connection.write("hello");
+ waits.push(function () { connection.end(); });
+});
+
+server.listen(common.PORT, function () {
+ for (var i = 0; i < N; i++) {
+ makeConnection(i);
+ }
+});
+
+server.maxConnections = N/2;
+
+console.error("server.maxConnections = %d", server.maxConnections);
+
+
+function makeConnection (index) {
+ setTimeout(function () {
+ var c = net.createConnection(common.PORT);
+ var gotData = false;
+
+ c.on('end', function () { c.end(); });
+
+ c.on('data', function (b) {
+ gotData = true;
+ assert.ok(0 < b.length);
+ });
+
+ c.on('error', function (e) {
+ console.error("error %d: %s", index, e);
+ });
+
+ c.on('close', function () {
+ console.error("closed %d", index);
+ closes++;
+
+ if (closes < N/2) {
+ assert.ok(server.maxConnections <= index,
+ index + " was one of the first closed connections but shouldnt have been");
+ }
+
+ if (closes === N/2) {
+ var cb;
+ console.error("calling wait callback.");
+ while (cb = waits.shift()) {
+ cb();
+ }
+ server.close();
+ }
+
+ if (index < server.maxConnections) {
+ assert.equal(true, gotData, index + " didn't get data, but should have");
+ } else {
+ assert.equal(false, gotData, index + " got data, but shouldn't have");
+ }
+ });
+ }, index);
+}
+
+
+process.on('exit', function () {
+ assert.equal(N, closes);
+});

0 comments on commit 4593c04

Please sign in to comment.