Showing with 113 additions and 27 deletions.
  1. +6 −0 lib/_stream_readable.js
  2. +4 −0 lib/_stream_writable.js
  3. +0 −3 lib/child_process.js
  4. +1 −0 lib/http.js
  5. +40 −15 lib/net.js
  6. +48 −9 test/simple/test-child-process-fork-net2.js
  7. +13 −0 test/simple/test-pipe-file-to-http.js
  8. +1 −0 test/simple/test-pipe.js
@@ -232,6 +232,12 @@ function onread(er, chunk) {
if (state.needReadable && !sync) {
state.needReadable = false;
this.emit('readable');
// just in case the user doesn't call read() on each readable
// event, start the ball rolling if we need to do another _read()
// call at this point to fill the buffer back up, or get the EOF
// event. This is particularly relevant for TCP streams where
// we get an immediate end() from the other side.
this.read(0);
}
}

@@ -45,6 +45,8 @@ function WritableState(options) {
this.ended = false;
// when 'finish' has emitted
this.finished = false;
// when 'finish' is being emitted
this.finishing = false;

// should we decode strings into buffers before passing to _write?
// this is here so that some node-core streams can optimize string
@@ -146,6 +148,7 @@ Writable.prototype.write = function(chunk, encoding, cb) {

if (state.length === 0 && (state.ended || state.ending)) {
// emit 'finish' at the very end.
state.finishing = true;
this.emit('finish');
state.finished = true;
return;
@@ -196,6 +199,7 @@ Writable.prototype.end = function(chunk, encoding) {
if (chunk)
this.write(chunk, encoding);
else if (state.length === 0) {
state.finishing = true;
this.emit('finish');
state.finished = true;
}
@@ -110,7 +110,6 @@ var handleConversion = {
'net.Socket': {
send: function(message, socket) {
// pause socket so no data is lost, will be resumed later
socket.pause();

// if the socket wsa created by net.Server
if (socket.server) {
@@ -142,7 +141,6 @@ var handleConversion = {
got: function(message, handle, emit) {
var socket = new net.Socket({handle: handle});
socket.readable = socket.writable = true;
socket.pause();

// if the socket was created by net.Server we will track the socket
if (message.key) {
@@ -153,7 +151,6 @@ var handleConversion = {
}

emit(socket);
socket.resume();
}
}
};
@@ -1717,6 +1717,7 @@ function connectionListener(socket) {
self.emit('clientError', e, this);
});

socket.resume();
socket.ondata = function(d, start, end) {
var ret = parser.execute(d, start, end - start);
if (ret instanceof Error) {
@@ -132,8 +132,6 @@ function Socket(options) {
break;
}

this._options = options;

this.readable = this.writable = false;
if (typeof options.fd === 'undefined') {
this._handle = options && options.handle; // private
@@ -160,7 +158,6 @@ function Socket(options) {

// default to *not* allowing half open sockets
this.allowHalfOpen = options && options.allowHalfOpen || false;

}
util.inherits(Socket, stream.Duplex);

@@ -177,21 +174,20 @@ function onSocketFinish() {
return this.destroy();
}

debug('oSF: not ended, call shutdown()');
// otherwise, just shutdown
var shutdownReq = this._handle.shutdown();

if (!shutdownReq) {
this._destroy(errnoException(errno, 'shutdown'));
return false;
}
if (!shutdownReq)
return this._destroy(errnoException(errno, 'shutdown'));

shutdownReq.oncomplete = afterShutdown;
return true;
}


function afterShutdown(status, handle, req) {
var self = handle.owner;

debug('afterShutdown destroyed=%j', self.destroyed,
self._readableState);

@@ -210,19 +206,23 @@ function afterShutdown(status, handle, req) {
// the EOF has been received, and no more bytes are coming.
// if the writable side has ended already, then clean everything
// up.
//
// Note that the Duplex class will forcibly end the writable
// side when the readable side ends if halfDuplex is not allowed.
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);
}

this.destroySoon();
if (!this.allowHalfOpen)
this.destroySoon();
}

exports.Socket = Socket;
@@ -332,12 +332,16 @@ Socket.prototype.end = function(data, encoding) {
stream.Duplex.prototype.end.call(this, data, encoding);
this.writable = false;
DTRACE_NET_STREAM_END(this);

// just in case we're waiting for an EOF.
this.read(0);
return;
};


Socket.prototype.destroySoon = function() {
if (this._writableState.finished)
debug('destroySoon finished? %j', this._writableState.finished);
if (this._writableState.finishing)
this.destroy();
else
this.once('finish', this.destroy);
@@ -530,6 +534,7 @@ Socket.prototype._write = function(dataEncoding, cb) {
timers.active(this);

if (!this._handle) {
debug('already destroyed');
this._destroy(new Error('This socket is closed.'), cb);
return false;
}
@@ -592,19 +597,26 @@ 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);

if (self !== process.stderr && self !== process.stdout)
debug('afterWrite call cb');

req.cb.call(self);
}

@@ -661,6 +673,7 @@ Socket.prototype.connect = function(options, cb) {
this._writableState.ended = false;
this._writableState.ending = false;
this._writableState.finished = false;
this._writableState.finishing = false;
this.destroyed = false;
this._handle = null;
}
@@ -761,7 +774,9 @@ function afterConnect(status, handle, req, readable, writable) {

// start the first read, or get an immediate EOF.
// this doesn't actually consume any bytes, because len=0.
self.read(0);
if (readable)
self.read(0);

} else {
self._connecting = false;
self._destroy(errnoException(errno, 'connect'));
@@ -888,12 +903,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');
@@ -902,6 +919,8 @@ Server.prototype._listen2 = function(address, port, addressType, backlog, fd) {
});
return;
}
} else {
debug('_listen2: have a handle already');
}

self._handle.onconnection = onconnection;
@@ -1071,11 +1090,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');
});
};
@@ -23,6 +23,7 @@ var assert = require('assert');
var common = require('../common');
var fork = require('child_process').fork;
var net = require('net');
var count = 12;

if (process.argv[2] === 'child') {

@@ -31,26 +32,59 @@ if (process.argv[2] === 'child') {
process.on('message', function(m, socket) {
if (!socket) return;

console.error('got socket', m);

// will call .end('end') or .write('write');
socket[m](m);

socket.resume();

socket.on('data', function() {
console.error('%d socket.data', process.pid, m);
});

socket.on('end', function() {
console.error('%d socket.end', process.pid, m);
});

// store the unfinished socket
if (m === 'write') {
endMe = socket;
console.error('setting endMe');
}

socket.on('close', function() {
console.error('%d socket.close', process.pid, m);
});

socket.on('finish', function() {
console.error('%d socket finished', process.pid, m, socket === endMe);
});
});

process.on('message', function(m) {
process.on('message', function om(m) {
if (m !== 'close') return;
process.removeListener('message', om);
console.error('got close message');
endMe.end('end');
endMe = null;
if (endMe) {
console.error('%d has endMe', process.pid);
endMe.end('end');
}

setTimeout(function() {
var h = process._getActiveHandles();
console.error('%d got end message, active handles=', process.pid, h);
h.forEach(function(handle) {
if (endMe && (handle === endMe || handle === endMe._handle))
console.error('endMe is still active!');
});
}, 1000).unref();
});

process.on('disconnect', function() {
endMe.end('end');
console.error('%d process disconnect, ending', process.pid);
if (endMe)
endMe.end('end');
endMe = null;
});

@@ -64,7 +98,7 @@ if (process.argv[2] === 'child') {

var connected = 0;
server.on('connection', function(socket) {
switch (connected) {
switch (connected % 6) {
case 0:
child1.send('end', socket); break;
case 1:
@@ -80,25 +114,29 @@ if (process.argv[2] === 'child') {
}
connected += 1;

if (connected === 6) {
if (connected === count) {
closeServer();
}
});

var disconnected = 0;
server.on('listening', function() {

var j = 6, client;
var j = count, client;
while (j--) {
client = net.connect(common.PORT, '127.0.0.1');
client.on('close', function() {
console.error('CLIENT: close event in master');
disconnected += 1;
});
setTimeout(client.end.bind(client, 'end'), 200);

}
});

var closeEmitted = false;
server.on('close', function() {
console.error('server close');
closeEmitted = true;

child1.kill();
@@ -117,6 +155,7 @@ if (process.argv[2] === 'child') {
timeElasped = Date.now() - startTime;
});

console.error('calling server.close');
server.close();

setTimeout(function() {
@@ -128,8 +167,8 @@ if (process.argv[2] === 'child') {
};

process.on('exit', function() {
assert.equal(disconnected, 6);
assert.equal(connected, 6);
assert.equal(disconnected, count);
assert.equal(connected, count);
assert.ok(closeEmitted);
assert.ok(timeElasped >= 190 && timeElasped <= 1000,
'timeElasped was not between 190 and 1000 ms');