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: do not inherit the no-half-open enforcer #18974

Closed
wants to merge 2 commits into from

Conversation

lpinca
Copy link
Member

@lpinca lpinca commented Feb 24, 2018

Socket.prototype.destroySoon() is called as soon as UV_EOF is read
if the allowHalfOpen option is disabled. This already works as a
"no-half-open enforcer" so there is no need to inherit another from
stream.Duplex.

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
Affected core subsystem(s)

net

@nodejs-github-bot nodejs-github-bot added the net Issues and PRs related to the net subsystem. label Feb 24, 2018
@lpinca
Copy link
Member Author

lpinca commented Feb 24, 2018

@lpinca
Copy link
Member Author

lpinca commented Mar 4, 2018

Ping @nodejs/collaborators.

@lpinca
Copy link
Member Author

lpinca commented Mar 4, 2018

@lpinca lpinca added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Mar 4, 2018
lib/net.js Outdated
// There is already a "no-half-open enforcer" in place so there is no need to
// inherit it again from `Duplex`.
stream.Readable.call(this, options);
stream.Writable.call(this, options);
Copy link
Member

Choose a reason for hiding this comment

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

I am not really a fan of this … not calling the Duplex constructor seems like a bad idea in general, and it’s going to be an extra obstacle while working on merging all StreamBase JS wrappers…

Can we instead make the Duplex constructor not attach the event handler if options.allowHalfOpen is set? That would help all other Duplex streams as well…

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, after #18953 my idea was to override the allowHalfOpen option when calling the Duplex constructor in order to never inherit the listener but to do that the options object has to be copied. This was done for performance reasons.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm open to suggestions.

Copy link
Member

Choose a reason for hiding this comment

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

@lpinca Yeah, good point.

I don’t think we can get around that without making some kind of change to the Duplex API, but I would be okay with that.

The best I can come up with right now would be this:

  • Add
    Duplex.kHasHalfOpenEnforcer = Symbol('kHasHalfOpenEnforcer'); // or non-enumerable
    Duplex.prototype[Duplex.kHasHalfOpenEnforcer] = true;
    net.Socket.prototype[Duplex.kHasHalfOpenEnforcer] = false;
  • Check for that property in the Duplex constructor

Another approach might be to create an internal Duplex constructor that omits the listener addition, which could serve as a base class of the “real”, publicly exposed Duplex stream…

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not very happy with option 1, adapting the base class to the needs of the child seems kinda hackish. Will think a bit about the second approach.

@@ -267,7 +276,7 @@ function Socket(options) {
this._writableState.decodeStrings = false;

// default to *not* allowing half open sockets
this.allowHalfOpen = options && options.allowHalfOpen || false;
this.allowHalfOpen = options.allowHalfOpen || false;
Copy link
Member

Choose a reason for hiding this comment

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

… and, as I understand it, would allow us to get rid of this, if we just transform allowHalfOpen correctly to begin with?

Also, I don’t know what the motivation for this was, but I don’t get why the default is not allowing half-open connections.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is unrelated but options is always an object so there is no reason to see if it's truthy.

@addaleax addaleax removed the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Mar 4, 2018
@benjamingr
Copy link
Member

I'm fine with either the solution here or the one addaleax suggested.

@lpinca lpinca force-pushed the avoid/inheriting-onend-listener branch from 1d5513d to a1c475d Compare March 5, 2018 10:15
@lpinca
Copy link
Member Author

lpinca commented Mar 5, 2018

@addaleax @benjamingr PTAL when you have some time. I will reorganize commits if this is acceptable.

New CI: https://ci.nodejs.org/job/node-test-pull-request/13500/

@mcollina
Copy link
Member

mcollina commented Mar 5, 2018

I'm -1 on the overall approach. Why cannot it be implemented only in Duplex, and the duplicating code removed from Socket? We should strive for that ideally.

If we go with this approach, we must move all the defined properties from Duplex to DuplexBase, or re-implement them for Socket if that implementation would be different.

This goes against the generic intent of reducing how we rely on streams internals.

@lpinca
Copy link
Member Author

lpinca commented Mar 5, 2018

@mcollina read discussion in #18974 (comment)

net.Socket has its own "no-half-open" enforcer so we should not inherit it from Duplex even when the allowHalfOpen option is false. It should never be inherited as it's only useless overhead.

To do that we can set the allowHalfOpen option to true before calling the Duplex constructor but the options object must be copied. That degrades performance.

Workarounds:

  1. Original approach: dc18709
  2. @addaleax's suggestions: net: do not inherit the no-half-open enforcer #18974 (comment)

As said I'm open to suggestions.

@addaleax
Copy link
Member

addaleax commented Mar 5, 2018

@lpinca Matteo has a point … what’s the downside of removing the extra code in net.Socket and just using what Duplex provides?

@lpinca
Copy link
Member Author

lpinca commented Mar 5, 2018

That means doing the work twice, no? First to add the listener in the Duplex constructor and then to remove it unconditionally in the net.Socket constructor. I would rather copy the options object in that case.

@addaleax
Copy link
Member

addaleax commented Mar 5, 2018

@lpinca If we want to use the Duplex mechanism, why would we remove the listener in the net.Socket destructor?

@lpinca
Copy link
Member Author

lpinca commented Mar 5, 2018

Assume that we instantiate the net.Socket with the allowHalfOpen option set to false.

const socket = new Socket({ allowHalfOpen: false });

The Duplex constructor will add the no-half-open enforcer but we don't need it as the Socket already has its own no-half-open enforcer so we have to remove it in the Socket constructor.

@lpinca
Copy link
Member Author

lpinca commented Mar 5, 2018

Removing the net.Socket no-half-open enforcer in favor of the one provided by Duplex is not an option as they don't work in same way.

  1. The no-half-open enforcer in Duplex calls end() on next tick
  2. It doesn't call Socket.prototype.destroySoon().

Copy link
Member

@benjamingr benjamingr left a comment

Choose a reason for hiding this comment

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

Still LGTM and nicer now.

@mcollina mcollina added the semver-major PRs that contain breaking changes and should be released in the next major version. label Mar 5, 2018
@mcollina
Copy link
Member

mcollina commented Mar 5, 2018

The destroySoon() method uses this._writableState.finished https://github.com/nodejs/node/blob/master/lib/net.js#L535. We should aim to remove that (maybe even moving destroySoon() under streams?).

I'd be actually 👍 in improving the allowHalfOpen behavior in the Duplex so that this could be supported. I do not see a reason to have allowHalfOpen in Duplex at all if our own Socket  implementation (the quintessence of a Duplex) cannot use it.

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.

Making my -1 explicit and flagging it at as semvermajor, because socket instanceof Duplex would now return false.

@addaleax
Copy link
Member

addaleax commented Mar 5, 2018

because socket instanceof Duplex would now return false.

@mcollina That’s incorrect, it does return true.

lib/net.js Outdated
@@ -223,7 +224,9 @@ function Socket(options) {
else if (options === undefined)
options = {};

stream.Duplex.call(this, options);
// There is already a "no-half-open enforcer" in place so there is no need to
// inherit it again from `Duplex`.
Copy link
Member

Choose a reason for hiding this comment

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

Can you please add a note along the following lines: Socket is still inheriting from Duplex, but we are just using a slimmed down constructor. socket instanceOf Duplex would keep returning 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.

Will do.

Copy link
Member Author

Choose a reason for hiding this comment

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

@mcollina updated, PTAL.

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 with a nit.

@lpinca lpinca removed the semver-major PRs that contain breaking changes and should be released in the next major version. label Mar 5, 2018
@lpinca lpinca force-pushed the avoid/inheriting-onend-listener branch from a1c475d to a40a803 Compare March 5, 2018 15:48
@lpinca lpinca force-pushed the avoid/inheriting-onend-listener branch from a40a803 to e530c3f Compare March 5, 2018 20:14
@BridgeAR
Copy link
Member

BridgeAR commented Mar 6, 2018

@BridgeAR BridgeAR added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Mar 6, 2018
lpinca added 2 commits March 7, 2018 10:33
Add ability to subclass `stream.Duplex` without inheriting the
"no-half-open enforcer" regardless of the value of the `allowHalfOpen`
option.
`Socket.prototype.destroySoon()` is called as soon as `UV_EOF` is read
if the `allowHalfOpen` option is disabled. This already works as a
"no-half-open enforcer" so there is no need to inherit another from
`stream.Duplex`.
@lpinca lpinca force-pushed the avoid/inheriting-onend-listener branch from e530c3f to 5b73a79 Compare March 7, 2018 10:12
@lpinca
Copy link
Member Author

lpinca commented Mar 7, 2018

Rebased and added a new test.
New CI: https://ci.nodejs.org/job/node-test-pull-request/13564/

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

lpinca added a commit that referenced this pull request Mar 7, 2018
Add ability to subclass `stream.Duplex` without inheriting the
"no-half-open enforcer" regardless of the value of the `allowHalfOpen`
option.

PR-URL: #18974
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Chen Gang <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
lpinca added a commit that referenced this pull request Mar 7, 2018
`Socket.prototype.destroySoon()` is called as soon as `UV_EOF` is read
if the `allowHalfOpen` option is disabled. This already works as a
"no-half-open enforcer" so there is no need to inherit another from
`stream.Duplex`.

PR-URL: #18974
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Chen Gang <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
@lpinca
Copy link
Member Author

lpinca commented Mar 7, 2018

Landed in 42e9b48...4e86f9b.

@lpinca lpinca closed this Mar 7, 2018
@lpinca lpinca deleted the avoid/inheriting-onend-listener branch March 7, 2018 15:07
@targos targos removed the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Mar 17, 2018
targos pushed a commit that referenced this pull request Mar 17, 2018
Add ability to subclass `stream.Duplex` without inheriting the
"no-half-open enforcer" regardless of the value of the `allowHalfOpen`
option.

PR-URL: #18974
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Chen Gang <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
targos pushed a commit that referenced this pull request Mar 17, 2018
`Socket.prototype.destroySoon()` is called as soon as `UV_EOF` is read
if the `allowHalfOpen` option is disabled. This already works as a
"no-half-open enforcer" so there is no need to inherit another from
`stream.Duplex`.

PR-URL: #18974
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Chen Gang <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
@targos targos mentioned this pull request Mar 18, 2018
MylesBorins pushed a commit that referenced this pull request Mar 20, 2018
Add ability to subclass `stream.Duplex` without inheriting the
"no-half-open enforcer" regardless of the value of the `allowHalfOpen`
option.

PR-URL: #18974
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Chen Gang <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
MylesBorins pushed a commit that referenced this pull request Mar 20, 2018
`Socket.prototype.destroySoon()` is called as soon as `UV_EOF` is read
if the `allowHalfOpen` option is disabled. This already works as a
"no-half-open enforcer" so there is no need to inherit another from
`stream.Duplex`.

PR-URL: #18974
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Chen Gang <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
MayaLekova pushed a commit to MayaLekova/node that referenced this pull request May 8, 2018
Add ability to subclass `stream.Duplex` without inheriting the
"no-half-open enforcer" regardless of the value of the `allowHalfOpen`
option.

PR-URL: nodejs#18974
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Chen Gang <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
MayaLekova pushed a commit to MayaLekova/node that referenced this pull request May 8, 2018
`Socket.prototype.destroySoon()` is called as soon as `UV_EOF` is read
if the `allowHalfOpen` option is disabled. This already works as a
"no-half-open enforcer" so there is no need to inherit another from
`stream.Duplex`.

PR-URL: nodejs#18974
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Chen Gang <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
@jasnell
Copy link
Member

jasnell commented Aug 17, 2018

Should this be backported to 8.x? If so, a separate backport PR is needed

@lpinca
Copy link
Member Author

lpinca commented Aug 18, 2018

I would skip this and eventually backport #19779 which is basically a revert of this one, but #19779 depends on #18953. Let me know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
net Issues and PRs related to the net subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants