fs: improve argument handling for ReadStream

Improve handling of erratic arguments in fs.ReadStream

Refs: https://github.com/nodejs/node/pull/19732
PR-URL: https://github.com/nodejs/node/pull/19898
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Ron Korving <ron@ronkorving.nl>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
This commit is contained in:
Ujjwal Sharma 2018-04-09 23:02:08 +05:30 committed by Anna Henningsen
parent 745463a0b7
commit 19374fd25b
No known key found for this signature in database
GPG Key ID: 9C63F3A6CD2AD8F9
5 changed files with 96 additions and 38 deletions

View File

@ -1299,6 +1299,11 @@ fs.copyFileSync('source.txt', 'destination.txt', COPYFILE_EXCL);
<!-- YAML
added: v0.1.31
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/19898
description: Impose new restrictions on `start` and `end`, throwing
more appropriate errors in cases when we cannot reasonably
handle the input values.
- version: v7.6.0
pr-url: https://github.com/nodejs/node/pull/10739
description: The `path` parameter can be a WHATWG `URL` object using

View File

@ -2031,30 +2031,51 @@ function ReadStream(path, options) {
this.closed = false;
if (this.start !== undefined) {
if (typeof this.start !== 'number' || Number.isNaN(this.start)) {
throw new ERR_INVALID_ARG_TYPE('start', 'number', this.start);
if (!Number.isSafeInteger(this.start)) {
if (typeof this.start !== 'number')
throw new ERR_INVALID_ARG_TYPE('start', 'number', this.start);
if (!Number.isInteger(this.start))
throw new ERR_OUT_OF_RANGE('start', 'an integer', this.start);
throw new ERR_OUT_OF_RANGE(
'start',
'>= 0 and <= 2 ** 53 - 1',
this.start
);
}
if (this.end === undefined) {
this.end = Infinity;
} else if (typeof this.end !== 'number' || Number.isNaN(this.end)) {
throw new ERR_INVALID_ARG_TYPE('end', 'number', this.end);
}
if (this.start > this.end) {
const errVal = `{start: ${this.start}, end: ${this.end}}`;
throw new ERR_OUT_OF_RANGE('start', '<= "end"', errVal);
if (this.start < 0) {
throw new ERR_OUT_OF_RANGE(
'start',
'>= 0 and <= 2 ** 53 - 1',
this.start
);
}
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')
if (this.end === undefined) {
this.end = Infinity;
else if (Number.isNaN(this.end))
throw new ERR_INVALID_ARG_TYPE('end', 'number', this.end);
} else if (this.end !== Infinity) {
if (!Number.isSafeInteger(this.end)) {
if (typeof this.end !== 'number')
throw new ERR_INVALID_ARG_TYPE('end', 'number', this.end);
if (!Number.isInteger(this.end))
throw new ERR_OUT_OF_RANGE('end', 'an integer', this.end);
throw new ERR_OUT_OF_RANGE('end', '>= 0 and <= 2 ** 53 - 1', this.end);
}
if (this.end < 0) {
throw new ERR_OUT_OF_RANGE('end', '>= 0 and <= 2 ** 53 - 1', this.end);
}
if (this.start !== undefined && this.start > this.end) {
throw new ERR_OUT_OF_RANGE(
'start',
`<= "end" (here: ${this.end})`,
this.start
);
}
}
if (typeof this.fd !== 'number')
this.open();

View File

@ -110,8 +110,8 @@ const rangeFile = fixtures.path('x.txt');
{
const message =
'The value of "start" is out of range. It must be <= "end". ' +
'Received {start: 10, end: 2}';
'The value of "start" is out of range. It must be <= "end" (here: 2).' +
' Received 10';
common.expectsError(
() => {

View File

@ -13,23 +13,55 @@ fs.createReadStream(example, null);
fs.createReadStream(example, 'utf8');
fs.createReadStream(example, { encoding: 'utf8' });
const createReadStreamErr = (path, opt) => {
common.expectsError(
() => {
fs.createReadStream(path, opt);
},
{
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError
});
const createReadStreamErr = (path, opt, error) => {
common.expectsError(() => {
fs.createReadStream(path, opt);
}, error);
};
createReadStreamErr(example, 123);
createReadStreamErr(example, 0);
createReadStreamErr(example, true);
createReadStreamErr(example, false);
const typeError = {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError
};
// createReadSteam _should_ throw on NaN
createReadStreamErr(example, { start: NaN });
createReadStreamErr(example, { end: NaN });
createReadStreamErr(example, { start: NaN, end: NaN });
const rangeError = {
code: 'ERR_OUT_OF_RANGE',
type: RangeError
};
[123, 0, true, false].forEach((opts) =>
createReadStreamErr(example, opts, typeError)
);
// Case 0: Should not throw if either start or end is undefined
[{}, { start: 0 }, { end: Infinity }].forEach((opts) =>
fs.createReadStream(example, opts)
);
// Case 1: Should throw TypeError if either start or end is not of type 'number'
[
{ start: 'invalid' },
{ end: 'invalid' },
{ start: 'invalid', end: 'invalid' }
].forEach((opts) => createReadStreamErr(example, opts, typeError));
// Case 2: Should throw RangeError if either start or end is NaN
[{ start: NaN }, { end: NaN }, { start: NaN, end: NaN }].forEach((opts) =>
createReadStreamErr(example, opts, rangeError)
);
// Case 3: Should throw RangeError if either start or end is negative
[{ start: -1 }, { end: -1 }, { start: -1, end: -1 }].forEach((opts) =>
createReadStreamErr(example, opts, rangeError)
);
// Case 4: Should throw RangeError if either start or end is fractional
[{ start: 0.1 }, { end: 0.1 }, { start: 0.1, end: 0.1 }].forEach((opts) =>
createReadStreamErr(example, opts, rangeError)
);
// Case 5: Should not throw if both start and end are whole numbers
fs.createReadStream(example, { start: 1, end: 5 });
// Case 6: Should throw RangeError if start is greater than end
createReadStreamErr(example, { start: 5, end: 1 }, rangeError);

View File

@ -148,8 +148,8 @@ common.expectsError(
},
{
code: 'ERR_OUT_OF_RANGE',
message: 'The value of "start" is out of range. It must be <= "end". ' +
'Received {start: 10, end: 2}',
message: 'The value of "start" is out of range. It must be <= "end"' +
' (here: 2). Received 10',
type: RangeError
});