Repository navigation
net: handle.onread called again after UV_EOF #32487
Description
Activity
- addedlibuvIssues and PRs related to the libuv dependency or the uv binding.Issues and PRs related to the libuv dependency or the uv binding.netIssues and PRs related to the net subsystem.Issues and PRs related to the net subsystem.windowsIssues and PRs related to the Windows platform.Issues and PRs related to the Windows platform.
on Mar 25, 2020 The workaround in #31806 is to call
handle.readStop()afterUV_EOFwhich seems to resolve the issue.Yeah, this is … surprising and deserving of a comment in the source code, but it seems reasonable to me. It seems like a libuv bug to me, though. Without trying it myself, which arguments does .onread() receive after the UV_EOF call?
which arguments does .onread() receive after the UV_EOF call?
Unfortunately, I seem to be unable to connect to the VM so I can't try it anymore.
which arguments does .onread() receive after the UV_EOF call?
onreadinvokesonStreamReadinstream_base_commonswhich in turn has the following state in the call afterUV_EOF.{ nread: -4077, arrayBuffer: undefined }
That
nreaderror code isUV_ECONNRESETa.k.a.WSAECONNRESET.@bnoordhuis Would you consider that an libuv bug? Again, it only happens on win10. Should I raise an issue over there?
Only if it happens after
uv_close()has been called on the handle.Only if it happens after uv_close() has been called on the handle.
Then this is a bit out of my depth. It seems to happen after
uv_shutdownbut beforeuv_close. Having theonreadcallback invoked afterUV_EOFseems rather strange to me.There's an API contract that works like this:
- Libuv reports EOF to Node's read callback
- Node closes (or is supposed to close) the handle
2a. No new read events are generated
2b. Outstanding write and shutdown requests are cancelled withUV_ECANCELED(-4081)
If Node doesn't close the handle however, libuv tries to keep reading and that often results in
UV_ECONNRESET.The smart money is on Node failing to uphold its end of the contract, not libuv.
Just so I understand, if libuv reports EOF on the readable side, then no further writes are allowed? i.e. the handle can't be half open (writable but not readable)?
Libuv doesn't mandate what you can or cannot do but EOF usually means the other end has closed the connection (both ways.)
node seems to assume that EOF without closing is a valid option, https://nodejs.org/api/net.html#net_new_net_socket_options, see
allowHalfOpen.Actually, that doesn't make sense. Node does call destroy always on
'end'. ThoughallowHalfOpen: truedoes seem to be a strange option to allow, i.e. writing to aSocketafter the handle has been closed?This might be fixed now?
A continuation of https://github.com/nodejs/node/pull/31806/files#r382938926.
On the following platforms the
handle.onreadis invoked again afterUV_EOFunless the handle ishandle.close():ed in the same tick asUV_EOFoccurs.https://github.com/nodejs/node/blob/master/lib/internal/stream_base_commons.js#L163
https://github.com/nodejs/node/blob/master/lib/net.js#L242
The workaround in #31806 is to call
handle.readStop()afterUV_EOFwhich seems to resolve the issue.We didn't notice this previously since
handle.onreadis assigned a noop and/orhandle.closeis called, insideSocket._destroywhich in turn was invoked synchronously with'end'.I have a VM where this is easily reproducible.