@ -20,7 +20,7 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var events = require ( 'events' ) ;
var S tream = require ( 'stream' ) ;
var s tream = require ( 'stream' ) ;
var timers = require ( 'timers' ) ;
var util = require ( 'util' ) ;
var assert = require ( 'assert' ) ;
@ -42,16 +42,16 @@ function createTCP() {
}
/* Bit flags for socket._flags */
var FLAG_GOT_EOF = 1 << 0 ;
var FLAG_SHUTDOWN = 1 << 1 ;
var FLAG_DESTROY_SOON = 1 << 2 ;
var FLAG_SHUTDOWN_QUEUED = 1 << 3 ;
var debug ;
if ( process . env . NODE_DEBUG && /net/ . test ( process . env . NODE_DEBUG ) ) {
debug = function ( x ) { console . error ( 'NET:' , x ) ; } ;
var pid = process . pid ;
debug = function ( x ) {
// if console is not set up yet, then skip this.
if ( ! console . error )
return ;
console . error ( 'NET: %d' , pid ,
util . format . apply ( util , arguments ) . slice ( 0 , 500 ) ) ;
} ;
} else {
debug = function ( ) { } ;
}
@ -110,12 +110,8 @@ function normalizeConnectArgs(args) {
exports . _ normalizeConnectArgs = normalizeConnectArgs ;
/* called when creating new Socket, or when re-using a closed Socket */
// called when creating new Socket, or when re-using a closed Socket
function initSocketHandle ( self ) {
self . _ pendingWriteReqs = 0 ;
self . _ flags = 0 ;
self . _ connectQueueSize = 0 ;
self . destroyed = false ;
self . errorEmitted = false ;
self . bytesRead = 0 ;
@ -131,8 +127,6 @@ function initSocketHandle(self) {
function Socket ( options ) {
if ( ! ( this instanceof Socket ) ) return new Socket ( options ) ;
Stream . call ( this ) ;
switch ( typeof options ) {
case 'number' :
options = { fd : options } ; // Legacy interface.
@ -142,7 +136,10 @@ function Socket(options) {
break ;
}
if ( typeof options . fd === 'undefined' ) {
this . readable = this . writable = false ;
if ( options . handle ) {
this . _ handle = options . handle ; // private
} else if ( typeof options . fd === 'undefined' ) {
this . _ handle = options && options . handle ; // private
} else {
this . _ handle = createPipe ( ) ;
@ -150,17 +147,105 @@ function Socket(options) {
this . readable = this . writable = true ;
}
this . onend = null ;
// shut down the socket when we're finished with it.
this . on ( 'finish' , onSocketFinish ) ;
this . on ( '_socketEnd' , onSocketEnd ) ;
initSocketHandle ( this ) ;
this . allowHalfOpen = options && options . allowHalfOpen ;
this . _ pendingWrite = null ;
stream . Duplex . call ( this , options ) ;
// handle strings directly
this . _ writableState . decodeStrings = false ;
// default to *not* allowing half open sockets
this . allowHalfOpen = options && options . allowHalfOpen || false ;
// if we have a handle, then start the flow of data into the
// buffer. if not, then this will happen when we connect
if ( this . _ handle && ( ! options || options . readable !== false ) )
this . read ( 0 ) ;
}
util . inherits ( Socket , stream . Duplex ) ;
// the user has called .end(), and all the bytes have been
// sent out to the other side.
// If allowHalfOpen is false, or if the readable side has
// ended already, then destroy.
// If allowHalfOpen is true, then we need to do a shutdown,
// so that only the writable side will be cleaned up.
function onSocketFinish ( ) {
debug ( 'onSocketFinish' ) ;
if ( this . _ readableState . ended ) {
debug ( 'oSF: ended, destroy' , this . _ readableState ) ;
return this . destroy ( ) ;
}
debug ( 'oSF: not ended, call shutdown()' ) ;
// otherwise, just shutdown, or destroy() if not possible
if ( ! this . _ handle . shutdown )
return this . destroy ( ) ;
var shutdownReq = this . _ handle . shutdown ( ) ;
if ( ! shutdownReq )
return this . _ destroy ( errnoException ( errno , 'shutdown' ) ) ;
shutdownReq . oncomplete = afterShutdown ;
}
function afterShutdown ( status , handle , req ) {
var self = handle . owner ;
debug ( 'afterShutdown destroyed=%j' , self . destroyed ,
self . _ readableState ) ;
// callback may come after call to destroy.
if ( self . destroyed )
return ;
if ( self . _ readableState . ended ) {
debug ( 'readableState ended, destroying' ) ;
self . destroy ( ) ;
} else {
self . once ( '_socketEnd' , self . destroy ) ;
}
}
// the EOF has been received, and no more bytes are coming.
// if the writable side has ended already, then clean everything
// up.
function onSocketEnd ( ) {
// XXX Should not have to do as much crap in this function.
// ended should already be true, since this is called *after*
// the EOF errno and onread has returned null to the _read cb.
debug ( 'onSocketEnd' , this . _ readableState ) ;
this . _ readableState . ended = true ;
if ( this . _ readableState . endEmitted ) {
this . readable = false ;
} else {
this . once ( 'end' , function ( ) {
this . readable = false ;
} ) ;
this . read ( 0 ) ;
}
util . inherits ( Socket , Stream ) ;
if ( ! this . allowHalfOpen )
this . destroySoon ( ) ;
}
exports . Socket = Socket ;
exports . Stream = Socket ; // Legacy naming.
Socket . prototype . listen = function ( ) {
debug ( 'socket.listen' ) ;
var self = this ;
self . on ( 'connection' , arguments [ 0 ] ) ;
listen ( self , null , null , null ) ;
@ -230,96 +315,62 @@ Object.defineProperty(Socket.prototype, 'readyState', {
Object . defineProperty ( Socket . prototype , 'bufferSize' , {
get : function ( ) {
if ( this . _ handle ) {
return this . _ handle . writeQueueSize + this . _ connectQueueSize ;
return this . _ handle . writeQueueSize ;
}
}
} ) ;
Socket . prototype . pause = function ( ) {
this . _ paused = true ;
if ( this . _ handle && ! this . _ connecting ) {
this . _ handle . readStop ( ) ;
// Just call handle.readStart until we have enough in the buffer
Socket . prototype . _ read = function ( n , callback ) {
debug ( '_read' ) ;
if ( this . _ connecting || ! this . _ handle ) {
debug ( '_read wait for connection' ) ;
this . once ( 'connect' , this . _ read . bind ( this , n , callback ) ) ;
return ;
}
} ;
assert ( callback === this . _ readableState . onread ) ;
assert ( this . _ readableState . reading = true ) ;
Socket . prototype . resume = function ( ) {
this . _ paused = false ;
if ( this . _ handle && ! this . _ connecting ) {
this . _ handle . readStart ( ) ;
if ( ! this . _ handle . reading ) {
debug ( 'Socket._read readStart' ) ;
this . _ handle . reading = true ;
var r = this . _ handle . readStart ( ) ;
if ( r )
this . _ destroy ( errnoException ( errno , 'read' ) ) ;
} else {
debug ( 'readStart already has been called.' ) ;
}
} ;
Socket . prototype . end = function ( data , encoding ) {
if ( this . _ connecting && ( ( this . _ flags & FLAG_SHUTDOWN_QUEUED ) == 0 ) ) {
// still connecting, add data to buffer
if ( data ) this . write ( data , encoding ) ;
stream . Duplex . prototype . end . call ( this , data , encoding ) ;
this . writable = false ;
this . _ flags |= FLAG_SHUTDOWN_QUEUED ;
}
if ( ! this . writable ) return ;
this . writable = false ;
if ( data ) this . write ( data , encoding ) ;
DTRACE_NET_STREAM_END ( this ) ;
if ( ! this . readable ) {
this . destroySoon ( ) ;
} else {
this . _ flags |= FLAG_SHUTDOWN ;
var shutdownReq = this . _ handle . shutdown ( ) ;
if ( ! shutdownReq ) {
this . _ destroy ( errnoException ( errno , 'shutdown' ) ) ;
return false ;
}
shutdownReq . oncomplete = afterShutdown ;
}
return true ;
} ;
function afterShutdown ( status , handle , req ) {
var self = handle . owner ;
assert . ok ( self . _ flags & FLAG_SHUTDOWN ) ;
assert . ok ( ! self . writable ) ;
// callback may come after call to destroy.
if ( self . destroyed ) {
// just in case we're waiting for an EOF.
if ( ! this . _ readableState . endEmitted )
this . read ( 0 ) ;
return ;
}
if ( self . _ flags & FLAG_GOT_EOF || ! self . readable ) {
self . _ destroy ( ) ;
} else {
}
}
} ;
Socket . prototype . destroySoon = function ( ) {
this . writable = false ;
this . _ flags |= FLAG_DESTROY_SOON ;
if ( this . _ pendingWriteReqs == 0 ) {
this . _ destroy ( ) ;
}
} ;
if ( this . writable )
this . end ( ) ;
Socket . prototype . _ connectQueueCleanUp = function ( exception ) {
this . _ connecting = false ;
this . _ connectQueueSize = 0 ;
this . _ connectQueue = null ;
if ( this . _ writableState . finishing || this . _ writableState . finished )
this . destroy ( ) ;
else
this . once ( 'finish' , this . destroy ) ;
} ;
Socket . prototype . _ destroy = function ( exception , cb ) {
debug ( 'destroy' ) ;
var self = this ;
function fireErrorCallbacks ( ) {
@ -333,13 +384,12 @@ Socket.prototype._destroy = function(exception, cb) {
} ;
if ( this . destroyed ) {
debug ( 'already destroyed, fire error callbacks' ) ;
fireErrorCallbacks ( ) ;
return ;
}
self . _ connectQueueCleanUp ( ) ;
debug ( 'destroy' ) ;
self . _ connecting = false ;
this . readable = this . writable = false ;
@ -347,6 +397,8 @@ Socket.prototype._destroy = function(exception, cb) {
debug ( 'close' ) ;
if ( this . _ handle ) {
if ( this !== process . stderr )
debug ( 'close handle' ) ;
this . _ handle . close ( ) ;
this . _ handle . onread = noop ;
this . _ handle = null ;
@ -355,6 +407,7 @@ Socket.prototype._destroy = function(exception, cb) {
fireErrorCallbacks ( ) ;
process . nextTick ( function ( ) {
debug ( 'emit close' ) ;
self . emit ( 'close' , exception ? true : false ) ;
} ) ;
@ -362,6 +415,7 @@ Socket.prototype._destroy = function(exception, cb) {
if ( this . server ) {
COUNTER_NET_SERVER_CONNECTION_CLOSE ( this ) ;
debug ( 'has server' ) ;
this . server . _ connections -- ;
if ( this . server . _ emitCloseIfDrained ) {
this . server . _ emitCloseIfDrained ( ) ;
@ -371,10 +425,13 @@ Socket.prototype._destroy = function(exception, cb) {
Socket . prototype . destroy = function ( exception ) {
debug ( 'destroy' , exception ) ;
this . _ destroy ( exception ) ;
} ;
// This function is called whenever the handle gets a
// buffer, or when there's an error reading.
function onread ( buffer , offset , length ) {
var handle = this ;
var self = handle . owner ;
@ -383,47 +440,56 @@ function onread(buffer, offset, length) {
timers . active ( self ) ;
var end = offset + length ;
debug ( 'onread' , global . errno , offset , length , end ) ;
if ( buffer ) {
// Emit 'data' event.
debug ( 'got data' ) ;
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 ) ) ;
}
// read success.
// In theory (and in practice) calling readStop right now
// will prevent this from being called again until _read() gets
// called again.
// if we didn't get any bytes, that doesn't necessarily mean EOF.
// wait for the next one .
if ( offset === end ) {
debug ( 'not any data, keep waiting' ) ;
return ;
}
// if it's not enough data, we'll just call handle.readStart()
// again right away.
self . bytesRead += length ;
self . _ readableState . onread ( null , buffer . slice ( offset , end ) ) ;
if ( handle . reading && ! self . _ readableState . reading ) {
handle . reading = false ;
debug ( 'readStop' ) ;
var r = handle . readStop ( ) ;
if ( r )
self . _ destroy ( errnoException ( errno , 'read' ) ) ;
}
// Optimization: emit the original buffer with end points
if ( self . ondata ) self . ondata ( buffer , offset , end ) ;
} else if ( errno == 'EOF' ) {
// EOF
debug ( 'EOF' ) ;
if ( self . _ readableState . length === 0 )
self . readable = false ;
assert . ok ( ! ( self . _ flags & FLAG_GOT_EOF ) ) ;
self . _ flags |= FLAG_GOT_EOF ;
if ( self . onend ) self . once ( 'end' , self . onend ) ;
// We call destroy() before end(). 'close' not emitted until nextTick so
// the 'end' event will come first as required.
if ( ! self . writable ) self . _ destroy ( ) ;
// send a null to the _read cb to signal the end of data.
self . _ readableState . onread ( null , null ) ;
if ( ! self . allowHalfOpen ) self . end ( ) ;
if ( self . _ decoder ) {
var ret = self . _ decoder . end ( ) ;
if ( ret )
self . emit ( 'data' , ret ) ;
}
if ( self . _ events && self . _ events [ 'end' ] ) self . emit ( 'end' ) ;
if ( self . onend ) self . onend ( ) ;
// internal end event so that we know that the actual socket
// is no longer readable, and we can start the shutdown
// procedure. No need to wait for all the data to be consumed.
self . emit ( '_socketEnd' ) ;
} else {
debug ( 'error' , errno ) ;
// Error
if ( errno == 'ECONNRESET' ) {
self . _ destroy ( ) ;
@ -434,12 +500,6 @@ function onread(buffer, offset, length) {
}
Socket . prototype . setEncoding = function ( encoding ) {
var StringDecoder = require ( 'string_decoder' ) . StringDecoder ; // lazy load
this . _ decoder = new StringDecoder ( encoding ) ;
} ;
Socket . prototype . _ getpeername = function ( ) {
if ( ! this . _ handle || ! this . _ handle . getpeername ) {
return { } ;
@ -465,63 +525,39 @@ Socket.prototype.__defineGetter__('remotePort', function() {
} ) ;
/ *
* Arguments data , [ encoding ] , [ cb ]
* /
Socket . prototype . write = function ( data , arg1 , arg2 ) {
var encoding , cb ;
Socket . prototype . write = function ( chunk , encoding , cb ) {
if ( typeof chunk !== 'string' && ! Buffer . isBuffer ( chunk ) )
throw new TypeError ( 'invalid data' ) ;
return stream . Duplex . prototype . write . apply ( this , arguments ) ;
} ;
// parse arguments
if ( arg1 ) {
if ( typeof arg1 === 'string' ) {
encoding = arg1 ;
cb = arg2 ;
} else if ( typeof arg1 === 'function' ) {
cb = arg1 ;
} else {
throw new Error ( 'bad arg' ) ;
}
}
if ( typeof data === 'string' ) {
encoding = ( encoding || 'utf8' ) . toLowerCase ( ) ;
switch ( encoding ) {
case 'utf8' :
case 'utf-8' :
case 'ascii' :
case 'ucs2' :
case 'ucs-2' :
case 'utf16le' :
case 'utf-16le' :
// This encoding can be handled in the binding layer.
break ;
Socket . prototype . _ write = function ( dataEncoding , cb ) {
assert ( Array . isArray ( dataEncoding ) ) ;
var data = dataEncoding [ 0 ] ;
var encoding = dataEncoding [ 1 ] || 'utf8' ;
default :
data = new Buffer ( data , encoding ) ;
}
} else if ( ! Buffer . isBuffer ( data ) ) {
throw new TypeError ( 'First argument must be a buffer or a string.' ) ;
}
if ( this !== process . stderr && this !== process . stdout )
debug ( 'Socket._write' ) ;
// If we are still connecting, then buffer this for later.
// The Writable logic will buffer up any more writes while
// waiting for this one to be done.
if ( this . _ connecting ) {
this . _ connectQueueSize += data . length ;
if ( this . _ connectQueue ) {
this . _ connectQueue . push ( [ data , encoding , cb ] ) ;
} else {
this . _ connectQueue = [ [ data , encoding , cb ] ] ;
}
return false ;
debug ( '_write: waiting for connection' ) ;
this . _ pendingWrite = dataEncoding ;
this . once ( 'connect' , function ( ) {
debug ( '_write: connected now, try again' ) ;
this . _ write ( dataEncoding , cb ) ;
} ) ;
return ;
}
this . _ pendingWrite = null ;
return this . _ write ( data , encoding , cb ) ;
} ;
Socket . prototype . _ write = function ( data , encoding , cb ) {
timers . active ( this ) ;
if ( ! this . _ handle ) {
debug ( 'already destroyed' ) ;
this . _ destroy ( new Error ( 'This socket is closed.' ) , cb ) ;
return false ;
}
@ -550,39 +586,32 @@ Socket.prototype._write = function(data, encoding, cb) {
break ;
default :
assert ( 0 ) ;
writeReq = this . _ handle . writeBuffer ( new Buffer ( data , encoding ) ) ;
break ;
}
}
if ( ! writeReq || typeof writeReq !== 'object' ) {
this . _ destroy ( errnoException ( errno , 'write' ) , cb ) ;
return false ;
}
if ( ! writeReq || typeof writeReq !== 'object' )
return this . _ destroy ( errnoException ( errno , 'write' ) , cb ) ;
writeReq . oncomplete = afterWrite ;
writeReq . cb = cb ;
this . _ pendingWriteReqs ++ ;
this . _ bytesDispatched += writeReq . bytes ;
return this . _ handle . writeQueueSize == 0 ;
} ;
Socket . prototype . __ defineGetter__ ( 'bytesWritten' , function ( ) {
var bytes = this . _ bytesDispatched ,
connectQueue = this . _ connectQueue ;
state = this . _ writableState ,
pending = this . _ pendingWrite ;
if ( connectQueue ) {
connectQueue . forEach ( function ( el ) {
var data = el [ 0 ] ;
if ( Buffer . isBuffer ( data ) ) {
bytes += data . length ;
} else {
bytes += Buffer . byteLength ( data , el [ 1 ] ) ;
}
} , this ) ;
}
state . buffer . forEach ( function ( el ) {
bytes += Buffer . byteLength ( el [ 0 ] , el [ 1 ] ) ;
} ) ;
if ( pending )
bytes += Buffer . byteLength ( pending [ 0 ] , pending [ 1 ] ) ;
return bytes ;
} ) ;
@ -590,30 +619,28 @@ Socket.prototype.__defineGetter__('bytesWritten', function() {
function afterWrite ( status , handle , req ) {
var self = handle . owner ;
var state = self . _ writableState ;
if ( self !== process . stderr && self !== process . stdout )
debug ( 'afterWrite' , status , req ) ;
// callback may come after call to destroy.
if ( self . destroyed ) {
debug ( 'afterWrite destroyed' ) ;
return ;
}
if ( status ) {
debug ( 'write failure' , errnoException ( errno , 'write' ) ) ;
self . _ destroy ( errnoException ( errno , 'write' ) , req . cb ) ;
return ;
}
timers . active ( self ) ;
self . _ pendingWriteReqs -- ;
if ( self . _ pendingWriteReqs == 0 ) {
self . emit ( 'drain' ) ;
}
if ( req . cb ) req . cb ( ) ;
if ( self !== process . stderr && self !== process . stdout )
debug ( 'afterWrite call cb' ) ;
if ( self . _ pendingWriteReqs == 0 && self . _ flags & FLAG_DESTROY_SOON ) {
self . _ destroy ( ) ;
}
req . cb . call ( self ) ;
}
@ -663,10 +690,21 @@ Socket.prototype.connect = function(options, cb) {
return Socket . prototype . connect . apply ( this , args ) ;
}
if ( this . destroyed ) {
this . _ readableState . reading = false ;
this . _ readableState . ended = false ;
this . _ writableState . ended = false ;
this . _ writableState . ending = false ;
this . _ writableState . finished = false ;
this . _ writableState . finishing = false ;
this . destroyed = false ;
this . _ handle = null ;
}
var self = this ;
var pipe = ! ! options . path ;
if ( this . destroyed || ! this . _ handle ) {
if ( ! this . _ handle ) {
this . _ handle = pipe ? createPipe ( ) : createTCP ( ) ;
initSocketHandle ( this ) ;
}
@ -755,28 +793,15 @@ function afterConnect(status, handle, req, readable, writable) {
self . writable = writable ;
timers . active ( self ) ;
if ( self . readable && ! self . _ paused ) {
handle . readStart ( ) ;
}
if ( self . _ connectQueue ) {
debug ( 'Drain the connect queue' ) ;
var connectQueue = self . _ connectQueue ;
for ( var i = 0 ; i < connectQueue . length ; i ++ ) {
self . _ write . apply ( self , connectQueue [ i ] ) ;
}
self . _ connectQueueCleanUp ( ) ;
}
self . emit ( 'connect' ) ;
if ( self . _ flags & FLAG_SHUTDOWN_QUEUED ) {
// end called before connected - call end now with no data
self . _ flags &= ~ FLAG_SHUTDOWN_QUEUED ;
self . end ( ) ;
}
// start the first read, or get an immediate EOF.
// this doesn't actually consume any bytes, because len=0.
if ( readable )
self . read ( 0 ) ;
} else {
self . _ connectQueueCleanUp ( ) ;
self . _ connecting = false ;
self . _ destroy ( errnoException ( errno , 'connect' ) ) ;
}
}
@ -831,9 +856,9 @@ function Server(/* [ options, ] listener */) {
configurable : true , enumerable : true
} ) ;
this . allowHalfOpen = options . allowHalfOpen || false ;
this . _ handle = null ;
this . allowHalfOpen = options . allowHalfOpen || false ;
}
util . inherits ( Server , events . EventEmitter ) ;
exports . Server = Server ;
@ -901,12 +926,14 @@ var createServerHandle = exports._createServerHandle =
Server . prototype . _ listen2 = function ( address , port , addressType , backlog , fd ) {
debug ( 'listen2' , address , port , addressType , backlog ) ;
var self = this ;
var r = 0 ;
// If there is not yet a handle, we need to create one and bind.
// In the case of a server sent via IPC, we don't need to do this.
if ( ! self . _ handle ) {
debug ( '_listen2: create a handle' ) ;
self . _ handle = createServerHandle ( address , port , addressType , fd ) ;
if ( ! self . _ handle ) {
var error = errnoException ( errno , 'listen' ) ;
@ -915,6 +942,8 @@ Server.prototype._listen2 = function(address, port, addressType, backlog, fd) {
} ) ;
return ;
}
} else {
debug ( '_listen2: have a handle already' ) ;
}
self . _ handle . onconnection = onconnection ;
@ -1049,7 +1078,6 @@ function onconnection(clientHandle) {
} ) ;
socket . readable = socket . writable = true ;
clientHandle . readStart ( ) ;
self . _ connections ++ ;
socket . server = self ;
@ -1086,11 +1114,17 @@ Server.prototype.close = function(cb) {
} ;
Server . prototype . _ emitCloseIfDrained = function ( ) {
debug ( 'SERVER _emitCloseIfDrained' ) ;
var self = this ;
if ( self . _ handle || self . _ connections ) return ;
if ( self . _ handle || self . _ connections ) {
debug ( 'SERVER handle? %j connections? %d' ,
! ! self . _ handle , self . _ connections ) ;
return ;
}
process . nextTick ( function ( ) {
debug ( 'SERVER: emit close' ) ;
self . emit ( 'close' ) ;
} ) ;
} ;