Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Remove net_legacy timers_legacy

  • Loading branch information...
commit be0bb2dc136ca20b44da81cded790417cbd1cfd2 1 parent d2b8037
@ry ry authored
View
0  lib/net_uv.js → lib/net.js
File renamed without changes
View
1,215 lib/net_legacy.js
@@ -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;
- }
- }
-}
View
0  lib/timers_uv.js → lib/timers.js
File renamed without changes
View
224 lib/timers_legacy.js
@@ -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();
- }
-};
View
9 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"', {
View
9 src/node.cc
@@ -77,14 +77,12 @@ extern "C" {
#ifdef __POSIX__
# include <node_io_watcher.h>
#endif
-#include <node_net.h>
#include <node_cares.h>
#include <node_file.h>
#include <node_http_parser.h>
#ifdef __POSIX__
# include <node_signal_watcher.h>
# include <node_stat_watcher.h>
-# include <node_timer.h>
#endif
#if !defined(_MSC_VER)
#include <node_child_process.h>
@@ -1902,13 +1900,6 @@ static Handle<Value> 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);
View
6 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';
View
3  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)
View
1,781 src/node_net.cc
@@ -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 <node.h>
-#include <node_buffer.h>
-#include <node_net.h>
-
-#include <v8.h>
-
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include <sys/types.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#ifdef __MINGW32__
-# include <platform_win32.h>
-#endif
-
-#ifdef __POSIX__
-# include <sys/ioctl.h>
-# include <sys/socket.h>
-# include <sys/un.h>
-# include <arpa/inet.h> /* inet_pton */
-# include <netdb.h>
-# include <netinet/in.h>
-# include <netinet/tcp.h>
-#endif
-
-#ifdef __linux__
-# include <linux/sockios.h> /* For the SIOCINQ / FIONREAD ioctl */
-#endif
-
-/* Non-linux platforms like OS X define this ioctl elsewhere */
-#ifndef FIONREAD
-# include <sys/filio.h>
-#endif
-
-#ifdef __OpenBSD__
-# include <sys/uio.h>
-#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 <inet_net_pton.h>
-# include <inet_ntop.h>
- }
-
-# 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<String> errno_symbol;
-static Persistent<String> syscall_symbol;
-
-static Persistent<String> fd_symbol;
-static Persistent<String> size_symbol;
-static Persistent<String> address_symbol;
-static Persistent<String> port_symbol;
-static Persistent<String> type_symbol;
-static Persistent<String> tcp_symbol;
-static Persistent<String> unix_symbol;
-
-static Persistent<FunctionTemplate> 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<HANDLE>(_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<Value> 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<Array> 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<Value> 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<Array> 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<Value> 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<Value> ParseAddressArgs(Handle<Value> first,
- Handle<Value> 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<Value>();
-}
-
-
-// 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<Value> 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<Value> 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<Value> 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<Value> 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<Value> 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<Value> 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<Value> 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<Object> info = Object::New();
- ADDRESS_TO_JS(info, address_storage, len);
- return scope.Close(info);
-}
-
-
-static Handle<Value> 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<Object> info = Object::New();
- ADDRESS_TO_JS(info, address_storage, len);
- return scope.Close(info);
-}
-
-
-static Handle<Value> 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<Value> 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<Object> 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<Value> 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<Value> 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<Object> 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<Value> 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<Object> 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<Object> 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<Value> 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<Object> 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
- );
- }
- }