-
Notifications
You must be signed in to change notification settings - Fork 29.1k
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
socket: don't emit premature 'close' #20745
Conversation
cc: @mafintosh |
@nodejs/streams @nodejs/http2 @mcollina @apapirovski PTAL |
This is a bit difficult to implement currently though, since close is emitted by the base class and there is currently no good way to do this without overriding |
Related, #20755 |
This comment has been minimized.
This comment has been minimized.
Sorry to have to ask but this feels like something that could cause quite subtle changes in behaviour - I can’t figure out whether this shipped in node 10, but another related change was previously reverted. If it has shipped, should the original commit be reverted for the 10.2 release? |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
25a6f51
to
be6aee8
Compare
This comment has been minimized.
This comment has been minimized.
d373e29
to
002c1a0
Compare
Correct, I missed this commit 5e3f516. Can you please update the PR description?
No, it is emitted when the handle is closed.
The callback of To summarise, only the following points are valid:
I think both of them are expected and work as intended. |
@lpinca what I'm still unsure about is since |
f39a17d
to
e112cdd
Compare
That doesn't seem to happen, it is hopefully covered by tests and if not it would have already been reported. |
lib/net.js
Outdated
const onClosed = () => { | ||
debug('emit close'); | ||
if (exception) { | ||
this.emit('error', exception) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I'm not wrong this will emit an error every time socket.destroy()
is called with an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That will happen through cb(exception)
as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, you are right, this is quite complicated...
22d7871
to
6fca676
Compare
@ronag @lpinca thanks both very much for continuing to dig into this. The reason I somewhat pushed back a bit was I remember many times we've have to |
e57f613
to
347fe56
Compare
347fe56
to
22b4ab7
Compare
I'm not going to block this but I'm not very happy with removing the |
@@ -583,33 +581,33 @@ Socket.prototype._destroy = function(exception, cb) { | |||
clearTimeout(s[kTimeout]); | |||
} | |||
|
|||
debug('close'); | |||
const onClosed = () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't test but I think this still does not prevent the 'error'
event from being emitted multiple times. Assume that socket.destroy(err)
is called twice.
First call will wait for this callback to be called and emit 'error'
and 'close'
when that happens.
Second call will find _readableState.destroyed
set to true
but _writableState.errorEmitted
is not set to true
yet as that will happen when this callback if invoked, so another 'error'
event will be emitted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But isn't that true for any stream? If you call destroy twice with error it will emit error twice... I would say the problem is in the destroy impl then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it's emitted only once
const { Writable } = require('stream');
const w = new Writable();
w.on('error', console.log);
w.destroy(new Error('Oops'));
w.destroy(new Error('Oops'));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Twice like this...
const { Writable } = require('stream');
const w = new Writable({
destroy (err, cb) {
process.nextTick(() => cb(err))
}
});
w.on('error', console.log);
w.destroy(new Error('Oops'));
w.destroy(new Error('Oops'));
it's only once if you call cb
synchronously. Which doesn't seem like the usual case or we wouldn't have a callback?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The callback on destroy is not a public api. It is used only internally.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, sorry, I was referring to https://nodejs.org/api/stream.html#stream_writable_destroy_error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(We should fix that error being emitted more than once in streams imo.)
Alternatively, I would suggest then that the I'm mostly concerned about the unexpected difference in behavior. I've had bugs in my own code due to this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm -1 to land this change right now. I prefer we land this after some of the refactoring in streams described in #20096.
Anyway, a unit test would be really handy.
Given the -1's and the lack of progress... closing this one for now. |
I'm not sure how to get this to work properly nor how to create a test for it but I think there is an issue here to fix.
Looking at the current
net.Socket
implementation I see a few potential problems:'close'
can be emitted twice. Once from https://github.com/nodejs/node/blob/master/lib/net.js#L597 and once from https://github.com/nodejs/node/blob/master/lib/net.js#L604. Once withhadError
argument and once without.EDIT: Doesn't emit
close
if!_handle
.EDIT:
_server._emitCloseIfDrained
before socket is closed.'close'
can be prematurely emitted before the handle is actually closed.'close'
has an argument which doesn't conform with the streams spec.if the handle calls the callback synchronously then'close'
is emitted whiledestroyed !== false
.Some user land code expect
'close'
to have no arguments, e.g.Also, I believe the spec for stream events says that
'close'
has no arguments?Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes