From be0bb2dc136ca20b44da81cded790417cbd1cfd2 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Tue, 11 Oct 2011 13:07:14 -0700 Subject: [PATCH] Remove net_legacy timers_legacy --- lib/{net_uv.js => net.js} | 0 lib/net_legacy.js | 1215 --------------------- lib/{timers_uv.js => timers.js} | 0 lib/timers_legacy.js | 224 ---- node.gyp | 9 +- src/node.cc | 9 - src/node.js | 6 - src/node_extensions.h | 3 - src/node_net.cc | 1781 ------------------------------- src/node_net.h | 33 - src/node_timer.cc | 200 ---- src/node_timer.h | 64 -- wscript | 2 - 13 files changed, 2 insertions(+), 3544 deletions(-) rename lib/{net_uv.js => net.js} (100%) delete mode 100644 lib/net_legacy.js rename lib/{timers_uv.js => timers.js} (100%) delete mode 100644 lib/timers_legacy.js delete mode 100644 src/node_net.cc delete mode 100644 src/node_net.h delete mode 100644 src/node_timer.cc delete mode 100644 src/node_timer.h diff --git a/lib/net_uv.js b/lib/net.js similarity index 100% rename from lib/net_uv.js rename to lib/net.js diff --git a/lib/net_legacy.js b/lib/net_legacy.js deleted file mode 100644 index 0388219b30..0000000000 --- a/lib/net_legacy.js +++ /dev/null @@ -1,1215 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var util = require('util'); -var events = require('events'); -var stream = require('stream'); -var timers = require('timers'); - -var kMinPoolSpace = 128; -var kPoolSize = 40 * 1024; - -var debug; -if (process.env.NODE_DEBUG && /net/.test(process.env.NODE_DEBUG)) { - debug = function(x) { console.error('NET:', x); }; -} else { - debug = function() { }; -} - - -var binding = process.binding('net'); - -// Note about Buffer interface: -// I'm attempting to do the simplest possible interface to abstracting raw -// memory allocation. This might turn out to be too simple - it seems that -// I always use a buffer.used member to keep track of how much I've filled. -// Perhaps giving the Buffer a file-like interface with a head (which would -// represent buffer.used) that can be seeked around would be easier. I'm not -// yet convinced that every use-case can be fit into that abstraction, so -// waiting to implement it until I get more experience with this. -var FreeList = require('freelist').FreeList; - -var IOWatcher = process.binding('io_watcher').IOWatcher; -var constants = process.binding('constants'); -var assert = require('assert').ok; - -var socket = binding.socket; -var bind = binding.bind; -var connect = binding.connect; -var listen = binding.listen; -var accept = binding.accept; -var close = binding.close; -var shutdown = binding.shutdown; -var read = binding.read; -var write = binding.write; -var toRead = binding.toRead; -var setNoDelay = binding.setNoDelay; -var setKeepAlive = binding.setKeepAlive; -var socketError = binding.socketError; -var getsockname = binding.getsockname; -var errnoException = binding.errnoException; -var sendMsg = binding.sendMsg; -var recvMsg = binding.recvMsg; - -var EINPROGRESS = constants.EINPROGRESS || constants.WSAEINPROGRESS; -var ENOENT = constants.ENOENT; -var EMFILE = constants.EMFILE; - -var END_OF_FILE = 42; - - -var ioWatchers = new FreeList('iowatcher', 100, function() { - return new IOWatcher(); -}); - -exports.isIP = binding.isIP; - -exports.isIPv4 = function(input) { - if (binding.isIP(input) === 4) { - return true; - } - return false; -}; - -exports.isIPv6 = function(input) { - if (binding.isIP(input) === 6) { - return true; - } - return false; -}; - -// Allocated on demand. -var pool = null; -function allocNewPool() { - pool = new Buffer(kPoolSize); - pool.used = 0; -} - -var emptyBuffer = null; -function allocEmptyBuffer() { - emptyBuffer = new Buffer(1); - emptyBuffer.sent = 0; - emptyBuffer.length = 0; -} - -function setImplmentationMethods(self) { - function noData(buf, off, len) { - return !buf || - (off != undefined && off >= buf.length) || - (len == 0); - }; - - if (self.type == 'unix') { - self._writeImpl = function(buf, off, len, fd, flags) { - // Detect and disallow zero-byte writes wth an attached file - // descriptor. This is an implementation limitation of sendmsg(2). - if (fd && noData(buf, off, len)) { - throw new Error('File descriptors can only be written with data'); - } - - return sendMsg(self.fd, buf, off, len, fd, flags); - }; - - self._readImpl = function(buf, off, len) { - var bytesRead = recvMsg(self.fd, buf, off, len); - - // Do not emit this in the same stack, otherwise we risk corrupting our - // buffer pool which is full of read data, but has not had had its - // pointers updated just yet. - // - // Save off recvMsg.fd in a closure so that, when we emit it later, we're - // emitting the same value that we see now. Otherwise, we can end up - // calling emit() after recvMsg() has been called again and end up - // emitting null (or another FD). - if (typeof recvMsg.fd === 'number') { - var fd = recvMsg.fd; - process.nextTick(function() { - self.emit('fd', fd); - }); - } - - return bytesRead; - }; - } else { - self._writeImpl = function(buf, off, len, fd, flags) { - // XXX: TLS support requires that 0-byte writes get processed - // by the kernel for some reason. Otherwise, we'd just - // fast-path return here. - - // Drop 'fd' and 'flags' as these are not supported by the write(2) - // system call - return write(self.fd, buf, off, len); - }; - - self._readImpl = function(buf, off, len) { - return read(self.fd, buf, off, len); - }; - } - - self._shutdownImpl = function() { - shutdown(self.fd, 'write'); - }; - -} - - -function onReadable(readable, writable) { - assert(this.socket); - var socket = this.socket; - socket._onReadable(); -} - - -function onWritable(readable, writable) { - assert(this.socket); - var socket = this.socket; - if (socket._connecting) { - assert(socket.writable); - socket._onConnect(); - } else { - socket._onWritable(); - } -} - -function initSocket(self) { - self._readWatcher = ioWatchers.alloc(); - self._readWatcher.socket = self; - self._readWatcher.callback = onReadable; - self.readable = self.destroyed = false; - - // Queue of buffers and string that need to be written to socket. - self._writeQueue = []; - self._writeQueueEncoding = []; - self._writeQueueFD = []; - self._writeQueueCallbacks = []; - // Number of charactes (which approx. equals number of bytes) - self.bufferSize = 0; - - self._writeWatcher = ioWatchers.alloc(); - self._writeWatcher.socket = self; - self._writeWatcher.callback = onWritable; - self.writable = false; - - self.bytesRead = 0; - self.bytesWritten = 0; -} - -// Deprecated API: Socket(fd, type) -// New API: Socket({ fd: 10, type: 'unix', allowHalfOpen: true }) -function Socket(options) { - if (!(this instanceof Socket)) return new Socket(arguments[0], arguments[1]); - stream.Stream.call(this); - - this.bufferSize = 0; - this.fd = null; - this.type = null; - this.allowHalfOpen = false; - - if (typeof options == 'object') { - this.fd = options.fd !== undefined ? parseInt(options.fd, 10) : null; - this.type = options.type || null; - this.allowHalfOpen = options.allowHalfOpen || false; - } else if (typeof options == 'number') { - this.fd = arguments[0]; - this.type = arguments[1]; - } - - if (parseInt(this.fd, 10) >= 0) { - this.open(this.fd, this.type); - } else { - setImplmentationMethods(this); - } -} -util.inherits(Socket, stream.Stream); -exports.Socket = Socket; - -// Legacy naming. -exports.Stream = Socket; - -Socket.prototype._onTimeout = function() { - this.emit('timeout'); -}; - - -Socket.prototype.open = function(fd, type) { - initSocket(this); - - this.fd = fd; - this.type = type || null; - this.readable = true; - - setImplmentationMethods(this); - - this._writeWatcher.set(this.fd, false, true); - this.writable = true; -}; - - -exports.createConnection = function(port, host, callback) { - var s = new Socket(); - s.connect(port, host, callback); - return s; -}; - - -Object.defineProperty(Socket.prototype, 'readyState', { - get: function() { - if (this._connecting) { - return 'opening'; - } else if (this.readable && this.writable) { - assert(typeof this.fd === 'number'); - return 'open'; - } else if (this.readable && !this.writable) { - assert(typeof this.fd === 'number'); - return 'readOnly'; - } else if (!this.readable && this.writable) { - assert(typeof this.fd === 'number'); - return 'writeOnly'; - } else { - assert(typeof this.fd !== 'number'); - return 'closed'; - } - } -}); - - -// Returns true if all the data was flushed to socket. Returns false if -// something was queued. If data was queued, then the 'drain' event will -// signal when it has been finally flushed to socket. -Socket.prototype.write = function(data /* [encoding], [fd], [cb] */) { - var encoding, fd, cb; - - assert(this.bufferSize >= 0); - - // parse arguments - if (typeof arguments[1] == 'string') { - encoding = arguments[1]; - if (typeof arguments[2] == 'number') { - fd = arguments[2]; - cb = arguments[3]; - } else { - cb = arguments[2]; - } - } else if (typeof arguments[1] == 'number') { - fd = arguments[1]; - cb = arguments[2]; - } else if (typeof arguments[2] == 'number') { - // This case is to support old calls when the encoding argument - // was not optional: s.write(buf, undefined, pipeFDs[1]) - encoding = arguments[1]; - fd = arguments[2]; - cb = arguments[3]; - } else { - cb = arguments[1]; - } - - // TODO - actually use cb - - if (this._connecting || (this._writeQueue && this._writeQueue.length)) { - if (!this._writeQueue) { - this.bufferSize = 0; - this._writeQueue = []; - this._writeQueueEncoding = []; - this._writeQueueFD = []; - this._writeQueueCallbacks = []; - } - - // Slow. There is already a write queue, so let's append to it. - if (this._writeQueueLast() === END_OF_FILE) { - throw new Error('Socket.end() called already; cannot write.'); - } - - var last = this._writeQueue.length - 1; - - this.bufferSize += data.length; - - if (typeof data == 'string' && - this._writeQueue.length && - typeof this._writeQueue[last] === 'string' && - this._writeQueueEncoding[last] === encoding) { - // optimization - concat onto last - this._writeQueue[last] += data; - - if (cb) { - if (!this._writeQueueCallbacks[last]) { - this._writeQueueCallbacks[last] = cb; - } else { - // awful - this._writeQueueCallbacks[last] = function() { - this._writeQueueCallbacks[last](); - cb(); - }; - } - } - } else { - this._writeQueue.push(data); - this._writeQueueEncoding.push(encoding); - this._writeQueueCallbacks.push(cb); - } - - if (fd != undefined) { - this._writeQueueFD.push(fd); - } - - this._onBufferChange(); - DTRACE_NET_SOCKET_WRITE(this, 0); - - return false; - } else { - // Fast. - // The most common case. There is no write queue. Just push the data - // directly to the socket. - return this._writeOut(data, encoding, fd, cb); - } -}; - -// Directly writes the data to socket. -// -// Steps: -// 1. If it's a string, write it to the `pool`. (If not space remains -// on the pool make a new one.) -// 2. Write data to socket. Return true if flushed. -// 3. Slice out remaining -// 4. Unshift remaining onto _writeQueue. Return false. -Socket.prototype._writeOut = function(data, encoding, fd, cb) { - if (!this.writable) { - throw new Error('Socket is not writable'); - } - - var buffer, off, len; - var bytesWritten, charsWritten; - var queuedData = false; - - if (typeof data != 'string') { - // 'data' is a buffer, ignore 'encoding' - buffer = data; - off = 0; - len = data.length; - - } else { - assert(typeof data == 'string'); - - if (!pool || pool.length - pool.used < kMinPoolSpace) { - pool = null; - allocNewPool(); - } - - if (!encoding || encoding == 'utf8' || encoding == 'utf-8') { - // default to utf8 - bytesWritten = pool.write(data, 'utf8', pool.used); - charsWritten = Buffer._charsWritten; - } else { - bytesWritten = pool.write(data, encoding, pool.used); - charsWritten = bytesWritten; - } - - if (encoding && data.length > 0) { - assert(bytesWritten > 0); - } - - buffer = pool; - len = bytesWritten; - off = pool.used; - - pool.used += bytesWritten; - - debug('wrote ' + bytesWritten + ' bytes to pool'); - - if (charsWritten != data.length) { - // debug('couldn't fit ' + - // (data.length - charsWritten) + - // ' bytes into the pool\n'); - // Unshift whatever didn't fit onto the buffer - assert(data.length > charsWritten); - this.bufferSize += data.length - charsWritten; - this._writeQueue.unshift(data.slice(charsWritten)); - this._writeQueueEncoding.unshift(encoding); - this._writeQueueCallbacks.unshift(cb); - this._writeWatcher.start(); - this._onBufferChange(); - queuedData = true; - } - } - - try { - bytesWritten = this._writeImpl(buffer, off, len, fd, 0); - DTRACE_NET_SOCKET_WRITE(this, bytesWritten); - } catch (e) { - this.destroy(e); - return false; - } - - this.bytesWritten += bytesWritten; - - debug('wrote ' + bytesWritten + ' bytes to socket.'); - debug('[fd, off, len] = ' + JSON.stringify([this.fd, off, len])); - - timers.active(this); - - if (bytesWritten == len) { - // awesome. sent to buffer. - if (buffer === pool) { - // If we're just writing from the pool then we can make a little - // optimization and save the space. - buffer.used -= len; - } - - if (queuedData) { - return false; - } else { - if (cb) cb(); - return true; - } - } - - // Didn't write the entire thing to buffer. - // Need to wait for the socket to become available before trying again. - this._writeWatcher.start(); - - // Slice out the data left. - var leftOver = buffer.slice(off + bytesWritten, off + len); - leftOver.used = leftOver.length; // used the whole thing... - - // util.error('data.used = ' + data.used); - //if (!this._writeQueue) initWriteSocket(this); - - // data should be the next thing to write. - this.bufferSize += leftOver.length; - this._writeQueue.unshift(leftOver); - this._writeQueueEncoding.unshift(null); - this._writeQueueCallbacks.unshift(cb); - this._onBufferChange(); - - // If didn't successfully write any bytes, enqueue our fd and try again - if (!bytesWritten) { - this._writeQueueFD.unshift(fd); - } - - return false; -}; - - -Socket.prototype._onBufferChange = function() { - // Put DTrace hooks here. -}; - - -// Flushes the write buffer out. -// Returns true if the entire buffer was flushed. -Socket.prototype.flush = function() { - while (this._writeQueue && this._writeQueue.length) { - var data = this._writeQueue.shift(); - var encoding = this._writeQueueEncoding.shift(); - var cb = this._writeQueueCallbacks.shift(); - var fd = this._writeQueueFD.shift(); - - if (data === END_OF_FILE) { - this._shutdown(); - return true; - } - - // Only decrement if it's not the END_OF_FILE object... - this.bufferSize -= data.length; - this._onBufferChange(); - - var flushed = this._writeOut(data, encoding, fd, cb); - if (!flushed) return false; - } - if (this._writeWatcher) this._writeWatcher.stop(); - return true; -}; - - -Socket.prototype._writeQueueLast = function() { - return this._writeQueue.length > 0 ? - this._writeQueue[this._writeQueue.length - 1] : null; -}; - - -Socket.prototype.setEncoding = function(encoding) { - var StringDecoder = require('string_decoder').StringDecoder; // lazy load - this._decoder = new StringDecoder(encoding); -}; - - -function doConnect(socket, port, host) { - if (socket.destroyed) return; - - timers.active(socket); - - try { - connect(socket.fd, port, host); - } catch (e) { - socket.destroy(e); - return; - } - - debug('connecting to ' + host + ' : ' + port); - - // Don't start the read watcher until connection is established - socket._readWatcher.set(socket.fd, true, false); - - // How to connect on POSIX: Wait for fd to become writable, then call - // socketError() if there isn't an error, we're connected. AFAIK this a - // platform independent way determining when a non-blocking connection - // is established, but I have only seen it documented in the Linux - // Manual Page connect(2) under the error code EINPROGRESS. - socket._writeWatcher.set(socket.fd, false, true); - socket._writeWatcher.start(); -} - - -function toPort(x) { return (x = Number(x)) >= 0 ? x : false; } - - -Socket.prototype._onConnect = function() { - var errno = socketError(this.fd); - if (errno == 0) { - // connection established - this._connecting = false; - this.resume(); - assert(this.writable); - this.readable = this.writable = true; - try { - this.emit('connect'); - } catch (e) { - this.destroy(e); - return; - } - - - if (this._writeQueue && this._writeQueue.length) { - // Flush this in case any writes are queued up while connecting. - this._onWritable(); - } - - } else if (errno != EINPROGRESS) { - this.destroy(errnoException(errno, 'connect')); - } -}; - - -Socket.prototype._onWritable = function() { - // Socket becomes writable on connect() but don't flush if there's - // nothing actually to write - if (this.flush()) { - if (this._events && this._events['drain']) this.emit('drain'); - if (this.ondrain) this.ondrain(); // Optimization - if (this.__destroyOnDrain) this.destroy(); - } -}; - - -Socket.prototype._onReadable = function() { - var self = this; - - // If this is the first recv (pool doesn't exist) or we've used up - // most of the pool, allocate a new one. - if (!pool || pool.length - pool.used < kMinPoolSpace) { - // discard the old pool. Can't add to the free list because - // users might have refernces to slices on it. - pool = null; - allocNewPool(); - } - - //debug('pool.used ' + pool.used); - var bytesRead; - - try { - bytesRead = self._readImpl(pool, - pool.used, - pool.length - pool.used); - DTRACE_NET_SOCKET_READ(this, bytesRead); - } catch (e) { - if (e.code == 'ECONNRESET') { - self.destroy(); - } else { - self.destroy(e); - } - return; - } - - // Note that some _readImpl() implementations return -1 bytes - // read as an indication not to do any processing on the result - // (but not an error). - - if (bytesRead === 0) { - self.readable = false; - self._readWatcher.stop(); - - if (!self.writable) self.destroy(); - // Note: 'close' not emitted until nextTick. - - if (!self.allowHalfOpen) self.end(); - if (self._events && self._events['end']) self.emit('end'); - if (self.onend) self.onend(); - } else if (bytesRead > 0) { - - timers.active(self); - - var start = pool.used; - var end = pool.used + bytesRead; - pool.used += bytesRead; - self.bytesRead += bytesRead; - - debug('socket ' + self.fd + ' received ' + bytesRead + ' bytes'); - - if (self._decoder) { - // emit String - var string = self._decoder.write(pool.slice(start, end)); - if (string.length) self.emit('data', string); - } else { - // emit buffer - if (self._events && self._events['data']) { - // emit a slice - self.emit('data', pool.slice(start, end)); - } - } - - // Optimization: emit the original buffer with end points - if (self.ondata) self.ondata(pool, start, end); - } -}; - - -// var socket = new Socket(); -// socket.connect(80) - TCP connect to port 80 on the localhost -// socket.connect(80, 'nodejs.org') - TCP connect to port 80 on nodejs.org -// socket.connect('/tmp/socket') - UNIX connect to socket specified by path -Socket.prototype.connect = function() { - var self = this; - initSocket(self); - if (typeof self.fd === 'number') throw new Error('Socket already opened'); - if (!self._readWatcher) throw new Error('No readWatcher'); - - timers.active(this); - - self._connecting = true; // set false in doConnect - self.writable = true; - - var host; - if (typeof arguments[1] === 'function') { - self.on('connect', arguments[1]); - } else { - host = arguments[1]; - if (typeof arguments[2] === 'function') { - self.on('connect', arguments[2]); - } - } - - var port = toPort(arguments[0]); - if (port === false) { - // UNIX - self.fd = socket('unix'); - self.type = 'unix'; - - setImplmentationMethods(this); - doConnect(self, arguments[0]); - } else { - // TCP - require('dns').lookup(host, function(err, ip, addressType) { - if (err) { - // net.createConnection() creates a net.Socket object and - // immediately calls net.Socket.connect() on it (that's us). - // There are no event listeners registered yet so defer the - // error event to the next tick. - process.nextTick(function() { - self.emit('error', err); - }); - } else { - addressType = addressType || 4; - - // node_net.cc handles null host names graciously but user land - // expects remoteAddress to have a meaningful value - ip = ip || (addressType === 4 ? '127.0.0.1' : '0:0:0:0:0:0:0:1'); - - timers.active(self); - self.type = addressType == 4 ? 'tcp4' : 'tcp6'; - self.fd = socket(self.type); - self.remoteAddress = ip; - self.remotePort = port; - doConnect(self, port, ip); - } - }); - } -}; - - -Socket.prototype.address = function() { - return getsockname(this.fd); -}; - - -Socket.prototype.setNoDelay = function(v) { - if ((this.type == 'tcp4') || (this.type == 'tcp6')) { - setNoDelay(this.fd, v); - } -}; - -Socket.prototype.setKeepAlive = function(enable, time) { - if ((this.type == 'tcp4') || (this.type == 'tcp6')) { - var secondDelay = Math.ceil(time / 1000); - setKeepAlive(this.fd, enable, secondDelay); - } -}; - -Socket.prototype.setTimeout = function(msecs, callback) { - if (msecs > 0) { - timers.enroll(this, msecs); - if (typeof this.fd === 'number') { timers.active(this); } - if (callback) { - this.once('timeout', callback); - } - } else if (msecs === 0) { - timers.unenroll(this); - } -}; - - -Socket.prototype.pause = function() { - if (this._readWatcher) this._readWatcher.stop(); -}; - - -Socket.prototype.resume = function() { - if (typeof this.fd !== 'number') { - throw new Error('Cannot resume() closed Socket.'); - } - if (this._readWatcher) { - this._readWatcher.stop(); - this._readWatcher.set(this.fd, true, false); - this._readWatcher.start(); - } -}; - -Socket.prototype.destroySoon = function() { - if (this.flush()) { - this.destroy(); - } else { - this.__destroyOnDrain = true; - } -}; - -Socket.prototype.destroy = function(exception) { - // pool is shared between sockets, so don't need to free it here. - var self = this; - - debug('destroy ' + this.fd); - - // TODO would like to set _writeQueue to null to avoid extra object alloc, - // but lots of code assumes this._writeQueue is always an array. - assert(this.bufferSize >= 0); - this._writeQueue = []; - this._writeQueueEncoding = []; - this._writeQueueCallbacks = []; - this._writeQueueFD = []; - this.bufferSize = 0; - - this.readable = this.writable = false; - - if (this._writeWatcher) { - this._writeWatcher.stop(); - this._writeWatcher.socket = null; - ioWatchers.free(this._writeWatcher); - this._writeWatcher = null; - } - - if (this._readWatcher) { - this._readWatcher.stop(); - this._readWatcher.socket = null; - ioWatchers.free(this._readWatcher); - this._readWatcher = null; - } - - timers.unenroll(this); - - if (this.server && !this.destroyed) { - this.server.connections--; - this.server._emitCloseIfDrained(); - } - - // FIXME Bug when this.fd == 0 - if (typeof this.fd === 'number') { - debug('close ' + this.fd); - close(this.fd); - this.fd = null; - process.nextTick(function() { - if (exception) self.emit('error', exception); - self.emit('close', exception ? true : false); - }); - } - - this.destroyed = true; -}; - - -Socket.prototype._shutdown = function() { - if (!this.writable) { - throw new Error('The connection is not writable'); - } else { - // readable and writable - this.writable = false; - - if (this.readable) { - - try { - this._shutdownImpl(); - } catch (e) { - if (e.code == 'ENOTCONN') { - // Allowed. - this.destroy(); - } else { - this.destroy(e); - } - } - } else { - // writable but not readable - this.destroy(); - } - } -}; - - -Socket.prototype.end = function(data, encoding) { - if (this.writable) { - if (this._writeQueueLast() !== END_OF_FILE) { - DTRACE_NET_STREAM_END(this); - if (data) this.write(data, encoding); - this._writeQueue.push(END_OF_FILE); - if (!this._connecting) { - this.flush(); - } - } - } -}; - - -function Server(/* [ options, ] listener */) { - if (!(this instanceof Server)) return new Server(arguments[0], arguments[1]); - events.EventEmitter.call(this); - var self = this; - - var options = {}; - if (typeof arguments[0] == 'object') { - options = arguments[0]; - } - - // listener: find the last argument that is a function - for (var l = arguments.length - 1; l >= 0; l--) { - if (typeof arguments[l] == 'function') { - self.addListener('connection', arguments[l]); - } - if (arguments[l] !== undefined) break; - } - - self.connections = 0; - - self.allowHalfOpen = options.allowHalfOpen || false; - - self.watcher = new IOWatcher(); - self.watcher.host = self; - self.watcher.callback = function() { - // Just in case we don't have a dummy fd. - getDummyFD(); - - if (self._pauseTimer) { - // Somehow the watcher got started again. Need to wait until - // the timer finishes. - self.watcher.stop(); - } - - while (typeof self.fd === 'number') { - try { - var peerInfo = accept(self.fd); - } catch (e) { - if (e.errno != EMFILE) throw e; - - // Gracefully reject pending clients by freeing up a file - // descriptor. - rescueEMFILE(function() { - self._rejectPending(); - }); - return; - } - if (!peerInfo) return; - - if (self.maxConnections && self.connections >= self.maxConnections) { - // Close the connection we just had - close(peerInfo.fd); - // Reject all other pending connectins. - self._rejectPending(); - return; - } - - self.connections++; - - var options = { fd: peerInfo.fd, - type: self.type, - allowHalfOpen: self.allowHalfOpen }; - var s = new Socket(options); - s.remoteAddress = peerInfo.address; - s.remotePort = peerInfo.port; - s.type = self.type; - s.server = self; - s.resume(); - - DTRACE_NET_SERVER_CONNECTION(s); - self.emit('connection', s); - - // The 'connect' event probably should be removed for server-side - // sockets. It's redundant. - try { - s.emit('connect'); - } catch (e) { - s.destroy(e); - return; - } - } - }; -} -util.inherits(Server, events.EventEmitter); -exports.Server = Server; - - -exports.createServer = function() { - return new Server(arguments[0], arguments[1]); -}; - - -// Just stop trying to accepting connections for a while. -// Useful for throttling against DoS attacks. -Server.prototype.pause = function(msecs) { - // We're already paused. - if (this._pauseTimer) return; - - var self = this; - msecs = msecs || 1000; - - this.watcher.stop(); - - // Wait a second before accepting more. - this._pauseTimer = setTimeout(function() { - // Our fd should still be there. If someone calls server.close() then - // the pauseTimer should be cleared. - assert(parseInt(self.fd) >= 0); - self._pauseTimer = null; - self.watcher.start(); - }, msecs); -}; - - -Server.prototype._rejectPending = function() { - var self = this; - var acceptCount = 0; - // Accept and close the waiting clients one at a time. - // Single threaded programming ftw. - while (true) { - var peerInfo = accept(this.fd); - if (!peerInfo) return; - close(peerInfo.fd); - - // Don't become DoS'd by incoming requests - if (++acceptCount > 50) { - this.pause(); - return; - } - } -}; - - -// Listen on a UNIX socket -// server.listen('/tmp/socket'); -// -// Listen on port 8000, accept connections from INADDR_ANY. -// server.listen(8000); -// -// Listen on port 8000, accept connections to '192.168.1.2' -// server.listen(8000, '192.168.1.2'); -Server.prototype.listen = function() { - var self = this; - if (typeof self.fd === 'number') throw new Error('Server already opened'); - - var lastArg = arguments[arguments.length - 1]; - if (typeof lastArg == 'function') { - self.addListener('listening', lastArg); - } - - var port = toPort(arguments[0]); - - if (arguments.length == 0 || typeof arguments[0] == 'function') { - // Don't bind(). OS will assign a port with INADDR_ANY. - // The port can be found with server.address() - self.type = 'tcp4'; - self.fd = socket(self.type); - self._doListen(port); - } else if (port === false) { - // the first argument specifies a path - self.fd = socket('unix'); - self.type = 'unix'; - var path = arguments[0]; - self.path = path; - // unlink sockfile if it exists - require('fs').stat(path, function(err, r) { - if (err) { - if (err.errno == ENOENT) { - self._doListen(path); - } else { - throw err; - } - } else { - if (!r.isSocket()) { - throw new Error('Non-socket exists at ' + path); - } else { - require('fs').unlink(path, function(err) { - if (err) throw err; - self._doListen(path); - }); - } - } - }); - } else { - // the first argument is the port, the second an IP - require('dns').lookup(arguments[1], function(err, ip, addressType) { - if (err) { - self.emit('error', err); - } else { - self.type = addressType == 4 ? 'tcp4' : 'tcp6'; - self.fd = socket(self.type); - self._doListen(port, ip); - } - }); - } -}; - -Server.prototype.listenFD = function(fd, type) { - if (typeof this.fd === 'number') { - throw new Error('Server already opened'); - } - - this.fd = fd; - this.type = type || null; - this._startWatcher(); -}; - -Server.prototype._startWatcher = function() { - this.watcher.set(this.fd, true, false); - this.watcher.start(); - this.emit('listening'); -}; - -Server.prototype._doListen = function() { - var self = this; - - // Ensure we have a dummy fd for EMFILE conditions. - getDummyFD(); - - try { - bind(self.fd, arguments[0], arguments[1]); - } catch (err) { - self.close(); - self.emit('error', err); - return; - } - - // Need to the listening in the nextTick so that people potentially have - // time to register 'listening' listeners. - process.nextTick(function() { - // It could be that server.close() was called between the time the - // original listen command was issued and this. Bail if that's the case. - // See test/simple/test-net-eaddrinuse.js - if (typeof self.fd !== 'number') return; - - try { - listen(self.fd, self._backlog || 128); - } catch (err) { - self.close(); - self.emit('error', err); - return; - } - - self._startWatcher(); - }); -}; - - -Server.prototype.address = function() { - return getsockname(this.fd); -}; - - -Server.prototype.close = function() { - var self = this; - if (typeof self.fd !== 'number') throw new Error('Not running'); - - self.watcher.stop(); - - close(self.fd); - self.fd = null; - - if (self._pauseTimer) { - clearTimeout(self._pauseTimer); - self._pauseTimer = null; - } - - if (self.type === 'unix') { - require('fs').unlink(self.path, function() { - self._emitCloseIfDrained(); - }); - } else { - self._emitCloseIfDrained(); - } -}; - -Server.prototype._emitCloseIfDrained = function() { - if ((typeof this.fd !== 'number') && !this.connections) { - this.emit('close'); - } -}; - -var dummyFD = null; -var lastEMFILEWarning = 0; -// Ensures to have at least on free file-descriptor free. -// callback should only use 1 file descriptor and close it before end of call -function rescueEMFILE(callback) { - // Output a warning, but only at most every 5 seconds. - var now = new Date(); - if (now - lastEMFILEWarning > 5000) { - console.error('(node) Hit max file limit. Increase "ulimit - n"'); - lastEMFILEWarning = now; - } - - if (dummyFD) { - close(dummyFD); - dummyFD = null; - callback(); - getDummyFD(); - } -} - -function getDummyFD() { - if (!dummyFD) { - try { - dummyFD = socket('tcp'); - } catch (e) { - dummyFD = null; - } - } -} diff --git a/lib/timers_uv.js b/lib/timers.js similarity index 100% rename from lib/timers_uv.js rename to lib/timers.js diff --git a/lib/timers_legacy.js b/lib/timers_legacy.js deleted file mode 100644 index d6fede1fa0..0000000000 --- a/lib/timers_legacy.js +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var Timer = process.binding('timer').Timer; -var L = require('_linklist'); -var assert = require('assert').ok; - -var debug; -if (process.env.NODE_DEBUG && /timer/.test(process.env.NODE_DEBUG)) { - debug = function() { require('util').error.apply(this, arguments); }; -} else { - debug = function() { }; -} - - -// IDLE TIMEOUTS -// -// Because often many sockets will have the same idle timeout we will not -// use one timeout watcher per item. It is too much overhead. Instead -// we'll use a single watcher for all sockets with the same timeout value -// and a linked list. This technique is described in the libev manual: -// http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#Be_smart_about_timeouts - -// Object containing all lists, timers -// key = time in milliseconds -// value = list -var lists = {}; - - -// the main function - creates lists on demand and the watchers associated -// with them. -function insert(item, msecs) { - item._idleStart = new Date(); - item._idleTimeout = msecs; - - if (msecs < 0) return; - - var list; - - if (lists[msecs]) { - list = lists[msecs]; - } else { - list = new Timer(); - L.init(list); - - lists[msecs] = list; - - list.callback = function() { - debug('timeout callback ' + msecs); - // TODO - don't stop and start the watcher all the time. - // just set its repeat - var now = new Date(); - debug('now: ' + now); - - var first; - while (first = L.peek(list)) { - var diff = now - first._idleStart; - if (diff + 1 < msecs) { - list.again(msecs - diff); - debug(msecs + ' list wait because diff is ' + diff); - return; - } else { - L.remove(first); - assert(first !== L.peek(list)); - if (first._onTimeout) first._onTimeout(); - } - } - - debug(msecs + ' list empty'); - assert(L.isEmpty(list)); - list.stop(); - }; - } - - if (L.isEmpty(list)) { - // if empty (re)start the timer - list.again(msecs); - } - - L.append(list, item); - assert(!L.isEmpty(list)); // list is not empty -} - - -var unenroll = exports.unenroll = function(item) { - L.remove(item); - - var list = lists[item._idleTimeout]; - // if empty then stop the watcher - debug('unenroll'); - if (list && L.isEmpty(list)) { - debug('unenroll: list empty'); - list.stop(); - } -}; - - -// Does not start the time, just sets up the members needed. -exports.enroll = function(item, msecs) { - // if this item was already in a list somewhere - // then we should unenroll it from that - if (item._idleNext) unenroll(item); - - item._idleTimeout = msecs; - L.init(item); -}; - - -// call this whenever the item is active (not idle) -// it will reset its timeout. -exports.active = function(item) { - var msecs = item._idleTimeout; - if (msecs >= 0) { - var list = lists[msecs]; - if (item._idleNext == item) { - insert(item, msecs); - } else { - item._idleStart = new Date(); - L.append(list, item); - } - } -}; - - -/* - * DOM-style timers - */ - - -exports.setTimeout = function(callback, after) { - var timer; - - if (after <= 0) { - // Use the slow case for after == 0 - timer = new Timer(); - timer.callback = callback; - } else { - timer = { _idleTimeout: after, _onTimeout: callback }; - timer._idlePrev = timer; - timer._idleNext = timer; - } - - /* - * Sometimes setTimeout is called with arguments, EG - * - * setTimeout(callback, 2000, "hello", "world") - * - * If that's the case we need to call the callback with - * those args. The overhead of an extra closure is not - * desired in the normal case. - */ - if (arguments.length > 2) { - var args = Array.prototype.slice.call(arguments, 2); - var c = function() { - callback.apply(timer, args); - }; - - if (timer instanceof Timer) { - timer.callback = c; - } else { - timer._onTimeout = c; - } - } - - if (timer instanceof Timer) { - timer.start(0, 0); - } else { - exports.active(timer); - } - - return timer; -}; - - -exports.clearTimeout = function(timer) { - if (timer && (timer.callback || timer._onTimeout)) { - timer.callback = timer._onTimeout = null; - exports.unenroll(timer); - if (timer instanceof Timer) timer.stop(); // for after === 0 - } -}; - - -exports.setInterval = function(callback, repeat) { - var timer = new Timer(); - - if (arguments.length > 2) { - var args = Array.prototype.slice.call(arguments, 2); - timer.callback = function() { - callback.apply(timer, args); - }; - } else { - timer.callback = callback; - } - - timer.start(repeat, repeat ? repeat : 1); - return timer; -}; - - -exports.clearInterval = function(timer) { - if (timer instanceof Timer) { - timer.callback = null; - timer.stop(); - } -}; diff --git a/node.gyp b/node.gyp index b15d4f1a5a..c87c12e90f 100644 --- a/node.gyp +++ b/node.gyp @@ -30,8 +30,7 @@ 'lib/http.js', 'lib/https.js', 'lib/module.js', - 'lib/net_legacy.js', - 'lib/net_uv.js', + 'lib/net.js', 'lib/os.js', 'lib/path.js', 'lib/punycode.js', @@ -41,8 +40,7 @@ 'lib/stream.js', 'lib/string_decoder.js', 'lib/sys.js', - 'lib/timers_legacy.js', - 'lib/timers_uv.js', + 'lib/timers.js', 'lib/tls.js', 'lib/tty_legacy.js', 'lib/tty_posix.js', @@ -112,7 +110,6 @@ 'src/node_file.h', 'src/node_http_parser.h', 'src/node_javascript.h', - 'src/node_net.h', 'src/node_os.h', 'src/node_root_certs.h', 'src/node_script.h', @@ -174,13 +171,11 @@ 'defines': [ '__POSIX__' ], 'sources': [ 'src/node_cares.cc', - 'src/node_net.cc', 'src/node_signal_watcher.cc', 'src/node_stat_watcher.cc', 'src/node_io_watcher.cc', 'src/node_stdio.cc', 'src/node_child_process.cc', - 'src/node_timer.cc' ] }], [ 'OS=="mac"', { diff --git a/src/node.cc b/src/node.cc index 99fd0eb80b..8a1eea5185 100644 --- a/src/node.cc +++ b/src/node.cc @@ -77,14 +77,12 @@ extern "C" { #ifdef __POSIX__ # include #endif -#include #include #include #include #ifdef __POSIX__ # include # include -# include #endif #if !defined(_MSC_VER) #include @@ -1902,13 +1900,6 @@ static Handle Binding(const Arguments& args) { binding_cache->Set(module, exports); #endif - } else if (!strcmp(*module_v, "timer")) { -#ifdef __POSIX__ - exports = Object::New(); - Timer::Initialize(exports); - binding_cache->Set(module, exports); - -#endif } else if (!strcmp(*module_v, "natives")) { exports = Object::New(); DefineJavaScript(exports); diff --git a/src/node.js b/src/node.js index 06936bc32a..28ca90116e 100644 --- a/src/node.js +++ b/src/node.js @@ -458,9 +458,6 @@ // backend. function translateId(id) { switch (id) { - case 'net': - return process.features.uv ? 'net_uv' : 'net_legacy'; - case 'tty': return process.features.uv ? 'tty_uv' : 'tty_legacy'; @@ -468,9 +465,6 @@ return process.features.uv ? 'child_process_uv' : 'child_process_legacy'; - case 'timers': - return process.features.uv ? 'timers_uv' : 'timers_legacy'; - case 'dgram': return process.features.uv ? 'dgram_uv' : 'dgram_legacy'; diff --git a/src/node_extensions.h b/src/node_extensions.h index a9023c62f7..3b0731605e 100644 --- a/src/node_extensions.h +++ b/src/node_extensions.h @@ -31,9 +31,6 @@ NODE_EXT_LIST_ITEM(node_crypto) #endif NODE_EXT_LIST_ITEM(node_evals) NODE_EXT_LIST_ITEM(node_fs) -#ifdef __POSIX__ -NODE_EXT_LIST_ITEM(node_net) -#endif NODE_EXT_LIST_ITEM(node_http_parser) #ifdef __POSIX__ NODE_EXT_LIST_ITEM(node_signal_watcher) diff --git a/src/node_net.cc b/src/node_net.cc deleted file mode 100644 index b73c5db2f2..0000000000 --- a/src/node_net.cc +++ /dev/null @@ -1,1781 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - - -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include - -#ifdef __MINGW32__ -# include -#endif - -#ifdef __POSIX__ -# include -# include -# include -# include /* inet_pton */ -# include -# include -# include -#endif - -#ifdef __linux__ -# include /* For the SIOCINQ / FIONREAD ioctl */ -#endif - -/* Non-linux platforms like OS X define this ioctl elsewhere */ -#ifndef FIONREAD -# include -#endif - -#ifdef __OpenBSD__ -# include -#endif - -/* - * HACK to use inet_pton/inet_ntop from c-ares because mingw32 doesn't have it - * This trick is used in node_ares.cc as well - * TODO fixme - */ -#ifdef __MINGW32__ - extern "C" { -# include -# include - } - -# define inet_pton ares_inet_pton -# define inet_ntop ares_inet_ntop -#endif - -// SHUT_* constants aren't available on windows but there are 1:1 equivalents -#ifdef __MINGW32__ -# define SHUT_RD SD_RECEIVE -# define SHUT_WR SD_SEND -# define SHUT_RDWR SD_BOTH -#endif - - -namespace node { - -using namespace v8; - -static Persistent errno_symbol; -static Persistent syscall_symbol; - -static Persistent fd_symbol; -static Persistent size_symbol; -static Persistent address_symbol; -static Persistent port_symbol; -static Persistent type_symbol; -static Persistent tcp_symbol; -static Persistent unix_symbol; - -static Persistent recv_msg_template; - - -#define FD_ARG(a) \ - int fd; \ - if (!(a)->IsInt32() || (fd = (a)->Int32Value()) < 0) { \ - return ThrowException(Exception::TypeError( \ - String::New("Bad file descriptor argument"))); \ - } - - -static inline bool SetCloseOnExec(int fd) { -#ifdef __POSIX__ - return (fcntl(fd, F_SETFD, FD_CLOEXEC) != -1); -#else // __MINGW32__ - return SetHandleInformation(reinterpret_cast(_get_osfhandle(fd)), - HANDLE_FLAG_INHERIT, 0) != 0; -#endif -} - - -static inline bool SetNonBlock(int fd) { -#ifdef __MINGW32__ - unsigned long value = 1; - return (ioctlsocket(_get_osfhandle(fd), FIONBIO, &value) == 0); -#else // __POSIX__ - return (fcntl(fd, F_SETFL, O_NONBLOCK) != -1); -#endif -} - - -static inline bool SetSockFlags(int fd) { -#ifdef __MINGW32__ - BOOL flags = TRUE; - setsockopt(_get_osfhandle(fd), SOL_SOCKET, SO_REUSEADDR, (const char *)&flags, sizeof(flags)); -#else // __POSIX__ - int flags = 1; - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags)); -#endif - return SetNonBlock(fd) && SetCloseOnExec(fd); -} - - -#ifdef __POSIX__ - -// Creates nonblocking pipe -static Handle Pipe(const Arguments& args) { - HandleScope scope; - int fds[2]; - - if (pipe(fds) < 0) return ThrowException(ErrnoException(errno, "pipe")); - - if (!SetSockFlags(fds[0]) || !SetSockFlags(fds[1])) { - int fcntl_errno = errno; - close(fds[0]); - close(fds[1]); - return ThrowException(ErrnoException(fcntl_errno, "fcntl")); - } - - Local a = Array::New(2); - a->Set(Integer::New(0), Integer::New(fds[0])); - a->Set(Integer::New(1), Integer::New(fds[1])); - return scope.Close(a); -} - - -// Creates nonblocking socket pair -static Handle SocketPair(const Arguments& args) { - HandleScope scope; - - int fds[2]; - - // XXX support SOCK_DGRAM? - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { - return ThrowException(ErrnoException(errno, "socketpair")); - } - - if (!SetSockFlags(fds[0]) || !SetSockFlags(fds[1])) { - int fcntl_errno = errno; - close(fds[0]); - close(fds[1]); - return ThrowException(ErrnoException(fcntl_errno, "fcntl")); - } - - Local a = Array::New(2); - a->Set(Integer::New(0), Integer::New(fds[0])); - a->Set(Integer::New(1), Integer::New(fds[1])); - return scope.Close(a); -} - -#endif - - -// Creates a new non-blocking socket fd -// t.socket("TCP"); -// t.socket("UNIX"); -// t.socket("UDP"); -static Handle Socket(const Arguments& args) { - HandleScope scope; - - // default to TCP - int domain = PF_INET; - int type = SOCK_STREAM; -#ifdef SO_REUSEPORT - bool set_reuseport = false; -#endif - - if (args[0]->IsString()) { - String::Utf8Value t(args[0]->ToString()); - // FIXME optimize this cascade. - if (0 == strcasecmp(*t, "TCP")) { - domain = PF_INET; - type = SOCK_STREAM; - } else if (0 == strcasecmp(*t, "TCP4")) { - domain = PF_INET; - type = SOCK_STREAM; - } else if (0 == strcasecmp(*t, "TCP6")) { - domain = PF_INET6; - type = SOCK_STREAM; - } else if (0 == strcasecmp(*t, "UNIX")) { - domain = PF_UNIX; - type = SOCK_STREAM; - } else if (0 == strcasecmp(*t, "UNIX_DGRAM")) { - domain = PF_UNIX; - type = SOCK_DGRAM; - } else if (0 == strcasecmp(*t, "UDP")) { - domain = PF_INET; - type = SOCK_DGRAM; -#ifdef SO_REUSEPORT - set_reuseport = true; -#endif - } else if (0 == strcasecmp(*t, "UDP4")) { - domain = PF_INET; - type = SOCK_DGRAM; -#ifdef SO_REUSEPORT - set_reuseport = true; -#endif - } else if (0 == strcasecmp(*t, "UDP6")) { - domain = PF_INET6; - type = SOCK_DGRAM; -#ifdef SO_REUSEPORT - set_reuseport = true; -#endif - } else { - return ThrowException(Exception::Error( - String::New("Unknown socket type."))); - } - } - -#ifdef __POSIX__ - int fd = socket(domain, type, 0); -#else // __MINGW32__ - int fd = _open_osfhandle(socket(domain, type, 0), 0); -#endif - - if (fd < 0) return ThrowException(ErrnoException(errno, "socket")); - - if (!SetSockFlags(fd)) { - int fcntl_errno = errno; - close(fd); - return ThrowException(ErrnoException(fcntl_errno, "fcntl")); - } - -#ifdef SO_REUSEPORT - // needed for datagrams to be able to have multiple processes listening to - // e.g. broadcasted datagrams. - if (set_reuseport) { - int flags = 1; - setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (const char *)&flags, - sizeof(flags)); - } -#endif - - return scope.Close(Integer::New(fd)); -} - - -// NOT AT ALL THREAD SAFE - but that's okay for node.js -// (yes this is all to avoid one small heap alloc) -static struct sockaddr *addr; -static socklen_t addrlen; -static inline Handle ParseAddressArgs(Handle first, - Handle second, - bool is_bind) { - static struct sockaddr_in in; - static struct sockaddr_in6 in6; - -#ifdef __POSIX__ // No unix sockets on windows - static struct sockaddr_un un; - - if (first->IsString() && !second->IsString()) { - // UNIX - String::Utf8Value path(first->ToString()); - - if ((size_t) path.length() >= ARRAY_SIZE(un.sun_path)) { - return Exception::Error(String::New("Socket path too long")); - } - - memset(&un, 0, sizeof un); - un.sun_family = AF_UNIX; - memcpy(un.sun_path, *path, path.length()); - - addr = (struct sockaddr*)&un; - addrlen = sizeof(un) - sizeof(un.sun_path) + path.length() + 1; - - } else { -#else // __MINGW32__ - if (first->IsString() && !second->IsString()) { - return ErrnoException(errno, "ParseAddressArgs", "Unix sockets are not supported on windows"); - } else { -#endif - // TCP or UDP - memset(&in, 0, sizeof in); - memset(&in6, 0, sizeof in6); - - int port = first->Int32Value(); - in.sin_port = in6.sin6_port = htons(port); - in.sin_family = AF_INET; - in6.sin6_family = AF_INET6; - - bool is_ipv4 = true; - - if (!second->IsString()) { - in.sin_addr.s_addr = htonl(is_bind ? INADDR_ANY : INADDR_LOOPBACK); - in6.sin6_addr = is_bind ? in6addr_any : in6addr_loopback; - } else { - String::Utf8Value ip(second->ToString()); - - if (inet_pton(AF_INET, *ip, &(in.sin_addr)) <= 0) { - is_ipv4 = false; - if (inet_pton(AF_INET6, *ip, &(in6.sin6_addr)) <= 0) { - return ErrnoException(errno, "inet_pton", "Invalid IP Address"); - } - } - } - - addr = is_ipv4 ? (struct sockaddr*)&in : (struct sockaddr*)&in6; - addrlen = is_ipv4 ? sizeof in : sizeof in6; - } - return Handle(); -} - - -// Bind with UNIX -// t.bind(fd, "/tmp/socket") -// Bind with TCP -// t.bind(fd, 80, "192.168.11.2") -// t.bind(fd, 80) -static Handle Bind(const Arguments& args) { - HandleScope scope; - - if (args.Length() < 2) { - return ThrowException(Exception::TypeError( - String::New("Must have at least two args"))); - } - - FD_ARG(args[0]) - - Handle error = ParseAddressArgs(args[1], args[2], true); - if (!error.IsEmpty()) return ThrowException(error); - - int flags = 1; - -#ifdef __POSIX__ - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags)); - - if (0 > bind(fd, addr, addrlen)) { - return ThrowException(ErrnoException(errno, "bind")); - } - -#else // __MINGW32__ - SOCKET handle = _get_osfhandle(fd); - setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, (char *)&flags, sizeof(flags)); - - if (SOCKET_ERROR == bind(handle, addr, addrlen)) { - return ThrowException(ErrnoException(WSAGetLastError(), "bind")); - } -#endif // __MINGW32__ - - return Undefined(); -} - - -static Handle Close(const Arguments& args) { - HandleScope scope; - - FD_ARG(args[0]) - - // Windows: this is not a winsock operation, don't use _get_osfhandle here! - if (0 > close(fd)) { - return ThrowException(ErrnoException(errno, "close")); - } - - return Undefined(); -} - - -// t.shutdown(fd, "read"); -- SHUT_RD -// t.shutdown(fd, "write"); -- SHUT_WR -// t.shutdown(fd, "readwrite"); -- SHUT_RDWR -// second arg defaults to "write". -static Handle Shutdown(const Arguments& args) { - HandleScope scope; - - FD_ARG(args[0]) - - int how = SHUT_WR; - - if (args[1]->IsString()) { - String::Utf8Value t(args[1]->ToString()); - if (0 == strcasecmp(*t, "write")) { - how = SHUT_WR; - } else if (0 == strcasecmp(*t, "read")) { - how = SHUT_RD; - } else if (0 == strcasecmp(*t, "readwrite")) { - how = SHUT_RDWR; - } else { - return ThrowException(Exception::Error(String::New( - "Unknown shutdown method. (Use 'read', 'write', or 'readwrite'.)"))); - } - } - -#ifdef __POSIX__ - if (0 > shutdown(fd, how)) { - return ThrowException(ErrnoException(errno, "shutdown")); - } -#else // __MINGW32__ - if (SOCKET_ERROR == shutdown(_get_osfhandle(fd), how)) { - return ThrowException(ErrnoException(WSAGetLastError(), "shutdown")); - } -#endif // __MINGW32__ - - return Undefined(); -} - - -// Connect with unix -// t.connect(fd, "/tmp/socket") -// -// Connect with TCP or UDP -// t.connect(fd, 80, "192.168.11.2") -// t.connect(fd, 80, "::1") -// t.connect(fd, 80) -// the third argument defaults to "::1" -static Handle Connect(const Arguments& args) { - HandleScope scope; - - if (args.Length() < 2) { - return ThrowException(Exception::TypeError( - String::New("Must have at least two args"))); - } - - FD_ARG(args[0]) - - Handle error = ParseAddressArgs(args[1], args[2], false); - if (!error.IsEmpty()) return ThrowException(error); - -#ifdef __POSIX__ - int r = connect(fd, addr, addrlen); - - if (r < 0 && errno != EINPROGRESS) { - return ThrowException(ErrnoException(errno, "connect")); - } -#else // __MINGW32__ - int r = connect(_get_osfhandle(fd), addr, addrlen); - - if (r == SOCKET_ERROR) { - int wsaErrno = WSAGetLastError(); - if (wsaErrno != WSAEWOULDBLOCK && wsaErrno != WSAEINPROGRESS) { - return ThrowException(ErrnoException(wsaErrno, "connect")); - } - } -#endif // __MINGW32__ - - return Undefined(); -} - - -#ifdef __POSIX__ - -#define ADDRESS_TO_JS(info, address_storage, addrlen) \ -do { \ - char ip[INET6_ADDRSTRLEN]; \ - int port; \ - struct sockaddr_in *a4; \ - struct sockaddr_in6 *a6; \ - struct sockaddr_un *au; \ - if (addrlen == 0) { \ - (info)->Set(address_symbol, String::Empty()); \ - } else { \ - switch ((address_storage).ss_family) { \ - case AF_INET6: \ - a6 = (struct sockaddr_in6*)&(address_storage); \ - inet_ntop(AF_INET6, &(a6->sin6_addr), ip, INET6_ADDRSTRLEN); \ - port = ntohs(a6->sin6_port); \ - (info)->Set(address_symbol, String::New(ip)); \ - (info)->Set(port_symbol, Integer::New(port)); \ - break; \ - case AF_INET: \ - a4 = (struct sockaddr_in*)&(address_storage); \ - inet_ntop(AF_INET, &(a4->sin_addr), ip, INET6_ADDRSTRLEN); \ - port = ntohs(a4->sin_port); \ - (info)->Set(address_symbol, String::New(ip)); \ - (info)->Set(port_symbol, Integer::New(port)); \ - break; \ - case AF_UNIX: \ - /* - * Three types of addresses (see man 7 unix): - * * unnamed: sizeof(sa_family_t) (sun_path should not be used) - * * abstract (Linux extension): sizeof(struct sockaddr_un) - * * pathname: sizeof(sa_family_t) + strlen(sun_path) + 1 - */ \ - au = (struct sockaddr_un*)&(address_storage); \ - if (addrlen == sizeof(sa_family_t)) { \ - (info)->Set(address_symbol, String::Empty()); \ - } else if (addrlen == sizeof(struct sockaddr_un)) { \ - /* first byte is '\0' and all remaining bytes are name; - * it is not NUL-terminated and may contain embedded NULs */ \ - (info)->Set(address_symbol, String::New(au->sun_path + 1, sizeof(au->sun_path) - 1)); \ - } else { \ - (info)->Set(address_symbol, String::New(au->sun_path)); \ - } \ - break; \ - default: \ - (info)->Set(address_symbol, String::Empty()); \ - } \ - } \ -} while (0) - -#else // __MINGW32__ - -#define ADDRESS_TO_JS(info, address_storage, addrlen) \ -do { \ - char ip[INET6_ADDRSTRLEN]; \ - int port; \ - struct sockaddr_in *a4; \ - struct sockaddr_in6 *a6; \ - if (addrlen == 0) { \ - (info)->Set(address_symbol, String::Empty()); \ - } else { \ - switch ((address_storage).ss_family) { \ - case AF_INET6: \ - a6 = (struct sockaddr_in6*)&(address_storage); \ - inet_ntop(AF_INET6, &(a6->sin6_addr), ip, INET6_ADDRSTRLEN); \ - port = ntohs(a6->sin6_port); \ - (info)->Set(address_symbol, String::New(ip)); \ - (info)->Set(port_symbol, Integer::New(port)); \ - break; \ - case AF_INET: \ - a4 = (struct sockaddr_in*)&(address_storage); \ - inet_ntop(AF_INET, &(a4->sin_addr), ip, INET6_ADDRSTRLEN); \ - port = ntohs(a4->sin_port); \ - (info)->Set(address_symbol, String::New(ip)); \ - (info)->Set(port_symbol, Integer::New(port)); \ - break; \ - default: \ - (info)->Set(address_symbol, String::Empty()); \ - } \ - } \ -} while (0) - -#endif // __MINGW32__ - - -static Handle GetSockName(const Arguments& args) { - HandleScope scope; - - FD_ARG(args[0]) - - struct sockaddr_storage address_storage; - socklen_t len = sizeof(struct sockaddr_storage); - -#ifdef __POSIX__ - if (0 > getsockname(fd, (struct sockaddr *) &address_storage, &len)) { - return ThrowException(ErrnoException(errno, "getsockname")); - } - -#else // __MINGW32__ - if (SOCKET_ERROR == getsockname(_get_osfhandle(fd), - (struct sockaddr *) &address_storage, &len)) { - return ThrowException(ErrnoException(WSAGetLastError(), "getsockname")); - } -#endif // __MINGW32__ - - Local info = Object::New(); - ADDRESS_TO_JS(info, address_storage, len); - return scope.Close(info); -} - - -static Handle GetPeerName(const Arguments& args) { - HandleScope scope; - - FD_ARG(args[0]) - - struct sockaddr_storage address_storage; - socklen_t len = sizeof(struct sockaddr_storage); - -#ifdef __POSIX__ - if (0 > getpeername(fd, (struct sockaddr *) &address_storage, &len)) { - return ThrowException(ErrnoException(errno, "getpeername")); - } - -#else // __MINGW32__ - if (SOCKET_ERROR == getpeername(_get_osfhandle(fd), - (struct sockaddr *) &address_storage, &len)) { - return ThrowException(ErrnoException(WSAGetLastError(), "getpeername")); - } -#endif // __MINGW32__ - - Local info = Object::New(); - ADDRESS_TO_JS(info, address_storage, len); - return scope.Close(info); -} - - -static Handle Listen(const Arguments& args) { - HandleScope scope; - - FD_ARG(args[0]) - int backlog = args[1]->IsInt32() ? args[1]->Int32Value() : 128; - -#ifdef __POSIX__ - if (0 > listen(fd, backlog)) { - return ThrowException(ErrnoException(errno, "listen")); - } -#else // __MINGW32__ - if (SOCKET_ERROR == listen(_get_osfhandle(fd), backlog)) { - return ThrowException(ErrnoException(WSAGetLastError(), "listen")); - } -#endif - - return Undefined(); -} - - -// var peerInfo = t.accept(server_fd); -// -// peerInfo.fd -// peerInfo.address -// peerInfo.port -// -// Returns a new nonblocking socket fd. If the listen queue is empty the -// function returns null (wait for server_fd to become readable and try -// again) -static Handle Accept(const Arguments& args) { - HandleScope scope; - - FD_ARG(args[0]) - - struct sockaddr_storage address_storage; - socklen_t len = sizeof(struct sockaddr_storage); - -#ifdef __POSIX__ - int peer_fd = accept(fd, (struct sockaddr*) &address_storage, &len); - - if (peer_fd < 0) { - if (errno == EAGAIN) return scope.Close(Null()); - if (errno == ECONNABORTED) return scope.Close(Null()); - return ThrowException(ErrnoException(errno, "accept")); - } -#else // __MINGW32__ - SOCKET peer_handle = accept(_get_osfhandle(fd), (struct sockaddr*) &address_storage, &len); - - if (peer_handle == INVALID_SOCKET) { - int wsaErrno = WSAGetLastError(); - if (wsaErrno == WSAEWOULDBLOCK) return scope.Close(Null()); - return ThrowException(ErrnoException(wsaErrno, "accept")); - } - - int peer_fd = _open_osfhandle(peer_handle, 0); -#endif // __MINGW32__ - - if (!SetSockFlags(peer_fd)) { -#ifdef __POSIX__ - int fcntl_errno = errno; -#else // __MINGW32__ - int fcntl_errno = WSAGetLastError(); -#endif // __MINGW32__ - close(peer_fd); - return ThrowException(ErrnoException(fcntl_errno, "fcntl")); - } - - Local peer_info = Object::New(); - - peer_info->Set(fd_symbol, Integer::New(peer_fd)); - - ADDRESS_TO_JS(peer_info, address_storage, len); - - return scope.Close(peer_info); -} - - -static Handle SocketError(const Arguments& args) { - HandleScope scope; - - FD_ARG(args[0]) - - int error; - socklen_t len = sizeof(int); - -#ifdef __POSIX__ - int r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len); - - if (r < 0) { - return ThrowException(ErrnoException(errno, "getsockopt")); - } -#else // __MINGW32__ - int r = getsockopt(_get_osfhandle(fd), SOL_SOCKET, SO_ERROR, (char*)&error, &len); - - if (r < 0) { - return ThrowException(ErrnoException(WSAGetLastError(), "getsockopt")); - } -#endif - - return scope.Close(Integer::New(error)); -} - - -// var bytesRead = t.read(fd, buffer, offset, length); -// returns null on EAGAIN or EINTR, raises an exception on all other errors -// returns 0 on EOF. -static Handle Read(const Arguments& args) { - HandleScope scope; - - if (args.Length() < 4) { - return ThrowException(Exception::TypeError( - String::New("Takes 4 parameters"))); - } - - FD_ARG(args[0]) - - if (!Buffer::HasInstance(args[1])) { - return ThrowException(Exception::TypeError( - String::New("Second argument should be a buffer"))); - } - - Local buffer_obj = args[1]->ToObject(); - char *buffer_data = Buffer::Data(buffer_obj); - size_t buffer_length = Buffer::Length(buffer_obj); - - size_t off = args[2]->Int32Value(); - if (off >= buffer_length) { - return ThrowException(Exception::Error( - String::New("Offset is out of bounds"))); - } - - size_t len = args[3]->Int32Value(); - if (off + len > buffer_length) { - return ThrowException(Exception::Error( - String::New("Length is extends beyond buffer"))); - } - -#ifdef __POSIX__ - ssize_t bytes_read = read(fd, (char*)buffer_data + off, len); - - if (bytes_read < 0) { - if (errno == EAGAIN || errno == EINTR) return Null(); - return ThrowException(ErrnoException(errno, "read")); - } -#else // __MINGW32__ - // read() doesn't work for overlapped sockets (the only usable - // type of sockets) so recv() is used here. - ssize_t bytes_read = recv(_get_osfhandle(fd), (char*)buffer_data + off, len, 0); - - if (bytes_read < 0) { - int wsaErrno = WSAGetLastError(); - if (wsaErrno == WSAEWOULDBLOCK || wsaErrno == WSAEINTR) return Null(); - return ThrowException(ErrnoException(wsaErrno, "read")); - } -#endif - - return scope.Close(Integer::New(bytes_read)); -} - - -// var info = t.recvfrom(fd, buffer, offset, length, flags); -// info.size // bytes read -// info.port // from port -// info.address // from address -// returns null on EAGAIN or EINTR, raises an exception on all other errors -// returns object otherwise -static Handle RecvFrom(const Arguments& args) { - HandleScope scope; - - if (args.Length() < 5) { - return ThrowException(Exception::TypeError( - String::New("Takes 5 parameters"))); - } - - FD_ARG(args[0]) - - if (!Buffer::HasInstance(args[1])) { - return ThrowException(Exception::TypeError( - String::New("Second argument should be a buffer"))); - } - - Local buffer_obj = args[1]->ToObject(); - char *buffer_data = Buffer::Data(buffer_obj); - size_t buffer_length = Buffer::Length(buffer_obj); - - size_t off = args[2]->Int32Value(); - if (off >= buffer_length) { - return ThrowException(Exception::Error( - String::New("Offset is out of bounds"))); - } - - size_t len = args[3]->Int32Value(); - if (off + len > buffer_length) { - return ThrowException(Exception::Error( - String::New("Length is extends beyond buffer"))); - } - - int flags = args[4]->Int32Value(); - - struct sockaddr_storage address_storage; - socklen_t addrlen = sizeof(struct sockaddr_storage); - -#ifdef __POSIX__ - ssize_t bytes_read = recvfrom(fd, (char*)buffer_data + off, len, flags, - (struct sockaddr*) &address_storage, &addrlen); - - if (bytes_read < 0) { - if (errno == EAGAIN || errno == EINTR) return Null(); - return ThrowException(ErrnoException(errno, "read")); - } - -#else // __MINGW32__ - ssize_t bytes_read = recvfrom(_get_osfhandle(fd), (char*)buffer_data + off, - len, flags, (struct sockaddr*) &address_storage, &addrlen); - - if (bytes_read == SOCKET_ERROR) { - int wsaErrno = WSAGetLastError(); - if (wsaErrno == WSAEWOULDBLOCK || wsaErrno == WSAEINTR) return Null(); - return ThrowException(ErrnoException(wsaErrno, "read")); - } -#endif - - Local info = Object::New(); - - info->Set(size_symbol, Integer::New(bytes_read)); - - ADDRESS_TO_JS(info, address_storage, addrlen); - - return scope.Close(info); -} - - -#ifdef __POSIX__ - -// bytesRead = t.recvMsg(fd, buffer, offset, length) -// if (recvMsg.fd) { -// receivedFd = recvMsg.fd; -// } -static Handle RecvMsg(const Arguments& args) { - HandleScope scope; - - if (args.Length() < 4) { - return ThrowException(Exception::TypeError( - String::New("Takes 4 parameters"))); - } - - FD_ARG(args[0]) - - if (!Buffer::HasInstance(args[1])) { - return ThrowException(Exception::TypeError( - String::New("Second argument should be a buffer"))); - } - - Local buffer_obj = args[1]->ToObject(); - char *buffer_data = Buffer::Data(buffer_obj); - size_t buffer_length = Buffer::Length(buffer_obj); - - size_t off = args[2]->Int32Value(); - if (off >= buffer_length) { - return ThrowException(Exception::Error( - String::New("Offset is out of bounds"))); - } - - size_t len = args[3]->Int32Value(); - if (off + len > buffer_length) { - return ThrowException(Exception::Error( - String::New("Length is extends beyond buffer"))); - } - - struct iovec iov[1]; - iov[0].iov_base = (char*)buffer_data + off; - iov[0].iov_len = len; - - struct msghdr msg; - msg.msg_flags = 0; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_name = NULL; - msg.msg_namelen = 0; - /* Set up to receive a descriptor even if one isn't in the message */ - char cmsg_space[64]; // should be big enough - msg.msg_controllen = 64; - msg.msg_control = (void *) cmsg_space; - - ssize_t bytes_read = recvmsg(fd, &msg, 0); - - if (bytes_read < 0) { - if (errno == EAGAIN || errno == EINTR) return Null(); - return ThrowException(ErrnoException(errno, "recvMsg")); - } - - // Why not return a two element array here [bytesRead, fd]? Because - // creating an object for each recvmsg() action is heavy. Instead we just - // assign the recved fd to a globalally accessable variable (recvMsg.fd) - // that the wrapper can pick up. Since we're single threaded, this is not - // a problem - just make sure to copy out that variable before the next - // call to recvmsg(). - // - // XXX: Some implementations can send multiple file descriptors in a - // single message. We should be using CMSG_NXTHDR() to walk the - // chain to get at them all. This would require changing the - // API to hand these back up the caller, is a pain. - - int received_fd = -1; - for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); - msg.msg_controllen > 0 && cmsg != NULL; - cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_type == SCM_RIGHTS) { - if (received_fd != -1) { - fprintf(stderr, "ignoring extra FD received: %d\n", received_fd); - } - - received_fd = *(int *) CMSG_DATA(cmsg); - } else { - fprintf(stderr, "ignoring non-SCM_RIGHTS ancillary data: %d\n", - cmsg->cmsg_type - ); - } - } - - recv_msg_template->GetFunction()->Set( - fd_symbol, - (received_fd != -1) ? - Integer::New(received_fd) : - Null() - ); - - return scope.Close(Integer::New(bytes_read)); -} - -#endif // __POSIX__ - - -// var bytesWritten = t.write(fd, buffer, offset, length); -// returns null on EAGAIN or EINTR, raises an exception on all other errors -static Handle Write(const Arguments& args) { - HandleScope scope; - - if (args.Length() < 4) { - return ThrowException(Exception::TypeError( - String::New("Takes 4 parameters"))); - } - - FD_ARG(args[0]) - - if (!Buffer::HasInstance(args[1])) { - return ThrowException(Exception::TypeError( - String::New("Second argument should be a buffer"))); - } - - Local buffer_obj = args[1]->ToObject(); - char *buffer_data = Buffer::Data(buffer_obj); - size_t buffer_length = Buffer::Length(buffer_obj); - - size_t off = args[2]->Int32Value(); - if (off >= buffer_length) { - return ThrowException(Exception::Error( - String::New("Offset is out of bounds"))); - } - - size_t len = args[3]->Int32Value(); - if (off + len > buffer_length) { - return ThrowException(Exception::Error( - String::New("Length is extends beyond buffer"))); - } - -#ifdef __POSIX__ - ssize_t written = write(fd, buffer_data + off, len); - - if (written < 0) { - if (errno == EAGAIN || errno == EINTR) { - return scope.Close(Integer::New(0)); - } - return ThrowException(ErrnoException(errno, "write")); - } -#else // __MINGW32__ - // write() doesn't work for overlapped sockets (the only usable - // type of sockets) so send() is used. - ssize_t written = send(_get_osfhandle(fd), buffer_data + off, len, 0); - - if (written < 0) { - int wsaErrno = WSAGetLastError(); - if (wsaErrno == WSAEWOULDBLOCK || wsaErrno == WSAEINTR) { - return scope.Close(Integer::New(0)); - } - return ThrowException(ErrnoException(wsaErrno, "write")); - } -#endif // __MINGW32__ - - return scope.Close(Integer::New(written)); -} - - -#ifdef __POSIX__ - -// var bytes = sendmsg(fd, buf, off, len, fd, flags); -// -// Write a buffer with optional offset and length to the given file -// descriptor. Note that we refuse to send 0 bytes. -// -// The 'fd' parameter is a numerical file descriptor, or the undefined value -// to send none. -// -// The 'flags' parameter is a number representing a bitmask of MSG_* values. -// This is passed directly to sendmsg(). -// -// Returns null on EAGAIN or EINTR, raises an exception on all other errors -static Handle SendMsg(const Arguments& args) { - HandleScope scope; - - struct iovec iov; - - if (args.Length() < 2) { - return ThrowException(Exception::TypeError( - String::New("Takes 2 parameters"))); - } - - // The first argument should be a file descriptor - FD_ARG(args[0]) - - // Grab the actul data to be written, stuffing it into iov - if (!Buffer::HasInstance(args[1])) { - return ThrowException(Exception::TypeError( - String::New("Expected a buffer"))); - } - - Local buffer_obj = args[1]->ToObject(); - char *buffer_data = Buffer::Data(buffer_obj); - size_t buffer_length = Buffer::Length(buffer_obj); - - size_t offset = 0; - if (args.Length() >= 3 && !args[2]->IsUndefined()) { - if (!args[2]->IsUint32()) { - return ThrowException(Exception::TypeError( - String::New("Expected unsigned integer for offset"))); - } - - offset = args[2]->Uint32Value(); - if (offset >= buffer_length) { - return ThrowException(Exception::Error( - String::New("Offset into buffer too large"))); - } - } - - size_t length = buffer_length - offset; - if (args.Length() >= 4 && !args[3]->IsUndefined()) { - if (!args[3]->IsUint32()) { - return ThrowException(Exception::TypeError( - String::New("Expected unsigned integer for length"))); - } - - length = args[3]->Uint32Value(); - if (offset + length > buffer_length) { - return ThrowException(Exception::Error( - String::New("offset + length beyond buffer length"))); - } - } - - iov.iov_base = buffer_data + offset; - iov.iov_len = length; - - int fd_to_send = -1; - if (args.Length() >= 5 && !args[4]->IsUndefined()) { - if (!args[4]->IsUint32()) { - return ThrowException(Exception::TypeError( - String::New("Expected unsigned integer for a file descriptor"))); - } - - fd_to_send = args[4]->Uint32Value(); - } - - int flags = 0; - if (args.Length() >= 6 && !args[5]->IsUndefined()) { - if (!args[5]->IsUint32()) { - return ThrowException(Exception::TypeError( - String::New("Expected unsigned integer for a flags argument"))); - } - - flags = args[5]->Uint32Value(); - } - - struct msghdr msg; - char scratch[64]; - - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_flags = 0; - msg.msg_control = NULL; - msg.msg_controllen = 0; - - if (fd_to_send >= 0) { - struct cmsghdr *cmsg; - - msg.msg_control = (void *) scratch; - msg.msg_controllen = CMSG_LEN(sizeof(fd_to_send)); - - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = msg.msg_controllen; - *(int*) CMSG_DATA(cmsg) = fd_to_send; - } - - ssize_t written = sendmsg(fd, &msg, flags); - - if (written < 0) { - if (errno == EAGAIN || errno == EINTR) return Null(); - return ThrowException(ErrnoException(errno, "sendmsg")); - } - - /* Note that the FD isn't explicitly closed here, this - * happens in the JS */ - - return scope.Close(Integer::New(written)); -} - -#endif // __POSIX__ - - -// var bytes = sendto(fd, buf, off, len, flags, destination port, desitnation address); -// -// Write a buffer with optional offset and length to the given file -// descriptor. Note that we refuse to send 0 bytes. -// -// The 'fd' parameter is a numerical file descriptor, or the undefined value -// to send none. -// -// The 'flags' parameter is a number representing a bitmask of MSG_* values. -// This is passed directly to sendmsg(). -// -// The destination port can either be an int port, or a path. -// -// Returns null on EAGAIN or EINTR, raises an exception on all other errors -static Handle SendTo(const Arguments& args) { - HandleScope scope; - - if (args.Length() < 5) { - return ThrowException(Exception::TypeError( - String::New("Takes 5 or 6 parameters"))); - } - - // The first argument should be a file descriptor - FD_ARG(args[0]) - - // Grab the actul data to be written - if (!Buffer::HasInstance(args[1])) { - return ThrowException(Exception::TypeError( - String::New("Expected a buffer"))); - } - - Local buffer_obj = args[1]->ToObject(); - char *buffer_data = Buffer::Data(buffer_obj); - size_t buffer_length = Buffer::Length(buffer_obj); - - size_t offset = 0; - if (args.Length() >= 3 && !args[2]->IsUndefined()) { - if (!args[2]->IsUint32()) { - return ThrowException(Exception::TypeError( - String::New("Expected unsigned integer for offset"))); - } - - offset = args[2]->Uint32Value(); - if (offset >= buffer_length) { - return ThrowException(Exception::Error( - String::New("Offset into buffer too large"))); - } - } - - size_t length = buffer_length - offset; - if (args.Length() >= 4 && !args[3]->IsUndefined()) { - if (!args[3]->IsUint32()) { - return ThrowException(Exception::TypeError( - String::New("Expected unsigned integer for length"))); - } - - length = args[3]->Uint32Value(); - if (offset + length > buffer_length) { - return ThrowException(Exception::Error( - String::New("offset + length beyond buffer length"))); - } - } - - int flags = 0; - if (args.Length() >= 5 && !args[4]->IsUndefined()) { - if (!args[4]->IsUint32()) { - return ThrowException(Exception::TypeError( - String::New("Expected unsigned integer for a flags argument"))); - } - - flags = args[4]->Uint32Value(); - } - - Handle error = ParseAddressArgs(args[5], args[6], false); - if (!error.IsEmpty()) return ThrowException(error); - -#ifdef __POSIX__ - ssize_t written = sendto(fd, buffer_data + offset, length, flags, addr, - addrlen); - - if (written < 0) { - if (errno == EAGAIN || errno == EINTR) return Null(); - return ThrowException(ErrnoException(errno, "sendto")); - } - -#else // __MINGW32__ - ssize_t written = sendto(_get_osfhandle(fd), buffer_data + offset, length, - flags, addr, addrlen); - - if (written == SOCKET_ERROR) { - int wsaErrno = WSAGetLastError(); - if (wsaErrno == WSAEWOULDBLOCK || wsaErrno == WSAEINTR) return Null(); - return ThrowException(ErrnoException(wsaErrno, "sendto")); - } -#endif // __MINGW32__ - - /* Note that the FD isn't explicitly closed here, this - * happens in the JS */ - - return scope.Close(Integer::New(written)); -} - - -// Probably only works for Linux TCP sockets? -// Returns the amount of data on the read queue. -static Handle ToRead(const Arguments& args) { - HandleScope scope; - - FD_ARG(args[0]) - -#ifdef __POSIX__ - int value; - - if (0 > ioctl(fd, FIONREAD, &value)) { - return ThrowException(ErrnoException(errno, "ioctl")); - } - -#else // __MINGW32__ - unsigned long value; - - if (SOCKET_ERROR == ioctlsocket(_get_osfhandle(fd), FIONREAD, &value)) { - return ThrowException(ErrnoException(WSAGetLastError(), "ioctlsocket")); - } -#endif // __MINGW32__ - - return scope.Close(Integer::New(value)); -} - - -static Handle SetNoDelay(const Arguments& args) { - HandleScope scope; - - FD_ARG(args[0]) - -#ifdef __POSIX__ - int flags = args[1]->IsFalse() ? 0 : 1; - - if (0 > setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, - sizeof(flags))) { - return ThrowException(ErrnoException(errno, "setsockopt")); - } - -#else // __MINGW32__ - BOOL flags = args[1]->IsFalse() ? FALSE : TRUE; - - if (SOCKET_ERROR == setsockopt(_get_osfhandle(fd), IPPROTO_TCP, TCP_NODELAY, - (const char *)&flags, sizeof(flags))) { - return ThrowException(ErrnoException(WSAGetLastError(), "setsockopt")); - } -#endif // __MINGW32__ - - return Undefined(); -} - - -static Handle SetKeepAlive(const Arguments& args) { - int r; - HandleScope scope; - - bool enable = false; - int time = 0; - - FD_ARG(args[0]) - - if (args.Length() > 0) enable = args[1]->IsTrue(); - if (enable == true) { - time = args[2]->Int32Value(); - } - -#ifdef __POSIX__ - int flags = enable ? 1 : 0; - - r = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)); - if ((time > 0)&&(r >= 0)) { -#if defined(__APPLE__) - r = setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, (void *)&time, sizeof(time)); -#elif defined(__linux__) - r = setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&time, sizeof(time)); -#else - // Solaris nor FreeBSD support TCP_KEEPIDLE, so do nothing here. -#endif - } - if (r < 0) { - return ThrowException(ErrnoException(errno, "setsockopt")); - } - -#else // __MINGW32__ - SOCKET handle = (SOCKET)_get_osfhandle(fd); - BOOL flags = enable ? TRUE : FALSE; - - r = setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, (const char *)&flags, - sizeof(flags)); - // Could set the timeout here, using WSAIoctl(SIO_KEEPALIVE_VALS), - // but ryah thinks it is not necessary, and mingw is missing mstcpip.h anyway. - if (r == SOCKET_ERROR) { - return ThrowException(ErrnoException(WSAGetLastError(), "setsockopt")); - } -#endif - - return Undefined(); -} - - -static Handle SetBroadcast(const Arguments& args) { - HandleScope scope; - - FD_ARG(args[0]) - -#ifdef __POSIX__ - int flags = args[1]->IsFalse() ? 0 : 1; - - if (0 > setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (void *)&flags, - sizeof(flags))) { - return ThrowException(ErrnoException(errno, "setsockopt")); - } - -#else // __MINGW32__ - BOOL flags = args[1]->IsFalse() ? FALSE : TRUE; - - if (SOCKET_ERROR == setsockopt(_get_osfhandle(fd), SOL_SOCKET, SO_BROADCAST, - (const char *)&flags, sizeof(flags))) { - return ThrowException(ErrnoException(WSAGetLastError(), "setsockopt")); - } -#endif - - return Undefined(); -} - -static Handle SetTTL(const Arguments& args) { - HandleScope scope; - -#ifdef __POSIX__ - int newttl; -#else // __MINGW32__ - DWORD newttl; -#endif - - if (args.Length() != 2) { - return ThrowException(Exception::TypeError( - String::New("Takes exactly two arguments: fd, new TTL"))); - } - - FD_ARG(args[0]); - - if (!args[1]->IsInt32()) { - return ThrowException(Exception::TypeError( - String::New("Argument must be a number"))); - } - - newttl = args[1]->Int32Value(); - - if (newttl < 1 || newttl > 255) { - return ThrowException(Exception::TypeError( - String::New("new TTL must be between 1 and 255"))); - } - -#ifdef __POSIX__ - int r = setsockopt(fd, - IPPROTO_IP, - IP_TTL, - reinterpret_cast(&newttl), - sizeof(newttl)); - if (r < 0) { - return ThrowException(ErrnoException(errno, "setsockopt")); - } - -#else // __MINGW32__ - if (SOCKET_ERROR > setsockopt(_get_osfhandle(fd), IPPROTO_IP, IP_TTL, - (const char *)&newttl, sizeof(newttl))) { - return ThrowException(ErrnoException(WSAGetLastError(), "setsockopt")); - } - -#endif // __MINGW32__ - - return scope.Close(Integer::New(newttl)); -} - - -#ifdef __POSIX__ - -static Handle SetMulticastTTL(const Arguments& args) { - HandleScope scope; - - if (args.Length() != 2) { - return ThrowException(Exception::TypeError( - String::New("Takes exactly two arguments: fd, new MulticastTTL"))); - } - - FD_ARG(args[0]); - - if (!args[1]->IsInt32()) { - return ThrowException(Exception::TypeError( - String::New("Argument must be a number"))); - } - - int value = args[1]->Int32Value(); - if (value < 0 || value > 255) { - return ThrowException(Exception::TypeError( - String::New("new MulticastTTL must be between 0 and 255"))); - } - -#ifdef __sun - unsigned char newttl = (unsigned char) value; -#else - int newttl = value; -#endif - - int r = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, - reinterpret_cast(&newttl), sizeof(newttl)); - - if (r < 0) { - return ThrowException(ErrnoException(errno, "setsockopt")); - } else { - return scope.Close(Integer::New(newttl)); - } -} - -static Handle SetMulticastLoopback(const Arguments& args) { -#ifdef __sun - unsigned char flags; -#else - int flags; -#endif - int r; - HandleScope scope; - - FD_ARG(args[0]) - - flags = args[1]->IsFalse() ? 0 : 1; - r = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, - reinterpret_cast(&flags), sizeof(flags)); - - if (r < 0) { - return ThrowException(ErrnoException(errno, "setsockopt")); - } else { - return scope.Close(Integer::New(flags)); - } -} - -static Handle SetMembership(const Arguments& args, int socketOption) { - HandleScope scope; - - if (args.Length() < 2 || args.Length() > 3) { - return ThrowException(Exception::TypeError( - String::New("Takes arguments: fd, multicast group, multicast address"))); - } - - FD_ARG(args[0]); - - struct ip_mreq mreq; - memset(&mreq, 0, sizeof(mreq)); - - // Multicast address (arg[1]) - String::Utf8Value multicast_address(args[1]->ToString()); - if (inet_pton( - AF_INET, *multicast_address, &(mreq.imr_multiaddr.s_addr)) <= 0) { - return ErrnoException(errno, "inet_pton", "Invalid multicast address"); - } - - // Interface address (arg[2] - optional, default:INADDR_ANY) - if (args.Length() < 3 || !args[2]->IsString()) { - mreq.imr_interface.s_addr = htonl(INADDR_ANY); - } else { - String::Utf8Value multicast_interface(args[2]->ToString()); - if (inet_pton( - AF_INET, *multicast_interface, &(mreq.imr_interface.s_addr)) <= 0) { - return ErrnoException(errno, "inet_pton", "Invalid multicast interface"); - } - } - - int r = setsockopt(fd, IPPROTO_IP, socketOption, - reinterpret_cast(&mreq), sizeof(mreq)); - - if (r < 0) { - return ThrowException(ErrnoException(errno, "setsockopt")); - } else { - return Undefined(); - } -} - -static Handle AddMembership(const Arguments& args) { - return SetMembership(args, IP_ADD_MEMBERSHIP); -} - -static Handle DropMembership(const Arguments& args) { - return SetMembership(args, IP_DROP_MEMBERSHIP); -} - -#endif // __POSIX__ - - -// -// G E T A D D R I N F O -// - - -struct resolve_request { - Persistent cb; - struct addrinfo *address_list; - int ai_family; // AF_INET or AF_INET6 - char hostname[1]; -}; - -#ifndef EAI_NODATA // EAI_NODATA is deprecated, FreeBSD already thrown it away in favor of EAI_NONAME -#define EAI_NODATA EAI_NONAME -#endif - -static int AfterResolve(eio_req *req) { - ev_unref(EV_DEFAULT_UC); - - struct resolve_request * rreq = (struct resolve_request *)(req->data); - - HandleScope scope; - Local argv[2]; - - if (req->result != 0) { - argv[1] = Array::New(); - if (req->result == EAI_NODATA) { - argv[0] = Local::New(Null()); - } else { - argv[0] = ErrnoException(req->result, - "getaddrinfo", - gai_strerror(req->result)); - } - } else { - struct addrinfo *address; - int n = 0; - - for (address = rreq->address_list; address; address = address->ai_next) { n++; } - - Local results = Array::New(n); - - char ip[INET6_ADDRSTRLEN]; - const char *addr; - - n = 0; - address = rreq->address_list; - while (address) { - assert(address->ai_socktype == SOCK_STREAM); - assert(address->ai_family == AF_INET || address->ai_family == AF_INET6); - addr = ( address->ai_family == AF_INET - ? (char *) &((struct sockaddr_in *) address->ai_addr)->sin_addr - : (char *) &((struct sockaddr_in6 *) address->ai_addr)->sin6_addr - ); - const char *c = inet_ntop(address->ai_family, addr, ip, INET6_ADDRSTRLEN); - Local s = String::New(c); - results->Set(Integer::New(n), s); - - n++; - address = address->ai_next; - } - - argv[0] = Local::New(Null()); - argv[1] = results; - } - - TryCatch try_catch; - - rreq->cb->Call(Context::GetCurrent()->Global(), 2, argv); - - if (try_catch.HasCaught()) { - FatalException(try_catch); - } - - if (rreq->address_list) freeaddrinfo(rreq->address_list); - rreq->cb.Dispose(); // Dispose of the persistent handle - free(rreq); - - return 0; -} - - -static void Resolve(eio_req *req) { - // Note: this function is executed in the thread pool! CAREFUL - struct resolve_request * rreq = (struct resolve_request *) req->data; - - struct addrinfo hints; - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = rreq->ai_family; - hints.ai_socktype = SOCK_STREAM; - - req->result = getaddrinfo((char*)rreq->hostname, - NULL, - &hints, - &(rreq->address_list)); -} - - -static Handle GetAddrInfo(const Arguments& args) { - HandleScope scope; - - String::Utf8Value hostname(args[0]->ToString()); - - int type = args[1]->Int32Value(); - int fam = AF_INET; - switch (type) { - case 4: - fam = AF_INET; - break; - case 6: - fam = AF_INET6; - break; - default: - return ThrowException(Exception::TypeError( - String::New("Second argument must be an integer 4 or 6"))); - } - - if (!args[2]->IsFunction()) { - return ThrowException(Exception::TypeError( - String::New("Thrid argument must be a callback"))); - } - - Local cb = Local::Cast(args[2]); - - struct resolve_request *rreq = (struct resolve_request *) - calloc(1, sizeof(struct resolve_request) + hostname.length() + 1); - - if (!rreq) { - V8::LowMemoryNotification(); - return ThrowException(Exception::Error( - String::New("Could not allocate enough memory"))); - } - - strncpy(rreq->hostname, *hostname, hostname.length() + 1); - rreq->cb = Persistent::New(cb); - rreq->ai_family = fam; - - // For the moment I will do DNS lookups in the eio thread pool. This is - // sub-optimal and cannot handle massive numbers of requests. - // - // (One particularly annoying problem is that the pthread stack size needs - // to be increased dramatically to handle getaddrinfo() see X_STACKSIZE in - // wscript ). - // - // In the future I will move to a system using c-ares: - // http://lists.schmorp.de/pipermail/libev/2009q1/000632.html - eio_custom(Resolve, EIO_PRI_DEFAULT, AfterResolve, rreq); - - // There will not be any active watchers from this object on the event - // loop while getaddrinfo() runs. If the only thing happening in the - // script was this hostname resolution, then the event loop would drop - // out. Thus we need to add ev_ref() until AfterResolve(). - ev_ref(EV_DEFAULT_UC); - - return Undefined(); -} - - -static Handle IsIP(const Arguments& args) { - HandleScope scope; - - if (!args[0]->IsString()) { - return scope.Close(Integer::New(4)); - } - - String::Utf8Value s(args[0]->ToString()); - - // avoiding buffer overflows in the following strcat - // 2001:0db8:85a3:08d3:1319:8a2e:0370:7334 - // 39 = max ipv6 address. - if (s.length() > INET6_ADDRSTRLEN) { - return scope.Close(Integer::New(0)); - } - - struct sockaddr_in6 a; - - if (inet_pton(AF_INET, *s, &(a.sin6_addr)) > 0) return scope.Close(Integer::New(4)); - if (inet_pton(AF_INET6, *s, &(a.sin6_addr)) > 0) return scope.Close(Integer::New(6)); - - return scope.Close(Integer::New(0)); -} - - -static Handle CreateErrnoException(const Arguments& args) { - HandleScope scope; - - int errorno = args[0]->Int32Value(); - String::Utf8Value syscall(args[1]->ToString()); - - Local exception = ErrnoException(errorno, *syscall); - - return scope.Close(exception); -} - - -void InitNet(Handle target) { - HandleScope scope; - - NODE_SET_METHOD(target, "write", Write); - NODE_SET_METHOD(target, "read", Read); - NODE_SET_METHOD(target, "sendto", SendTo); - NODE_SET_METHOD(target, "recvfrom", RecvFrom); - -#ifdef __POSIX__ - NODE_SET_METHOD(target, "sendMsg", SendMsg); - - recv_msg_template = - Persistent::New(FunctionTemplate::New(RecvMsg)); - target->Set(String::NewSymbol("recvMsg"), recv_msg_template->GetFunction()); -#endif //__POSIX__ - - NODE_SET_METHOD(target, "socket", Socket); - NODE_SET_METHOD(target, "close", Close); - NODE_SET_METHOD(target, "shutdown", Shutdown); - -#ifdef __POSIX__ - NODE_SET_METHOD(target, "pipe", Pipe); - NODE_SET_METHOD(target, "socketpair", SocketPair); -#endif // __POSIX__ - - NODE_SET_METHOD(target, "connect", Connect); - NODE_SET_METHOD(target, "bind", Bind); - NODE_SET_METHOD(target, "listen", Listen); - NODE_SET_METHOD(target, "accept", Accept); - NODE_SET_METHOD(target, "socketError", SocketError); - NODE_SET_METHOD(target, "toRead", ToRead); - NODE_SET_METHOD(target, "setNoDelay", SetNoDelay); - NODE_SET_METHOD(target, "setBroadcast", SetBroadcast); - NODE_SET_METHOD(target, "setTTL", SetTTL); - NODE_SET_METHOD(target, "setKeepAlive", SetKeepAlive); -#ifdef __POSIX__ - NODE_SET_METHOD(target, "setMulticastTTL", SetMulticastTTL); - NODE_SET_METHOD(target, "setMulticastLoopback", SetMulticastLoopback); - NODE_SET_METHOD(target, "addMembership", AddMembership); - NODE_SET_METHOD(target, "dropMembership", DropMembership); -#endif // __POSIX__ - NODE_SET_METHOD(target, "getsockname", GetSockName); - NODE_SET_METHOD(target, "getpeername", GetPeerName); - NODE_SET_METHOD(target, "getaddrinfo", GetAddrInfo); - NODE_SET_METHOD(target, "isIP", IsIP); - NODE_SET_METHOD(target, "errnoException", CreateErrnoException); - - errno_symbol = NODE_PSYMBOL("errno"); - syscall_symbol = NODE_PSYMBOL("syscall"); - fd_symbol = NODE_PSYMBOL("fd"); - size_symbol = NODE_PSYMBOL("size"); - address_symbol = NODE_PSYMBOL("address"); - port_symbol = NODE_PSYMBOL("port"); -} - -} // namespace node - -NODE_MODULE(node_net, node::InitNet); diff --git a/src/node_net.h b/src/node_net.h deleted file mode 100644 index b57b4967aa..0000000000 --- a/src/node_net.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -#ifndef NODE_NET -#define NODE_NET - -#include - -namespace node { - -void InitNet(v8::Handle target); - -} - -#endif // NODE_NET diff --git a/src/node_timer.cc b/src/node_timer.cc deleted file mode 100644 index 50902895e7..0000000000 --- a/src/node_timer.cc +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -#include -#include -#include - -namespace node { - -using namespace v8; - -Persistent Timer::constructor_template; - - -static Persistent timeout_symbol; -static Persistent repeat_symbol; -static Persistent callback_symbol; - - -void Timer::Initialize(Handle target) { - HandleScope scope; - - Local t = FunctionTemplate::New(Timer::New); - constructor_template = Persistent::New(t); - constructor_template->InstanceTemplate()->SetInternalFieldCount(1); - constructor_template->SetClassName(String::NewSymbol("Timer")); - - timeout_symbol = NODE_PSYMBOL("timeout"); - repeat_symbol = NODE_PSYMBOL("repeat"); - callback_symbol = NODE_PSYMBOL("callback"); - - NODE_SET_PROTOTYPE_METHOD(constructor_template, "start", Timer::Start); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "stop", Timer::Stop); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "again", Timer::Again); - - constructor_template->InstanceTemplate()->SetAccessor(repeat_symbol, - RepeatGetter, RepeatSetter); - - target->Set(String::NewSymbol("Timer"), constructor_template->GetFunction()); -} - - -Handle Timer::RepeatGetter(Local property, - const AccessorInfo& info) { - HandleScope scope; - Timer *timer = ObjectWrap::Unwrap(info.This()); - - assert(timer); - assert(property == repeat_symbol); - - Local v = Integer::New(timer->watcher_.repeat); - - return scope.Close(v); -} - -void Timer::RepeatSetter(Local property, - Local value, - const AccessorInfo& info) { - HandleScope scope; - Timer *timer = ObjectWrap::Unwrap(info.This()); - - assert(timer); - assert(property == repeat_symbol); - - timer->watcher_.repeat = NODE_V8_UNIXTIME(value); -} - -void Timer::OnTimeout(EV_P_ ev_timer *watcher, int revents) { - Timer *timer = static_cast(watcher->data); - - assert(revents == EV_TIMEOUT); - - HandleScope scope; - - Local callback_v = timer->handle_->Get(callback_symbol); - if (!callback_v->IsFunction()) { - timer->Stop(); - return; - } - - Local callback = Local::Cast(callback_v); - - TryCatch try_catch; - - callback->Call(timer->handle_, 0, NULL); - - if (try_catch.HasCaught()) { - FatalException(try_catch); - } - - if (timer->watcher_.repeat == 0) timer->Unref(); -} - - -Timer::~Timer() { - ev_timer_stop(EV_DEFAULT_UC_ &watcher_); -} - - -Handle Timer::New(const Arguments& args) { - if (!args.IsConstructCall()) { - return FromConstructorTemplate(constructor_template, args); - } - - HandleScope scope; - - Timer *t = new Timer(); - t->Wrap(args.Holder()); - - return args.This(); -} - -Handle Timer::Start(const Arguments& args) { - HandleScope scope; - Timer *timer = ObjectWrap::Unwrap(args.Holder()); - - if (args.Length() != 2) - return ThrowException(String::New("Bad arguments")); - - bool was_active = ev_is_active(&timer->watcher_); - - ev_tstamp after = NODE_V8_UNIXTIME(args[0]); - ev_tstamp repeat = NODE_V8_UNIXTIME(args[1]); - ev_timer_init(&timer->watcher_, Timer::OnTimeout, after, repeat); - timer->watcher_.data = timer; - - // Update the event loop time. Need to call this because processing JS can - // take non-negligible amounts of time. - ev_now_update(EV_DEFAULT_UC); - - ev_timer_start(EV_DEFAULT_UC_ &timer->watcher_); - - if (!was_active) timer->Ref(); - - return Undefined(); -} - - -Handle Timer::Stop(const Arguments& args) { - HandleScope scope; - Timer *timer = ObjectWrap::Unwrap(args.Holder()); - timer->Stop(); - return Undefined(); -} - - -void Timer::Stop() { - if (watcher_.active) { - ev_timer_stop(EV_DEFAULT_UC_ &watcher_); - Unref(); - } -} - - -Handle Timer::Again(const Arguments& args) { - HandleScope scope; - Timer *timer = ObjectWrap::Unwrap(args.Holder()); - - int was_active = ev_is_active(&timer->watcher_); - - if (args.Length() > 0) { - ev_tstamp repeat = NODE_V8_UNIXTIME(args[0]); - if (repeat > 0) timer->watcher_.repeat = repeat; - } - - ev_timer_again(EV_DEFAULT_UC_ &timer->watcher_); - - // ev_timer_again can start or stop the watcher. - // So we need to check what happened and adjust the ref count - // appropriately. - - if (ev_is_active(&timer->watcher_)) { - if (!was_active) timer->Ref(); - } else { - if (was_active) timer->Unref(); - } - - return Undefined(); -} - - -} // namespace node diff --git a/src/node_timer.h b/src/node_timer.h deleted file mode 100644 index 264e1ded13..0000000000 --- a/src/node_timer.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -#ifndef SRC_NODE_TIMER_H_ -#define SRC_NODE_TIMER_H_ - -#include -#include -#include -#include - -namespace node { - -class Timer : ObjectWrap { - public: - static void Initialize(v8::Handle target); - - protected: - static v8::Persistent constructor_template; - - Timer() : ObjectWrap() { - // dummy timeout values - ev_timer_init(&watcher_, OnTimeout, 0., 1.); - watcher_.data = this; - } - - ~Timer(); - - static v8::Handle New(const v8::Arguments& args); - static v8::Handle Start(const v8::Arguments& args); - static v8::Handle Stop(const v8::Arguments& args); - static v8::Handle Again(const v8::Arguments& args); - static v8::Handle RepeatGetter(v8::Local property, - const v8::AccessorInfo& info); - static void RepeatSetter(v8::Local property, - v8::Local value, - const v8::AccessorInfo& info); - - private: - static void OnTimeout(EV_P_ ev_timer *watcher, int revents); - void Stop(); - ev_timer watcher_; -}; - -} // namespace node -#endif // SRC_NODE_TIMER_H_ diff --git a/wscript b/wscript index 8528d8aaea..e1ef0d0181 100644 --- a/wscript +++ b/wscript @@ -903,13 +903,11 @@ def build(bld): node.source += " src/node_stdio_win32.cc " else: node.source += " src/node_cares.cc " - node.source += " src/node_net.cc " node.source += " src/node_signal_watcher.cc " node.source += " src/node_stat_watcher.cc " node.source += " src/node_io_watcher.cc " node.source += " src/node_stdio.cc " node.source += " src/node_child_process.cc " - node.source += " src/node_timer.cc " node.source += bld.env["PLATFORM_FILE"] if not product_type_is_lib: