billywhizz
12/6/2011 - 6:53 PM

minimal node.js tcp server

minimal node.js tcp server

var TCPServer = require("./lib/tcp").Server;

var s = new TCPServer({
	host: "0.0.0.0",
	port: 80,
	backlog: 128,
	secure: true,
	maxconn: 10000,
	credentials: {
		cert: require("fs").readFileSync("./cert.pem").toString(),
		key: require("fs").readFileSync("./key.pem").toString(),
		ciphers: "RC4-SHA:AES128-SHA:AES256-SHA"
	},
	nodelay: false,
	keepalive: {
		on: false,
		delay: 30000
	},
	onConnect: function() {
		// "this" object has the client socket
		console.log("connect");
	},
	onData: function(buffer, start, len) {
		// "this" object has the client socket
		var socket = this;
		socket.send(buffer.slice(start, start + len), null, function(status, handle, r, buffer) {
			if(status != 0) {
				socket.destroy();
			}
		});
	},
	onEnd: function() {
		// "this" object has the client socket
		console.log("end");
	},
	onError: function(err) {
		// "this" object has the client socket
		console.log("error");
		console.log(err);
		var ex = this.destroy();
		if(ex) console.log(ex);
	}
});
s.onError = function(err) {
	console.log("server.error");
	console.log(err);
}
s.listen();
var TCP = process.binding("tcp_wrap").TCP;
try {
	var crypto = process.binding("crypto");
	var SecureContext = crypto.SecureContext;
} catch (e) {
	throw new Error("node.js not compiled with openssl crypto support.");
}
function noop() {};

function createCredentials(key, cert, ciphers) {
	var c = new SecureContext();
	c.init();
	c.setKey(key);
	c.setCert(cert);
	if(ciphers) c.setCiphers(ciphers);
	c.addRootCerts();
	c.context = c;
	return c;
}

function TCPServer(options, handle) {
	var _server = this;
	var socket = handle?handle:new TCP();
	options.maxconn = options.maxconn || 1000;
	socket.onconnection = function(client) {
		if(!client) {
			if(_server.onError) _server.onError(new Error("Accept Failed: " + errno));
			return;
		}
		var pair;
		var send;
		["onEnd", "onConnect", "onError", "onData"].forEach(function(foo) {
			client[foo] = options[foo]?options[foo]:noop;
		});
		if(options.secure) {
			send = function(buff, encoding, cb) {
				if(encoding) {
					buff = new Buffer(buff, encoding);
				}
				var wr = pair.cleartext.write(buff);
				if (!wr) {
					shutdown();
					return false;
				}
				wr.oncomplete = cb;
				return true;
			}
		}
		else {
			send = function(buff, encoding, cb) {
				if(encoding) {
					buff = new Buffer(buff, encoding);
				}
				var wr = client.write(buff);
				if (!wr) {
					shutdown();
					return false;
				}
				wr.oncomplete = cb;
				return true;
			}
		}
		function shutdown() {
			try {
				client.readStop();
				client.onread = noop;
				var shutdownReq = client.shutdown();
				shutdownReq.oncomplete = function(status, handle, req) {
					client.onEnd.apply(client);
					handle.close();
				};
				return null;
			}
			catch(ex) {
				return ex;
			}
		}
		if(options.nodelay) client.setNoDelay();
		if(options.keepalive && options.keepalive.on) {
			client.setKeepAlive(true, options.keepalive.delay);
		}
		client.destroy = shutdown;
		client.send = send;
		if(options.secure) {
			client.onread = function(buffer, offset, length) {
				if(!buffer) {
					shutdown();
				}
				else {
					var ret = pair.encrypted.write(buffer.slice(offset, offset + length));
				}
			};
			var serverCreds = createCredentials(options.credentials.key, options.credentials.cert, options.credentials.ciphers);
			pair = require("tls").createSecurePair(serverCreds, true);
			pair.on("secure", function() {
				client.onConnect.apply(client);
			});
			pair.encrypted.on("data", function(chunk) {
				var wr = client.write(chunk);
				wr.oncomplete = function(status, handle, req, buffer) {
				};
			});
			pair.cleartext.on("data", function(chunk) {
				client.onData.call(client, chunk, 0, chunk.length);
			});
		}
		else {
			client.onread = function(buffer, offset, length) {
				if(!buffer) {
					shutdown();
					return;
				}
				client.onData.call(client, buffer, offset, length);
			};
			client.onConnect.apply(client);
		}
		client.readStart();
	};
	_server.listen = function() {
		if(handle) {
			r = handle.listen(options.backlog || 128);
			return;
		}
		var r = socket.bind(options.host, options.port);
		if(r) {
			if(_server.onError) _server.onError(new Error("Bind Failed:" + errno));
			socket.close();
		}
		else {
			r = socket.listen(options.backlog || 128);
			if(r < 0) {
				if(_server.onError) _server.onError(new Error("Listen Failed: " + errno));
				socket.close();
			}
		}
	}
}
exports.Server = TCPServer;