diff options
author | Robert Nagy <ronagy@icloud.com> | 2021-07-02 14:51:30 +0200 |
---|---|---|
committer | Rich Trott <rtrott@gmail.com> | 2021-07-04 20:52:39 -0700 |
commit | 0738a2b7bd011c421b458e586c49f9a7f2657b90 (patch) | |
tree | ff55f5dc5277d4fc64aff34bcc549794cf4fb6bd /lib | |
parent | 68548fd661f87dbb69fc730e56cfd15ad8c8f9ca (diff) | |
download | node-new-0738a2b7bd011c421b458e586c49f9a7f2657b90.tar.gz |
stream: finished should error on errored stream
Calling finished before or after a stream has errored or closed
should end up with the same behavior.
PR-URL: https://github.com/nodejs/node/pull/39235
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/_http_client.js | 3 | ||||
-rw-r--r-- | lib/_http_incoming.js | 3 | ||||
-rw-r--r-- | lib/internal/streams/end-of-stream.js | 61 |
3 files changed, 41 insertions, 26 deletions
diff --git a/lib/_http_client.js b/lib/_http_client.js index fde7fde86b..598b585bcf 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -806,6 +806,9 @@ function onSocketNT(req, socket, err) { socket.emit('free'); } else { finished(socket.destroy(err || req[kError]), (er) => { + if (er?.code === 'ERR_STREAM_PREMATURE_CLOSE') { + er = null; + } _destroy(req, er || err); }); return; diff --git a/lib/_http_incoming.js b/lib/_http_incoming.js index 3961b583de..a92687ce37 100644 --- a/lib/_http_incoming.js +++ b/lib/_http_incoming.js @@ -188,6 +188,9 @@ IncomingMessage.prototype._destroy = function _destroy(err, cb) { if (this.socket && !this.socket.destroyed && this.aborted) { this.socket.destroy(err); const cleanup = finished(this.socket, (e) => { + if (e?.code === 'ERR_STREAM_PREMATURE_CLOSE') { + e = null; + } cleanup(); process.nextTick(onError, this, e || err, cb); }); diff --git a/lib/internal/streams/end-of-stream.js b/lib/internal/streams/end-of-stream.js index 318ab4c2e6..efc2441c51 100644 --- a/lib/internal/streams/end-of-stream.js +++ b/lib/internal/streams/end-of-stream.js @@ -98,8 +98,7 @@ function eos(stream, options, callback) { isWritable(stream) === writable ); - let writableFinished = stream.writableFinished || - (wState && wState.finished); + let writableFinished = stream.writableFinished || wState?.finished; const onfinish = () => { writableFinished = true; // Stream should not be destroyed here. If it is that @@ -111,8 +110,7 @@ function eos(stream, options, callback) { if (!readable || readableEnded) callback.call(stream); }; - let readableEnded = stream.readableEnded || - (rState && rState.endEmitted); + let readableEnded = stream.readableEnded || rState?.endEmitted; const onend = () => { readableEnded = true; // Stream should not be destroyed here. If it is that @@ -128,7 +126,17 @@ function eos(stream, options, callback) { callback.call(stream, err); }; + let closed = wState?.closed || rState?.closed; + const onclose = () => { + closed = true; + + const errored = wState?.errored || rState?.errored; + + if (errored && typeof errored !== 'boolean') { + return callback.call(stream, errored); + } + if (readable && !readableEnded) { if (!isReadableEnded(stream)) return callback.call(stream, @@ -139,6 +147,7 @@ function eos(stream, options, callback) { return callback.call(stream, new ERR_STREAM_PREMATURE_CLOSE()); } + callback.call(stream); }; @@ -168,29 +177,29 @@ function eos(stream, options, callback) { if (options.error !== false) stream.on('error', onerror); stream.on('close', onclose); - // _closed is for OutgoingMessage which is not a proper Writable. - const closed = (!wState && !rState && stream._closed === true) || ( - (wState && wState.closed) || - (rState && rState.closed) || - (wState && wState.errorEmitted) || - (rState && rState.errorEmitted) || - (rState && stream.req && stream.aborted) || - ( - (!writable || (wState && wState.finished)) && - (!readable || (rState && rState.endEmitted)) - ) - ); - if (closed) { - // TODO(ronag): Re-throw error if errorEmitted? - // TODO(ronag): Throw premature close as if finished was called? - // before being closed? i.e. if closed but not errored, ended or finished. - // TODO(ronag): Throw some kind of error? Does it make sense - // to call finished() on a "finished" stream? - // TODO(ronag): willEmitClose? - process.nextTick(() => { - callback(); - }); + process.nextTick(onclose); + } else if (wState?.errorEmitted || rState?.errorEmitted) { + if (!willEmitClose) { + process.nextTick(onclose); + } + } else if ( + !readable && + (!willEmitClose || stream.readable) && + writableFinished + ) { + process.nextTick(onclose); + } else if ( + !writable && + (!willEmitClose || stream.writable) && + readableEnded + ) { + process.nextTick(onclose); + } else if (!wState && !rState && stream._closed === true) { + // _closed is for OutgoingMessage which is not a proper Writable. + process.nextTick(onclose); + } else if ((rState && stream.req && stream.aborted)) { + process.nextTick(onclose); } const cleanup = () => { |