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

net: handle.onread called again after UV_EOF #32487

Closed
ronag opened this issue Mar 25, 2020 · 14 comments
Closed

net: handle.onread called again after UV_EOF #32487

ronag opened this issue Mar 25, 2020 · 14 comments
Labels
libuv Issues and PRs related to the libuv dependency or the uv binding. net Issues and PRs related to the net subsystem. windows Issues and PRs related to the Windows platform.

Comments

@ronag
Copy link
Member

ronag commented Mar 25, 2020

A continuation of https://github.com/nodejs/node/pull/31806/files#r382938926.

On the following platforms the handle.onread is invoked again after UV_EOF unless the handle is handle.close():ed in the same tick as UV_EOF occurs.

  • win10-COMPILED_BY-vs2019
  • win2012r2-COMPILED_BY-vs2019-x86

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() after UV_EOF which seems to resolve the issue.

We didn't notice this previously since handle.onread is assigned a noop and/or handle.close is called, inside Socket._destroy which in turn was invoked synchronously with 'end'.

I have a VM where this is easily reproducible.

@ronag
Copy link
Member Author

ronag commented Mar 25, 2020

@jasnell @addaleax, the PR could land without fixing this so I'm making a separate issue so that we don't lose track of it. Might be worth digging further into.

This was referenced Mar 25, 2020
@addaleax addaleax added libuv Issues and PRs related to the libuv dependency or the uv binding. net Issues and PRs related to the net subsystem. windows Issues and PRs related to the Windows platform. labels Mar 25, 2020
@addaleax
Copy link
Member

The workaround in #31806 is to call handle.readStop() after UV_EOF which 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?

@ronag
Copy link
Member Author

ronag commented Mar 25, 2020

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.

@ronag
Copy link
Member Author

ronag commented Mar 26, 2020

@addaleax

which arguments does .onread() receive after the UV_EOF call?

onread invokes onStreamRead in stream_base_commons which in turn has the following state in the call after UV_EOF.

{ nread: -4077, arrayBuffer: undefined }

@bnoordhuis
Copy link
Member

That nread error code is UV_ECONNRESET a.k.a. WSAECONNRESET.

@ronag
Copy link
Member Author

ronag commented Mar 26, 2020

@bnoordhuis Would you consider that an libuv bug? Again, it only happens on win10. Should I raise an issue over there?

@bnoordhuis
Copy link
Member

Only if it happens after uv_close() has been called on the handle.

@ronag
Copy link
Member Author

ronag commented Mar 26, 2020

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_shutdown but before uv_close. Having the onread callback invoked after UV_EOF seems rather strange to me.

@bnoordhuis
Copy link
Member

There's an API contract that works like this:

  1. Libuv reports EOF to Node's read callback
  2. Node closes (or is supposed to close) the handle
    2a. No new read events are generated
    2b. Outstanding write and shutdown requests are cancelled with UV_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.

@ronag
Copy link
Member Author

ronag commented Mar 26, 2020

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)?

@bnoordhuis
Copy link
Member

Libuv doesn't mandate what you can or cannot do but EOF usually means the other end has closed the connection (both ways.)

@ronag
Copy link
Member Author

ronag commented Mar 26, 2020

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.

@ronag
Copy link
Member Author

ronag commented Mar 26, 2020

Actually, that doesn't make sense. Node does call destroy always on 'end'. Though allowHalfOpen: true does seem to be a strange option to allow, i.e. writing to a Socket after the handle has been closed?

@vtjnash
Copy link
Contributor

vtjnash commented Mar 10, 2022

This might be fixed now?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libuv Issues and PRs related to the libuv dependency or the uv binding. net Issues and PRs related to the net subsystem. windows Issues and PRs related to the Windows platform.
Projects
None yet
Development

No branches or pull requests

5 participants