You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
TCP socket won't call end() callback if wasn't connected #47322
'use strict';constnet=require('net');conststream=require('stream');functioncheck(stm){stm.on('finish',()=>console.log('finish'));stm.on('close',()=>console.log('close'));stm.end(()=>console.log('ended'));}// nothing is printedcheck(newnet.Socket());// 'finish' and 'ended' is printed// check(new stream.Writable());
How often does it reproduce? Is there a required condition?
always
What is the expected behavior? Why is that the expected behavior?
Expect at least 'ended' to show
What do you see instead?
nothing printed
Additional information
All streams seem to always call the callback from end() but non-connected sockets do not thus violating stream.Writable API.
Why it is needed: I use universal function to close stream gracefully and when it's done call destroy. With this behavior the function stucks at non-connected sockets. Currently I seem to have to check stream.writable and only call end when it's true.
Well, base Writable works as expected while Socket does not. So Socket violates its parent's behavior and thus makes impossible some flows dealing with generic streams.
You're comparing net.Socket to a stream.Writable but it's an instance of stream.Duplex - i.e., it's bidrectional, as @jazelly points out.
What's more, the stream-y part of the socket is never activated; no data goes in or out. It would be more surprising IMO if the stream events did fire.
Writable works as it is a unidirectional stream. It closes when you end one stream. But socket is bidirectional. Ending a socket only ends the writable part. It's still readable. This has been implied from the documentation on Socket.end().
You're comparing net.Socket to a stream.Writable but it's an instance of stream.Duplex - i.e., it's bidrectional, as @jazelly points out.
But stream.Duplex does produce 'finish' and 'ended' output!
I'm not insisting on events here, but I think if it is allowed to run a method with callback, it must be called always. Or just let it throw error.
Half-closes the socket. i.e., it sends a FIN packet. It is possible the server will still send some data.
And it has this to say on the callback argument:
callback {Function} Optional callback for when the socket is finished.
But the socket never finishes (because it is never started) so I feel it's unsurprising that the callback isn't called. For the same reason it's unsurprising no 'close' or 'finish' events are emitted.
That's my opinion. I'll keep this issue open for a few days and see if other maintainers feel differently.
The pending getter returns true in this case, as it simply regards any socket that is connecting or not handling data as pending, and defers the final call.
IMO it's better to differentiate the cases of pending, one is user has attempted to connnect while waiting for the connection, the other is this, constructed but never started the attempt.
Activity
jazelly commentedon Mar 30, 2023
Antonius-S commentedon Mar 31, 2023
Well, base Writable works as expected while Socket does not. So Socket violates its parent's behavior and thus makes impossible some flows dealing with generic streams.
jazelly commentedon Mar 31, 2023
bnoordhuis commentedon Mar 31, 2023
So, I don't think the behavior is too surprising.
You're comparing
net.Socketto astream.Writablebut it's an instance ofstream.Duplex- i.e., it's bidrectional, as @jazelly points out.What's more, the stream-y part of the socket is never activated; no data goes in or out. It would be more surprising IMO if the stream events did fire.
Antonius-S commentedon Mar 31, 2023
But stream.Duplex does produce 'finish' and 'ended' output!
I'm not insisting on events here, but I think if it is allowed to run a method with callback, it must be called always. Or just let it throw error.
bnoordhuis commentedon Mar 31, 2023
The documentation for
socket.end()says this:And it has this to say on the callback argument:
But the socket never finishes (because it is never started) so I feel it's unsurprising that the callback isn't called. For the same reason it's unsurprising no
'close'or'finish'events are emitted.That's my opinion. I'll keep this issue open for a few days and see if other maintainers feel differently.
ronag commentedon Apr 1, 2023
Based on a very quick look at this I think the callback should be called. I'll try to find time to dig into this.
jazelly commentedon Apr 3, 2023
What happens here is the
prefinish()check inWritable, which will not destroy a pending socket until it's connected.See here.
The
pendinggetter returns true in this case, as it simply regards any socket that isconnectingor not handling data as pending, and defers thefinalcall.IMO it's better to differentiate the cases of
pending, one is user has attempted to connnect while waiting for the connection, the other is this, constructed but never started the attempt.ronag commentedon Apr 3, 2023
@jazelly That sounds like a correct analysis. PR welcome!
net: only defer _final call when connecting