diff options
author | Matt Ranney <mjr@ranney.com> | 2010-07-05 13:38:13 -0700 |
---|---|---|
committer | Ryan Dahl <ry@tinyclouds.org> | 2010-07-15 10:27:44 -0700 |
commit | 4e50197e5330b7f1f603981f6dc817cac7f9a4f9 (patch) | |
tree | e012835aeb46ab7b5e74bbdc471e054d17ff9df6 /lib | |
parent | e7c4f8cdaa5905a45fa990354023330f3309e4ae (diff) | |
download | node-new-4e50197e5330b7f1f603981f6dc817cac7f9a4f9.tar.gz |
Datagram socket refactor. Add tests and documentation.
Support setTTL() and setBroadcast() socket options.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/dgram.js | 249 | ||||
-rw-r--r-- | lib/dns.js | 2 |
2 files changed, 133 insertions, 118 deletions
diff --git a/lib/dgram.js b/lib/dgram.js index ed9eec716b..9c27c8b4d8 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -7,12 +7,9 @@ var Buffer = require('buffer').Buffer; var IOWatcher = process.IOWatcher; var binding = process.binding('net'); var socket = binding.socket; -var bind = binding.bind; var recvfrom = binding.recvfrom; -var sendto = binding.sendto; var close = binding.close; var ENOENT = binding.ENOENT; -var setBroadcast = binding.setBroadcast; function isPort (x) { return parseInt(x) >= 0; } var pool = null; @@ -31,18 +28,19 @@ function getPool() { return pool; } -function Socket (broadcast, listener) { +function Socket (type, listener) { events.EventEmitter.call(this); var self = this; - if (typeof(broadcast) != 'boolean') { - listener = broadcast; - broadcast = false; + self.type = type; + if (type === "unix_dgram" || type === "udp4" || type === "udp6") { + self.fd = socket(self.type); + } else { + throw new Error("Bad socket type specified. Valid types are: unix_dgram, udp4, udp6"); } - self.broadcast = broadcast; - if (listener) { - self.addListener('message', listener); + if (typeof listener === 'function') { + self.on('message', listener); } self.watcher = new IOWatcher(); @@ -59,156 +57,173 @@ function Socket (broadcast, listener) { p.used += rinfo.size; } }; + + if (self.type === "udp4" || self.type === "udp6") { + self._startWatcher(); + } } sys.inherits(Socket, events.EventEmitter); exports.Socket = Socket; -exports.createSocket = function (broadcast, listener) { - return new Socket(broadcast, listener); +exports.createSocket = function (type, listener) { + return new Socket(type, listener); }; Socket.prototype.bind = function () { var self = this; - if (self.fd) throw new Error('Server already opened'); - - if (!isPort(arguments[0])) { - /* TODO: unix path dgram */ - self.fd = socket('unix_dgram'); - self.type = 'unix_dgram'; - var path = arguments[0]; - self.path = path; - // unlink sockfile if it exists - fs.stat(path, function (err, r) { - if (err) { - if (err.errno == ENOENT) { - bind(self.fd, path); - process.nextTick(function() { - self._startWatcher(); - }); - } else { - throw r; - } + + if (this.type === "unix_dgram") { // bind(path) + if (typeof arguments[0] !== "string") { + throw new Error("unix_dgram sockets must be bound to a path in the filesystem"); + } + this.path = arguments[0]; + + fs.unlink(this.path, function (err) { // unlink old file, OK if it doesn't exist + if (err && err.errno !== ENOENT) { + throw err; } else { - if (!r.isFile()) { - throw new Error("Non-file exists at " + path); - } else { - fs.unlink(path, function (err) { - if (err) { - throw err; - } else { - bind(self.fd, path); - process.nextTick(function() { - self._startWatcher(); - }); - } - }); + try { + binding.bind(self.fd, self.path); + self._startWatcher(); + self.emit("listening"); + } catch (err) { + console.log("Error in unix_dgram bind of " + self.path); + console.log(err.stack); + throw err; } } }); - } else if (!arguments[1]) { - // Don't bind(). OS will assign a port with INADDR_ANY. - // The port can be found with server.address() - self.type = 'udp4'; - self.fd = socket(self.type); - bind(self.fd, arguments[0]); - process.nextTick(function() { - self._startWatcher(); - }); - } else { - // the first argument is the port, the second an IP - var port = arguments[0]; - dns.lookup(arguments[1], function (err, ip, addressType) { - if (err) { - self.emit('error', err); + } else if (this.type === "udp4" || this.type === "udp6") { // bind(port, [address]) + if (arguments[1] === undefined) { + // Not bind()ing a specific address. Use INADDR_ANY and OS will pick one. + // The address can be found with server.address() + binding.bind(self.fd, arguments[0]); + this.emit("listening"); + } else { + // the first argument is the port, the second an address + this.port = arguments[0]; + if (dns.isIP(arguments[1])) { + this.address = arguments[1]; + binding.bind(self.fd, port, arguments[1]); + this.emit("listening"); } else { - self.type = addressType == 4 ? 'udp4' : 'udp6'; - self.fd = socket(self.type); - bind(self.fd, port, ip); - process.nextTick(function() { - self._startWatcher(); + dns.lookup(arguments[1], function (err, ip, addressType) { + // TODO - only look up proper record for address family + if (err) { + self.emit('error', err); + } else { + self.ip = ip; + binding.bind(self.fd, self.port, ip); + self.emit("listening"); + } }); } - }); + } } }; Socket.prototype._startWatcher = function () { - this.watcher.set(this.fd, true, false); - this.watcher.start(); - this.emit("listening"); + if (! this._watcherStarted) { + this.watcher.set(this.fd, true, false); // listen for read ready, not write ready + this.watcher.start(); + this._watcherStarted = true; + } }; Socket.prototype.address = function () { - return getsockname(this.fd); + return binding.getsockname(this.fd); }; -Socket.prototype.send = function(port, addr, buffer, offset, length) { - var self = this; +Socket.prototype.setBroadcast = function(arg) { + if (arg) { + return binding.setBroadcast(this.fd, 1); + } else { + return binding.setBroadcast(this.fd, 0); + } +}; - var lastArg = arguments[arguments.length - 1]; - var callback = typeof lastArg === 'function' ? lastArg : null; +Socket.prototype.setTTL = function(arg) { + var newttl = parseInt(arg); + + if (newttl > 0 && newttl < 256) { + return binding.setTTL(this.fd, newttl); + } else { + throw new Error("New TTL must be between 1 and 255"); + } +}; - if (!isPort(arguments[0])) { - try { - if (!self.fd) { - self.type = 'unix_dgram'; - self.fd = socket(self.type); - } - var bytes = sendto(self.fd, buffer, offset, length, 0, port, addr); - } catch (e) { - if (callback) callback(e); - return; - } +// translate arguments from JS API into C++ API, after optional DNS lookup +Socket.prototype.send = function(buffer, offset, length) { + var self = this; - if (callback) callback(null, bytes); + if (typeof offset !== "number" || typeof length !== "number") { + throw new Error("send takes offset and length as args 2 and 3"); + } - } else { - dns.lookup(arguments[1], function (err, ip, addressType) { - // DNS error - if (err) { - if (callback) callback(err); - self.emit('error', err); - return; - } + if (this.type === "unix_dgram") { // send(buffer, offset, length, path [, callback]) + if (typeof arguments[3] !== "string") { + throw new Error("unix_dgram sockets must send to a path in the filesystem"); + } - try { - if (!self.fd) { - self.type = addressType == 4 ? 'udp4' : 'udp6'; - self.fd = socket(self.type); - setBroadcast(self.fd, self.broadcast); - process.nextTick(function() { - self._startWatcher(); - }); + self.sendto(buffer, offset, length, arguments[3], null, arguments[4]); + } else if (this.type === "udp4" || this.type === "udp6") { // send(buffer, offset, length, port, address [, callback]) + if (typeof arguments[4] !== "string") { + throw new Error(this.type + " sockets must send to port, address"); + } + + if (dns.isIP(arguments[4])) { + self.sendto(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]); + } else { + var port = arguments[3], + callback = arguments[5]; + + // TODO - only look up proper record for address family + dns.lookup(arguments[4], function (err, ip, addressType) { + if (err) { // DNS error + if (callback) { + callback(err); + } + self.emit('error', err); + return; } - var bytes = sendto(self.fd, buffer, offset, length, 0, port, ip); - } catch (err) { - // socket creation, or sendto error. - if (callback) callback(err); - return; - } + self.sendto(buffer, offset, length, port, ip, callback); + }); + } + } +}; - if (callback) callback(null, bytes); - }); +Socket.prototype.sendto = function(buffer, offset, length, port, addr, callback) { + try { + var bytes = binding.sendto(this.fd, buffer, offset, length, 0, port, addr); + } catch (err) { + if (callback) { + callback(err); + } + return; + } + + if (callback) { + callback(null, bytes); } }; Socket.prototype.close = function () { var self = this; - if (!self.fd) throw new Error('Not running'); + if (!this.fd) throw new Error('Not running'); - self.watcher.stop(); + this.watcher.stop(); + this._watcherStarted = false; - close(self.fd); - self.fd = null; + close(this.fd); + this.fd = null; - if (self.type === "unix_dgram") { - fs.unlink(self.path, function () { + if (this.type === "unix_dgram" && this.path) { + fs.unlink(this.path, function () { self.emit("close"); }); } else { - self.emit("close"); + this.emit("close"); } }; - diff --git a/lib/dns.js b/lib/dns.js index 52aa034272..afd5cb1fcc 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -71,7 +71,7 @@ var channel = new dns.Channel({SOCK_STATE_CB: function (socket, read, write) { updateTimer(); }}); - +exports.isIP = dns.isIP; exports.resolve = function (domain, type_, callback_) { var type, callback; |