diff --git a/lib/fs.js b/lib/fs.js index 5efccceb544d20..b0d117673d6bfc 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -1919,8 +1919,7 @@ function ReadStream(path, options) { this.flags = options.flags === undefined ? 'r' : options.flags; this.mode = options.mode === undefined ? 0o666 : options.mode; - this.start = typeof this.fd !== 'number' && options.start === undefined ? - 0 : options.start; + this.start = options.start; this.end = options.end; this.autoClose = options.autoClose === undefined ? true : options.autoClose; this.pos = undefined; @@ -1943,6 +1942,12 @@ function ReadStream(path, options) { this.pos = this.start; } + // Backwards compatibility: Make sure `end` is a number regardless of `start`. + // TODO(addaleax): Make the above typecheck not depend on `start` instead. + // (That is a semver-major change). + if (typeof this.end !== 'number') + this.end = Infinity; + if (typeof this.fd !== 'number') this.open(); @@ -1996,6 +2001,8 @@ ReadStream.prototype._read = function(n) { if (this.pos !== undefined) toRead = Math.min(this.end - this.pos + 1, toRead); + else + toRead = Math.min(this.end - this.bytesRead + 1, toRead); // already read everything we were supposed to read! // treat as EOF. diff --git a/test/parallel/test-fs-read-stream.js b/test/parallel/test-fs-read-stream.js index 95d5fbeaef9973..5161fd068b14d6 100644 --- a/test/parallel/test-fs-read-stream.js +++ b/test/parallel/test-fs-read-stream.js @@ -1,5 +1,7 @@ 'use strict'; const common = require('../common'); + +const child_process = require('child_process'); const assert = require('assert'); const fixtures = require('../common/fixtures'); @@ -146,11 +148,6 @@ stream.on('end', function() { })); } -// pause and then resume immediately. -const pauseRes = fs.createReadStream(rangeFile); -pauseRes.pause(); -pauseRes.resume(); - let file7 = fs.createReadStream(rangeFile, {autoClose: false }); file7.on('data', () => {}); file7.on('end', function() { @@ -173,6 +170,38 @@ function file7Next() { }); } +if (!common.isWindows) { + // Verify that end works when start is not specified, and we do not try to + // use positioned reads. This makes sure that this keeps working for + // non-seekable file descriptors. + common.refreshTmpDir(); + const filename = `${common.tmpDir}/foo.pipe`; + const mkfifoResult = child_process.spawnSync('mkfifo', [filename]); + if (!mkfifoResult.error) { + child_process.exec(`echo "xyz foobar" > '${filename}'`); + const stream = new fs.createReadStream(filename, { end: 1 }); + stream.data = ''; + + stream.on('data', function(chunk) { + stream.data += chunk; + }); + + stream.on('end', common.mustCall(function() { + assert.strictEqual('xy', stream.data); + fs.unlinkSync(filename); + })); + } else { + common.printSkipMessage('mkfifo not available'); + } +} + +{ + // pause and then resume immediately. + const pauseRes = fs.createReadStream(rangeFile); + pauseRes.pause(); + pauseRes.resume(); +} + // Just to make sure autoClose won't close the stream because of error. const file8 = fs.createReadStream(null, {fd: 13337, autoClose: false }); file8.on('data', () => {});