Showing with 57 additions and 31 deletions.
  1. +27 −30 lib/_stream_readable.js
  2. +21 −1 lib/child_process.js
  3. +9 −0 lib/net.js
@@ -305,12 +305,14 @@ Readable.prototype.pipe = function(dest, pipeOpts) {
dest !== process.stdout &&
dest !== process.stderr) {
src.once('end', onend);
dest.on('unpipe', function unpipe(readable) {
if (readable === src) {
src.removeListener('end', onend);
dest.removeListener('unpipe', unpipe);
}
});
} else {
src.once('end', cleanup);
}

dest.on('unpipe', onunpipe);
function onunpipe(readable) {
if (readable !== src) return;
cleanup();
}

if (pipeOpts && pipeOpts.chunkSize)
@@ -326,19 +328,25 @@ Readable.prototype.pipe = function(dest, pipeOpts) {
// too slow.
var ondrain = pipeOnDrain(src);
dest.on('drain', ondrain);
dest.on('unpipe', function cleanup(readable) {
if (readable === src) {
dest.removeListener('unpipe', cleanup);

// if the reader is waiting for a drain event from this
// specific writer, then it would cause it to never start
// flowing again.
// So, if this is awaiting a drain, then we just call it now.
// If we don't know, then assume that we are waiting for one.
if (!dest._writableState || dest._writableState.needDrain)
ondrain();
}
});

function cleanup() {
// cleanup event handlers once the pipe is broken
dest.removeListener('close', unpipe);
dest.removeListener('finish', onfinish);
dest.removeListener('drain', ondrain);
dest.removeListener('error', onerror);
dest.removeListener('unpipe', onunpipe);
src.removeListener('end', onend);
src.removeListener('end', cleanup);

// if the reader is waiting for a drain event from this
// specific writer, then it would cause it to never start
// flowing again.
// So, if this is awaiting a drain, then we just call it now.
// If we don't know, then assume that we are waiting for one.
if (!dest._writableState || dest._writableState.needDrain)
ondrain();
}

// if the dest has an error, then stop piping into it.
// however, don't suppress the throwing behavior for this.
@@ -361,17 +369,6 @@ Readable.prototype.pipe = function(dest, pipeOpts) {
src.unpipe(dest);
}

// cleanup event handlers once the pipe is broken
dest.once('unpipe', function cleanup(readable) {
if (readable !== src) return;

dest.removeListener('close', unpipe);
dest.removeListener('finish', onfinish);
dest.removeListener('drain', ondrain);
dest.removeListener('error', onerror);
dest.removeListener('unpipe', cleanup);
});

// tell the dest that it's being piped to
dest.emit('pipe', src);

@@ -65,7 +65,6 @@ function createSocket(pipe, readable) {
if (readable) {
s.writable = false;
s.readable = true;
s.resume();
} else {
s.writable = true;
s.readable = false;
@@ -695,12 +694,33 @@ function ChildProcess() {
self.emit('exit', self.exitCode, self.signalCode);
}

// if any of the stdio streams have not been touched,
// then pull all the data through so that it can get the
// eof and emit a 'close' event.
// Do it on nextTick so that the user has one last chance
// to consume the output, if for example they only want to
// start reading the data once the process exits.
process.nextTick(function() {
flushStdio(self);
});

maybeClose(self);
};
}
util.inherits(ChildProcess, EventEmitter);


function flushStdio(subprocess) {
subprocess.stdio.forEach(function(stream, fd, stdio) {
if (!stream || !stream.readable || stream._consuming ||
stream._readableState.flowing)
return;
stream.resume();
});
}



function getHandleWrapType(stream) {
if (stream instanceof handleWraps.Pipe) return 'pipe';
if (stream instanceof handleWraps.TTY) return 'tty';
@@ -244,6 +244,15 @@ function onSocketEnd() {
exports.Socket = Socket;
exports.Stream = Socket; // Legacy naming.

Socket.prototype.read = function(n) {
if (n === 0)
return stream.Readable.prototype.read.call(this, n);

this.read = stream.Readable.prototype.read;
this._consuming = true;
return this.read(n);
};


Socket.prototype.listen = function() {
debug('socket.listen');