Browse Source

Implement net.Server.maxConnections

Simplify EMFILE behavior.
v0.7.4-release
Ryan Dahl 15 years ago
parent
commit
4593c04959
  1. 9
      doc/api.markdown
  2. 47
      lib/net.js
  3. 1
      test/simple/test-net-pingpong.js
  4. 79
      test/simple/test-net-server-max-connections.js

9
doc/api.markdown

@ -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

47
lib/net.js

@ -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,6 +1039,8 @@ function Server (listener) {
self.addListener('connection', listener);
}
self.connections = 0;
self.watcher = new IOWatcher();
self.watcher.host = self;
self.watcher.callback = function () {
@ -1065,15 +1048,19 @@ function Server (listener) {
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") {

1
test/simple/test-net-pingpong.js

@ -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;

79
test/simple/test-net-server-max-connections.js

@ -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);
});
Loading…
Cancel
Save