diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js index f57be90fcc5..ee2db017476 100644 --- a/lib/_stream_readable.js +++ b/lib/_stream_readable.js @@ -271,9 +271,7 @@ function onread(stream, er, chunk) { if (er) return stream.emit('error', er); - if (chunk === null || - chunk === undefined || - (!state.objectMode && !chunk.length)) { + if (chunk === null || chunk === undefined) { // eof state.ended = true; if (state.decoder) { @@ -296,11 +294,20 @@ function onread(stream, er, chunk) { if (state.decoder) chunk = state.decoder.write(chunk); + // at this point, if we got a zero-length buffer or string, + // and we're not in object-mode, then there's really no point + // continuing. it means that there is nothing to read right + // now, but as we have not received the EOF-signaling null, + // we're not ended. we've already unset the reading flag, + // so just get out of here. + if (!state.objectMode && + (chunk || typeof chunk === 'string') && + 0 === chunk.length) + return; + // update the buffer info. - if (chunk || (state.objectMode && chunk !== undefined && chunk !== null)) { - state.length += state.objectMode ? 1 : chunk.length; - state.buffer.push(chunk); - } + state.length += state.objectMode ? 1 : chunk.length; + state.buffer.push(chunk); // if we haven't gotten enough to pass the lowWaterMark, // and we haven't ended, then don't bother telling the user diff --git a/test/simple/test-stream2-readable-empty-buffer-no-eof.js b/test/simple/test-stream2-readable-empty-buffer-no-eof.js new file mode 100644 index 00000000000..18d45966c07 --- /dev/null +++ b/test/simple/test-stream2-readable-empty-buffer-no-eof.js @@ -0,0 +1,85 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +var Readable = require('stream').Readable; + +var r = new Readable(); + +// should not end when we get a Buffer(0) or '' as the _read result +// that just means that there is *temporarily* no data, but to go +// ahead and try again later. +// +// note that this is very unusual. it only works for crypto streams +// because the other side of the stream will call read(0) to cycle +// data through openssl. that's why we set the timeouts to call +// r.read(0) again later, otherwise there is no more work being done +// and the process just exits. + +var buf = new Buffer(5); +buf.fill('x'); +var reads = 5; +r._read = function(n, cb) { + switch (reads--) { + case 0: + return cb(null, null); // EOF + case 1: + return cb(null, buf); + case 2: + setTimeout(r.read.bind(r, 0), 10); + return cb(null, new Buffer(0)); // Not-EOF! + case 3: + setTimeout(r.read.bind(r, 0), 10); + return process.nextTick(function() { + return cb(null, new Buffer(0)); + }); + case 4: + setTimeout(r.read.bind(r, 0), 10); + return setTimeout(function() { + return cb(null, new Buffer(0)); + }); + case 5: + return setTimeout(function() { + return cb(null, buf); + }); + default: + throw new Error('unreachable'); + } +}; + +var results = []; +function flow() { + var chunk; + while (null !== (chunk = r.read())) + results.push(chunk + ''); +} +r.on('readable', flow); +r.on('end', function() { + results.push('EOF'); +}); +flow(); + +process.on('exit', function() { + assert.deepEqual(results, [ 'xxxxx', 'xxxxx', 'EOF' ]); + console.log('ok'); +});