mirror of https://github.com/lukechilds/node.git
Ryan Dahl
14 years ago
1 changed files with 325 additions and 0 deletions
@ -0,0 +1,325 @@ |
|||||
|
var events = require('events'); |
||||
|
var stream = require('stream'); |
||||
|
var timers = require('timers'); |
||||
|
var util = require('util'); |
||||
|
var assert = require('assert'); |
||||
|
var TCP = process.binding('tcp_wrap').TCP; |
||||
|
|
||||
|
|
||||
|
var debug; |
||||
|
if (process.env.NODE_DEBUG && /net/.test(process.env.NODE_DEBUG)) { |
||||
|
debug = function(x) { console.error('NET:', x); }; |
||||
|
} else { |
||||
|
debug = function() { }; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function Socket(options) { |
||||
|
if (!(this instanceof Socket)) return new Socket(options); |
||||
|
|
||||
|
stream.Stream.call(this); |
||||
|
|
||||
|
// private
|
||||
|
if (options.handle) { |
||||
|
this._handle = options.handle; |
||||
|
} else { |
||||
|
this._handle = new TCP(); |
||||
|
} |
||||
|
this._handle.socket = this; |
||||
|
this._handle.onread = onread; |
||||
|
|
||||
|
this._writeRequests = []; |
||||
|
} |
||||
|
util.inherits(Socket, stream.Stream); |
||||
|
|
||||
|
|
||||
|
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() { |
||||
|
this._handle.readStop(); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
Socket.prototype.resume = function() { |
||||
|
this._handle.readStart(); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
Socket.prototype.end = function() { |
||||
|
throw new Error("implement me"); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
Socket.prototype.destroySoon = function() { |
||||
|
throw new Error("implement me"); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
Socket.prototype.destroy = function(exception) { |
||||
|
var self = this; |
||||
|
|
||||
|
debug('destroy ' + this.fd); |
||||
|
|
||||
|
this.readable = this.writable = false; |
||||
|
|
||||
|
timers.unenroll(this); |
||||
|
|
||||
|
if (this.server && !this.destroyed) { |
||||
|
this.server.connections--; |
||||
|
} |
||||
|
|
||||
|
debug('close ' + this.fd); |
||||
|
this._handle.close(); |
||||
|
|
||||
|
process.nextTick(function() { |
||||
|
if (exception) self.emit('error', exception); |
||||
|
self.emit('close', exception ? true : false); |
||||
|
}); |
||||
|
|
||||
|
this.destroyed = true; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
function onread(buffer, offset, length) { |
||||
|
var handle = this; |
||||
|
var self = handle.socket; |
||||
|
assert.equal(handle, self._handle); |
||||
|
|
||||
|
timers.active(self); |
||||
|
|
||||
|
var end = offset + length; |
||||
|
|
||||
|
// Emit 'data' event.
|
||||
|
|
||||
|
if (self._decoder) { |
||||
|
// Emit a string.
|
||||
|
var string = self._decoder.write(buffer.slice(offset, end)); |
||||
|
if (string.length) self.emit('data', string); |
||||
|
} else { |
||||
|
// Emit a slice. Attempt to avoid slicing the buffer if no one is
|
||||
|
// listening for 'data'.
|
||||
|
if (self._events && self._events['data']) { |
||||
|
self.emit('data', buffer.slice(offset, end)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Optimization: emit the original buffer with end points
|
||||
|
if (self.ondata) self.ondata(buffer, offset, end); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
Socket.prototype.setEncoding = function(encoding) { |
||||
|
var StringDecoder = require('string_decoder').StringDecoder; // lazy load
|
||||
|
this._decoder = new StringDecoder(encoding); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
Socket.prototype.write = function(data /* [encoding], [fd], [cb] */) { |
||||
|
var encoding, fd, cb; |
||||
|
|
||||
|
// 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]; |
||||
|
} |
||||
|
|
||||
|
// Change strings to buffers. SLOW
|
||||
|
if (typeof data == 'string') { |
||||
|
data = new Buffer(data, encoding); |
||||
|
} |
||||
|
|
||||
|
var writeReq = this._handle.write(data); |
||||
|
writeReq.oncomplete = afterWrite; |
||||
|
writeReq.cb = cb; |
||||
|
this._writeRequests.push(writeReq); |
||||
|
|
||||
|
return this._handle.writeQueueSize == 0; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
function afterWrite(status, handle, req, buffer) { |
||||
|
var self = handle.socket; |
||||
|
|
||||
|
// TODO check status.
|
||||
|
|
||||
|
var req_ = self._writeRequests.shift(); |
||||
|
assert.equal(req, req_); |
||||
|
|
||||
|
if (req.cb) req.cb(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
Socket.prototype.connect = function(port, host) { |
||||
|
var self = this; |
||||
|
|
||||
|
timers.active(this); |
||||
|
|
||||
|
require('dns').lookup(host, function(err, ip, addressType) { |
||||
|
if (err) { |
||||
|
self.emit('error', err); |
||||
|
} else { |
||||
|
timers.active(self); |
||||
|
|
||||
|
if (addressType != 4) { |
||||
|
throw new Error("ipv6 addresses not yet supported by libuv"); |
||||
|
} |
||||
|
|
||||
|
self.remoteAddress = ip; |
||||
|
self.remotePort = port; |
||||
|
|
||||
|
// TODO retrun promise from Socket.prototype.connect which
|
||||
|
// wraps _connectReq.
|
||||
|
|
||||
|
assert.ok(!self._connectReq); |
||||
|
|
||||
|
self._connectReq = self._handle.connect(ip, port); |
||||
|
|
||||
|
if (!self._connectReq) { |
||||
|
self.destroy(errnoException(errno, 'connect')); |
||||
|
} |
||||
|
|
||||
|
self._connectReq.oncomplete = afterConnect; |
||||
|
} |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
function afterConnect(status, handle, req) { |
||||
|
var self = handle.socket; |
||||
|
assert.equal(handle, self._handle); |
||||
|
|
||||
|
if (status == 0) { |
||||
|
self.readable = self.writable = true; |
||||
|
timers.active(self); |
||||
|
handle.readStart(); |
||||
|
self.emit('connect'); |
||||
|
} else { |
||||
|
self.destroy(errnoException(errno, 'connect')); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function errnoException(errorno, syscall) { |
||||
|
// TODO make this more compatible with ErrnoException from src/node.cc
|
||||
|
// Once all of Node is using this function the ErrnoException from
|
||||
|
// src/node.cc should be removed.
|
||||
|
var e = new Error(syscall + ' ' + errorno); |
||||
|
e.errno = errorno; |
||||
|
e.syscall = syscall; |
||||
|
return e; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
function Server(/* [ options, ] listener */) { |
||||
|
if (!(this instanceof Server)) return new Server(arguments[0], arguments[1]); |
||||
|
events.EventEmitter.call(this); |
||||
|
|
||||
|
var self = this; |
||||
|
|
||||
|
var options, listenerCallback; |
||||
|
|
||||
|
if (typeof arguments[0] == 'function') { |
||||
|
options = {}; |
||||
|
listenerCallback = arguments[0]; |
||||
|
} else { |
||||
|
options = arguments[0]; |
||||
|
|
||||
|
if (typeof arguments[1] == 'function') { |
||||
|
listenerCallback = arguments[1]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.on('connection', listenerCallback); |
||||
|
this.connections = 0; |
||||
|
self.allowHalfOpen = options.allowHalfOpen || false; |
||||
|
|
||||
|
|
||||
|
this._handle = new TCP(); |
||||
|
this._handle.socket = this; |
||||
|
this._handle.onconnection = onconnection; |
||||
|
} |
||||
|
util.inherits(Server, events.EventEmitter); |
||||
|
exports.Server = Server; |
||||
|
|
||||
|
|
||||
|
function toPort(x) { return (x = Number(x)) >= 0 ? x : false; } |
||||
|
|
||||
|
|
||||
|
Server.prototype.listen = function() { |
||||
|
var self = this; |
||||
|
|
||||
|
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()
|
||||
|
this._handle.listen(self._backlog || 128); |
||||
|
this.emit('listening'); |
||||
|
} 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 { |
||||
|
if (addressType != 4) { |
||||
|
throw new Error("ipv6 addresses not yet supported by libuv"); |
||||
|
} |
||||
|
|
||||
|
var r = self._handle.bind(ip || '0.0.0.0', port); |
||||
|
if (r) { |
||||
|
self.emit('error', errnoException(errno, 'listen')); |
||||
|
} else { |
||||
|
self._handle.listen(self._backlog || 128); |
||||
|
self.emit('listening'); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
function onconnection(clientHandle) { |
||||
|
var handle = this; |
||||
|
var self = handle.socket; |
||||
|
|
||||
|
var socket = new Socket({ handle: clientHandle }); |
||||
|
socket.resume(); |
||||
|
|
||||
|
DTRACE_NET_SERVER_CONNECTION(socket); |
||||
|
self.emit('connection', socket); |
||||
|
} |
||||
|
|
Loading…
Reference in new issue