Browse Source

stream: Writable.end(chunk) after end is an error

Calling end(data) calls write(data).  Doing this after end should
raise a 'write after end' error.

However, because end() calls were previously ignored on already
ended streams, this error was confusingly suppressed, even though the
data never is written, and cannot get to the other side.
v0.9.12-release
isaacs 12 years ago
parent
commit
5222d19a11
  1. 31
      lib/_stream_writable.js
  2. 16
      test/simple/test-stream2-writable.js

31
lib/_stream_writable.js

@ -304,10 +304,6 @@ Writable.prototype._write = function(chunk, cb) {
Writable.prototype.end = function(chunk, encoding, cb) { Writable.prototype.end = function(chunk, encoding, cb) {
var state = this._writableState; var state = this._writableState;
// ignore unnecessary end() calls.
if (state.ending || state.ended || state.finished)
return;
if (typeof chunk === 'function') { if (typeof chunk === 'function') {
cb = chunk; cb = chunk;
chunk = null; chunk = null;
@ -317,17 +313,28 @@ Writable.prototype.end = function(chunk, encoding, cb) {
encoding = null; encoding = null;
} }
state.ending = true;
if (chunk) if (chunk)
this.write(chunk, encoding, cb); this.write(chunk, encoding);
else if (state.length === 0 && !state.finishing && !state.finished) {
// ignore unnecessary end() calls.
if (!state.ending && !state.ended && !state.finished)
endWritable(this, state, !!chunk, cb);
};
function endWritable(stream, state, hadChunk, cb) {
state.ending = true;
if (!hadChunk &&
state.length === 0 &&
!state.finishing) {
state.finishing = true; state.finishing = true;
this.emit('finish'); stream.emit('finish');
state.finished = true; state.finished = true;
if (cb) process.nextTick(cb);
} else if (cb) {
this.once('finish', cb);
} }
if (cb) {
if (state.finished || state.finishing)
process.nextTick(cb);
else
stream.once('finish', cb);
}
state.ended = true; state.ended = true;
}; };

16
test/simple/test-stream2-writable.js

@ -311,3 +311,19 @@ test('duplexes are pipable', function(t) {
assert(!gotError); assert(!gotError);
t.end(); t.end();
}); });
test('end(chunk) two times is an error', function(t) {
var w = new W();
w._write = function() {};
var gotError = false;
w.on('error', function(er) {
gotError = true;
t.equal(er.message, 'write after end');
});
w.end('this is the end');
w.end('and so is this');
process.nextTick(function() {
assert(gotError);
t.end();
});
});

Loading…
Cancel
Save