Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fs: improve argument handling for ReadStream #19898

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e507f93
fs: improve argument handling for ReadStream
ryzokuken Apr 9, 2018
30c285f
test,fs: update tests for improvements in ReadStream
ryzokuken Apr 10, 2018
915d77c
Remove inconsistency
ryzokuken Apr 10, 2018
17d6d55
Separate testing for start and end
ryzokuken Apr 10, 2018
36574f4
Remove invalid check
ryzokuken Apr 10, 2018
e4415d5
Address comments regarding handling undefined
ryzokuken Apr 10, 2018
b946616
Refactor conditions
ryzokuken Apr 11, 2018
d00b46a
fs,doc: add documentation regarding changes to createReadStream
ryzokuken Apr 13, 2018
93b0915
Add a changes block and move new documentation towards the end
ryzokuken Apr 14, 2018
5d03199
Replace version by REPLACEME
ryzokuken Apr 14, 2018
047529b
Minimize comments and documentation
ryzokuken Apr 15, 2018
a90457b
Address nit by adding parens
ryzokuken Apr 15, 2018
c3be59a
Resolve merge conflict
ryzokuken Apr 16, 2018
41b3a2e
Thow RangeError on fractional values
ryzokuken Apr 20, 2018
3530baa
Cleanup fs.md
ryzokuken Apr 23, 2018
4863dba
Fix error
ryzokuken Apr 24, 2018
1cf1814
Fix failures
ryzokuken Apr 25, 2018
2c35628
Address nit
ryzokuken Apr 26, 2018
a24f115
Use isSafeInteger over isInteger
ryzokuken Apr 27, 2018
0368397
Change error message
ryzokuken Apr 29, 2018
b6fa515
Improve error handling
ryzokuken May 5, 2018
7bda63c
Refactor fs tests for brevity
ryzokuken May 5, 2018
c34ba43
Address nits
ryzokuken May 8, 2018
30e36c2
Update RangeError error message
ryzokuken May 9, 2018
9af225c
Update RangeError error message v2
ryzokuken May 9, 2018
c76e75a
Fix lint errors
ryzokuken May 9, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions doc/api/fs.md
Original file line number Diff line number Diff line change
Expand Up @@ -1291,6 +1291,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
Expand Down
43 changes: 26 additions & 17 deletions lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2014,30 +2014,39 @@ 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 (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 (!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.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();
Expand Down
66 changes: 49 additions & 17 deletions test/parallel/test-fs-read-stream-throw-type-error.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

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 });

// createReadSteam _should_ throw on NaN
createReadStreamErr(example, { start: NaN });
createReadStreamErr(example, { end: NaN });
createReadStreamErr(example, { start: NaN, end: NaN });
// Case 6: Should throw RangeError if start is greater than end
createReadStreamErr(example, { start: 5, end: 1 }, rangeError);
4 changes: 2 additions & 2 deletions test/parallel/test-fs-read-stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
});

Expand Down