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

stream.pipeline(httpResponse, decompressStream) emits error three times #31029

Closed
szmarczak opened this issue Dec 19, 2019 · 7 comments
Closed
Labels
http Issues or PRs related to the http subsystem. stream Issues and PRs related to the stream subsystem.

Comments

@szmarczak
Copy link
Member

szmarczak commented Dec 19, 2019

  • Version: v13.5.0
  • Platform: Linux solus 5.4.1-137.current #1 SMP PREEMPT Sat Dec 7 15:17:22 UTC 2019 x86_64 GNU/Linux
  • Subsystem: stream, http
const http = require('http');
const {pipeline, PassThrough} = require('stream');

http.get('http://example.com', response => {
	const stream = new PassThrough();

	stream.on('error', error => {
		console.log('stream error', error);
	});

	pipeline(
		response,
		stream,
		error => {
			console.log('pipeline error', error);
		}
	);

	stream.destroy(new Error('oh no'));
}).on('error', error => {
	console.log('request error', error);
});

If you run the example above, the error will be emitted three times:

  • stream error (oh no)
  • pipeline error (oh no)
  • request error (oh no)

I did not expect the ClientRequest instance to throw.
It's because response.destroy(error) calls socket.destroy(error) directly.
ClientRequest catches the error and throws it again.

But why does it call response.destroy(error)?
Well, it happens because of 6480882 (previously it just called response.destroy()).

If you run the example in Node.js 12, you will get these results:

  • stream error (Cannot call write after a stream was destroyed)
  • pipeline error (Cannot call write after a stream was destroyed)
  • stream error (oh no)

I just want to say that the new behavior is correct, but ClientRequest should not throw if using pipeline with response.

@lpinca
Copy link
Member

lpinca commented Dec 19, 2019

See 6480882.

@lpinca
Copy link
Member

lpinca commented Dec 19, 2019

cc: @mcollina

@lpinca lpinca added http Issues or PRs related to the http subsystem. stream Issues and PRs related to the stream subsystem. labels Dec 19, 2019
@mcollina
Copy link
Member

mcollina commented Dec 19, 2019

Let's revert 6480882 and try again - I'm swamped, it would be great if somebody else took care of it.

@szmarczak
Copy link
Member Author

There's no need to revert that. I would just replace:

if (typeof stream.destroy === 'function') {
if (stream.req && stream._writableState === undefined) {
// This is a ClientRequest
// TODO(mcollina): backward compatible fix to avoid crashing.
// Possibly remove in a later semver-major change.
stream.req.on('error', noop);
}
return stream.destroy(err);
}

with

    if (isRequest(stream.req)) return stream.req.abort();
    if (typeof stream.destroy === 'function') return stream.destroy(err);

The same is already done if you provide ClientRequest directly
(perhaps you just missed that, no worries):

if (isRequest(stream)) return stream.abort();

@BridgeAR
Copy link
Member

Ping @ronag

@ronag
Copy link
Member

ronag commented Dec 20, 2019

Yes, res.destroy(err) propagating the error to req might be a bit weird, though I'm actually not sure what is "correct" here. I'm working on trying to clarify these cases.

For now I think @szmarczak suggestion makes sense. @szmarczak would you like to create a PR? I'll investigate later whether that would have any impact on what 6480882 was trying to fix.

@szmarczak
Copy link
Member Author

Sure, will do today.

ronag added a commit to nxtedition/node that referenced this issue Dec 21, 2019
destroy(err) on http response will propagate the error to the
request causing 'error' to be unexpectedly emitted. Furthermore,
response.destroy() unlike request.abort() does not _dump buffered
data.

Fixes a breaking change introduced in nodejs@6480882.

Prefer res.req.abort() over res.destroy() until this situation is
clarified.

Fixes: nodejs#31029
Refs: nodejs@6480882
@Trott Trott closed this as completed in c852f7e Dec 25, 2019
BridgeAR pushed a commit that referenced this issue Jan 3, 2020
destroy(err) on http response will propagate the error to the
request causing 'error' to be unexpectedly emitted. Furthermore,
response.destroy() unlike request.abort() does not _dump buffered
data.

Fixes a breaking change introduced in 6480882.

Prefer res.req.abort() over res.destroy() until this situation is
clarified.

Fixes: #31029
Refs: 6480882

PR-URL: #31054
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
targos pushed a commit to targos/node that referenced this issue Apr 25, 2020
destroy(err) on http response will propagate the error to the
request causing 'error' to be unexpectedly emitted. Furthermore,
response.destroy() unlike request.abort() does not _dump buffered
data.

Fixes a breaking change introduced in nodejs@6480882.

Prefer res.req.abort() over res.destroy() until this situation is
clarified.

Fixes: nodejs#31029
Refs: nodejs@6480882

PR-URL: nodejs#31054
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
MylesBorins pushed a commit that referenced this issue Aug 17, 2020
destroy(err) on http response will propagate the error to the
request causing 'error' to be unexpectedly emitted. Furthermore,
response.destroy() unlike request.abort() does not _dump buffered
data.

Fixes a breaking change introduced in 6480882.

Prefer res.req.abort() over res.destroy() until this situation is
clarified.

Fixes: #31029
Refs: 6480882

PR-URL: #31054
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
http Issues or PRs related to the http subsystem. stream Issues and PRs related to the stream subsystem.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants