-
Notifications
You must be signed in to change notification settings - Fork 30.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
Sockets in Node 10.0.0 and up are not destroyed if ended before connect #21268
Comments
I can only think of not changing the |
FWIW, I can confirm that adding a |
We have this issue open: #445 but I would propose a PR anyway and continue discussion there. If you have time please do that, otherwise I'll try to open it before the end of the week. |
@lpinca Is that more or less what you had in mind? |
Yes, that's exactly what I had in mind, thanks. |
Don't set `writable` to true when a socket connects if the socket is already in an ending state. In the existing code, afterConnect always set `writable` to true. This has been the case for a long time, but previous to commit 9b7a691, the socket would still be destroyed by `destroySoon` and emit a `'close'` event. Since that commit removed this masking behavior, we have relied on maybeDestroy to destroy the socket when the readble state is ended, and that won't happen if `writable` is set to true. If the socket has `allowHalfOpen` set to true, then `destroy` will still not be called and `'close'` will not be emitted. PR-URL: nodejs#21290 Fixes: nodejs#21268
Don't set `writable` to true when a socket connects if the socket is already in an ending state. In the existing code, afterConnect always set `writable` to true. This has been the case for a long time, but previous to commit 9b7a691, the socket would still be destroyed by `destroySoon` and emit a `'close'` event. Since that commit removed this masking behavior, we have relied on maybeDestroy to destroy the socket when the readble state is ended, and that won't happen if `writable` is set to true. If the socket has `allowHalfOpen` set to true, then `destroy` will still not be called and `'close'` will not be emitted. PR-URL: #21290 Fixes: #21268 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Beginning with commit 9b7a691, calling
.end
a Node.js socket before it is connected will fail to call.destroy
on the socket, so the socket will never emit aclose
event.Here is a minimal repro, provided by @lpinca in dicussion on #19241 (comment):
A test run illustrating the problem:
This occurs because when we fire end before the connect completes (a real-world example is a timeout that fires off an
end
on the connecting socket if it is slow) we get the ordering:Socket.end
from client code on timeout (calls down tostream.Duplex.prototype.end
, settingthis.writable
to false andthis._writableState.ending
to true)Socket.afterConnect
(setsthis.writable
to true when we connect without error)Socket.onReadableStreamEnd
(callsthis.end
)Socket.end
(callsstream.Duplex.prototype.end
)Writable.prototype.end
(does NOT callendWritable
becausethis._writableState.ending
is already true from the previous call tosocket.end
, sothis.writable
stays true)socket.maybeDestroy
(does not callsocket.destroy
becausesocket.writable
istrue
, sothis.destroy
is never called andclose
is never emitted)While this is not really a problem with 9b7a691, but rather an issue with afterConnect setting
writable
without any state checks, commit 9b7a691 removes the behavior that was masking the issue -- previous to that change we were callingdestroySoon
, which would call down todestroy
even with.writable
set to true.This is causing a problem for at least the fairly popular IORedis (redis/ioredis#633), both in theory and in practice, and it seems as though it warrants a look, I'd be surprised if that was the only thing that was depending on those sockets being destroyed and emitting
close
.I'm not sure what to propose as a good fix: I don't immediately see any internal state on the socket that looks appropriate for switching behavior in
afterConnect
, although I guess it peeks at the _writableState in a couple of places.The text was updated successfully, but these errors were encountered: