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: make readable/writable start as true #32272

Closed
wants to merge 2 commits into from

Conversation

ronag
Copy link
Member

@ronag ronag commented Mar 14, 2020

net.Socket is slightly breaking stream invariants by having readable/writable going from false to true.

Streams assume that readable/writable either starts out as false and stay that way, or true and then goes to false through push(null)/end() after which it never goes back to true.

This PR changes 2 things:

Unless explicitly set to false through options:

  • starts as readable/writable true by default.
  • net.Socket does not manage/set readable/writable, that's internal/private
    stream state.
  • uses push(null)/end() to set readable/writable to false if required after connect. Note that this would cause the socket to emit the 'end'/'finish' events, which it did not do previously.

In the case it is explicitly set to false through options it never becomes true.

Currently something like this would fail:

// Uhoh, socket doesn't start as writable until connected? Different from regular streams...
const socket = net.connect(...);
while (socket.writable && chunks.length) {
  socket.write(await chunks.shift());
}
socket.end();
Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines

@ronag ronag added net Issues and PRs related to the net subsystem. semver-major PRs that contain breaking changes and should be released in the next major version. labels Mar 14, 2020
@ronag ronag requested review from jasnell and addaleax March 14, 2020 22:51
@nodejs-github-bot nodejs-github-bot added stream Issues and PRs related to the stream subsystem. tls Issues and PRs related to the tls subsystem. labels Mar 14, 2020
@ronag ronag force-pushed the net-writable-readbale2 branch 2 times, most recently from 64e26b8 to 33f143b Compare March 14, 2020 23:10
`net.Socket` is slightly breaking stream invariants by having readable/writable going from `false` to `true`. Streams assume that readable/writable starts out `true` and then goes to `false` through `push(null)`/`end()` after which it never goes back to `true`, e.g. once a stream is `writable == false` it is assumed it will never become `true`.

This PR changes 2 things:

Unless explicitly set to `false` through options:

- starts as `readable`/`writable` `true` by default.
- uses `push(null)`/`end()` to set `readable`/`writable` to `false`. Note that this would cause the socket to emit the `'end'`/`'finish'` events, which it did not do previously.

In the case it is explicitly set to `false` through `options` it is assumed to never become `true`.
// The readable flag is only needed if pauseOnCreate will be handled.
readable: tlsOptions.pauseOnConnect,
writable: false
manualStart: true
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What we actually want here is manualStart since we in the lines below actually do manually start it.

Copy link
Member Author

@ronag ronag Mar 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nodejs-github-bot
Copy link
Collaborator

@nodejs-github-bot
Copy link
Collaborator

@jasnell
Copy link
Member

jasnell commented Mar 16, 2020

So a key challenge here is that Streams have never really had a clearly defined semantic around "I'm writable/readable yes, but you can't write or read yet". With both http/2 and quic we've essentially had to hack our way around this by deferring write and read operations until a "ready" event occurs, which is rather messy. What would be ideal here is some kind of proper, performant notion of a ready status that allows writes to be internally buffered prior to the ready state, then passed through like normal after the ready state... that would allow us to avoid the deferral hacks and cork/uncork dance while we're waiting for the underlying socket and communication sessions to be established.

@jasnell
Copy link
Member

jasnell commented Mar 16, 2020

In other words, I'd prefer to have the underlying mechanism in stream.Writable, stream.Readable, and stream.Duplex addressed first then have these specific implementations build on that.

@ronag
Copy link
Member Author

ronag commented Mar 16, 2020

In other words, I'd prefer to have the underlying mechanism in stream.Writable, stream.Readable, and stream.Duplex addressed first then have these specific implementations build on that.

I totally agree we need something like that. But I'm unsure whether that should block this? We have a way that it "works" today, shouldn't we make it consistent first? It might take quite a while until we have such a mechanism you suggest.

@jasnell
Copy link
Member

jasnell commented Mar 16, 2020

I totally agree we need something like that. But I'm unsure whether that should block this? We have a way that it "works" today, shouldn't we make it consistent first? It might take quite a while until we have such a mechanism you suggest.

I'm not sure really. I really dislike the hacks we have in place now ;-) .... I won't block this PR waiting for the other mechanism but would definitely like to see work on the other progress.

@ronag
Copy link
Member Author

ronag commented Mar 16, 2020

I really dislike the hacks we have in place now ;-)

I agree, my point here though is to try and reduce the hacks... a little bit...

but would definitely like to see work on the other progress.

Definitely something I would like to look into in the future.

@ronag ronag requested a review from mcollina March 20, 2020 14:28
@ronag
Copy link
Member Author

ronag commented Mar 20, 2020

if this gets merged I think we could eventually deprecate Writable.writable and Readable.readable undocumented but public setters. Only Duplex constructor would need to be able to set them.

@nodejs-github-bot
Copy link
Collaborator

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just 2 nits.
It needs a close look of CITGM

lib/_stream_readable.js Show resolved Hide resolved
lib/_stream_writable.js Show resolved Hide resolved
@ronag
Copy link
Member Author

ronag commented Mar 20, 2020

@mcollina Added comments, PTAL

@ronag
Copy link
Member Author

ronag commented Mar 20, 2020

@ronag ronag added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Mar 20, 2020
Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@ronag
Copy link
Member Author

ronag commented Mar 20, 2020

CITGM looks good

@ronag
Copy link
Member Author

ronag commented Mar 23, 2020

needs another @nodejs/tsc approval

@nodejs-github-bot
Copy link
Collaborator

@ronag
Copy link
Member Author

ronag commented Mar 24, 2020

Landed in eeccd52

@ronag ronag closed this Mar 24, 2020
ronag added a commit that referenced this pull request Mar 24, 2020
`net.Socket` is slightly breaking stream invariants by
having readable/writable going from `false` to `true`.
Streams assume that readable/writable starts out `true`
and then goes to `false` through `push(null)`/`end()`
after which it never goes back to `true`, e.g. once a
stream is `writable == false` it is assumed it will
never become `true`.

This PR changes 2 things:

Unless explicitly set to `false` through options:

- starts as `readable`/`writable` `true` by default.
- uses `push(null)`/`end()` to set `readable`/`writable`
  to `false`. Note that this would cause the socket to
  emit the `'end'`/`'finish'` events, which it did not
  do previously.

In the case it is explicitly set to `false` through
options` it is assumed to never become `true`.

PR-URL: #32272
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: James M Snell <[email protected]>
lpinca added a commit to lpinca/node-https-proxy-agent that referenced this pull request May 16, 2020
If the socket is writable, a write is attempted and an
`ERR_SOCKET_CLOSED` error is emitted because the socket was never
connected.

Refs: nodejs/node#32272
TooTallNate pushed a commit to TooTallNate/proxy-agents that referenced this pull request Jul 28, 2020
If the socket is writable, a write is attempted and an
`ERR_SOCKET_CLOSED` error is emitted because the socket was never
connected.

Refs: nodejs/node#32272
adrianchu0120 added a commit to adrianchu0120/proxy-agents that referenced this pull request Aug 25, 2024
If the socket is writable, a write is attempted and an
`ERR_SOCKET_CLOSED` error is emitted because the socket was never
connected.

Refs: nodejs/node#32272
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
author ready PRs that have at least one approval, no pending requests for changes, and a CI started. net Issues and PRs related to the net subsystem. semver-major PRs that contain breaking changes and should be released in the next major version. stream Issues and PRs related to the stream subsystem. tls Issues and PRs related to the tls subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants