Skip to content

Commit 36468ca

Browse files
committed
lib: require a callback for end-of-stream
Make the callback mandatory as mostly done in all other Node.js callback APIs so users explicitly have to decide what to do in error cases. This also documents the options for `Stream.finished()`. When originally implemented it was missed that Stream.finished() has an optional options object. PR-URL: #21058 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Mathias Buus <mathiasbuus@gmail.com>
1 parent 9dae0ae commit 36468ca

File tree

3 files changed

+60
-9
lines changed

3 files changed

+60
-9
lines changed

doc/api/stream.md

+11-2
Original file line numberDiff line numberDiff line change
@@ -1294,12 +1294,21 @@ implementors should not override this method, but instead implement
12941294
[`readable._destroy()`][readable-_destroy].
12951295
The default implementation of `_destroy()` for `Transform` also emit `'close'`.
12961296

1297-
### stream.finished(stream, callback)
1297+
### stream.finished(stream[, options], callback)
12981298
<!-- YAML
12991299
added: v10.0.0
13001300
-->
13011301

13021302
* `stream` {Stream} A readable and/or writable stream.
1303+
* `options` {Object}
1304+
* `error` {boolean} If set to `false`, then a call to `emit('error', err)` is
1305+
not treated as finished. **Default**: `true`.
1306+
* `readable` {boolean} When set to `false`, the callback will be called when
1307+
the stream ends even though the stream might still be readable.
1308+
**Default**: `true`.
1309+
* `writable` {boolean} When set to `false`, the callback will be called when
1310+
the stream ends even though the stream might still be writable.
1311+
**Default**: `true`.
13031312
* `callback` {Function} A callback function that takes an optional error
13041313
argument.
13051314

@@ -2438,7 +2447,7 @@ contain multi-byte characters.
24382447
[zlib]: zlib.html
24392448
[hwm-gotcha]: #stream_highwatermark_discrepancy_after_calling_readable_setencoding
24402449
[pipeline]: #stream_stream_pipeline_streams_callback
2441-
[finished]: #stream_stream_finished_stream_callback
2450+
[finished]: #stream_stream_finished_stream_options_callback
24422451
[stream-_flush]: #stream_transform_flush_callback
24432452
[stream-_read]: #stream_readable_read_size_1
24442453
[stream-_transform]: #stream_transform_transform_chunk_encoding_callback

lib/internal/streams/end-of-stream.js

+13-5
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@
44
'use strict';
55

66
const {
7+
ERR_INVALID_ARG_TYPE,
78
ERR_STREAM_PREMATURE_CLOSE
89
} = require('internal/errors').codes;
910

10-
function noop() {}
11-
1211
function isRequest(stream) {
1312
return stream.setHeader && typeof stream.abort === 'function';
1413
}
@@ -23,10 +22,19 @@ function once(callback) {
2322
}
2423

2524
function eos(stream, opts, callback) {
26-
if (typeof opts === 'function') return eos(stream, null, opts);
27-
if (!opts) opts = {};
25+
if (arguments.length === 2) {
26+
callback = opts;
27+
opts = {};
28+
} else if (opts == null) {
29+
opts = {};
30+
} else if (typeof opts !== 'object') {
31+
throw new ERR_INVALID_ARG_TYPE('opts', 'object', opts);
32+
}
33+
if (typeof callback !== 'function') {
34+
throw new ERR_INVALID_ARG_TYPE('callback', 'function', callback);
35+
}
2836

29-
callback = once(callback || noop);
37+
callback = once(callback);
3038

3139
const ws = stream._writableState;
3240
const rs = stream._readableState;

test/parallel/test-stream-finished.js

+36-2
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ const { promisify } = require('util');
9191
{
9292
const rs = fs.createReadStream('file-does-not-exist');
9393

94-
finished(rs, common.mustCall((err) => {
95-
assert.strictEqual(err.code, 'ENOENT');
94+
finished(rs, common.expectsError({
95+
code: 'ENOENT'
9696
}));
9797
}
9898

@@ -119,3 +119,37 @@ const { promisify } = require('util');
119119
rs.push(null);
120120
rs.resume();
121121
}
122+
123+
// Test faulty input values and options.
124+
{
125+
const rs = new Readable({
126+
read() {}
127+
});
128+
129+
assert.throws(
130+
() => finished(rs, 'foo'),
131+
{
132+
name: /ERR_INVALID_ARG_TYPE/,
133+
message: /callback/
134+
}
135+
);
136+
assert.throws(
137+
() => finished(rs, 'foo', () => {}),
138+
{
139+
name: /ERR_INVALID_ARG_TYPE/,
140+
message: /opts/
141+
}
142+
);
143+
assert.throws(
144+
() => finished(rs, {}, 'foo'),
145+
{
146+
name: /ERR_INVALID_ARG_TYPE/,
147+
message: /callback/
148+
}
149+
);
150+
151+
finished(rs, null, common.mustCall());
152+
153+
rs.push(null);
154+
rs.resume();
155+
}

0 commit comments

Comments
 (0)