Skip to content

net: do not inherit the no-half-open enforcer - #18974

Closed
lpinca wants to merge 2 commits into
nodejs:masterfrom
lpinca:avoid/inheriting-onend-listener
Closed

net: do not inherit the no-half-open enforcer#18974
lpinca wants to merge 2 commits into
nodejs:masterfrom
lpinca:avoid/inheriting-onend-listener

Conversation

@lpinca

Copy link
Copy Markdown
Member

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-botnodejs-github-bot added the net Issues and PRs related to the net subsystem. label Feb 24, 2018
@lpinca

Copy link
Copy Markdown
MemberAuthor

@lpinca

Copy link
Copy Markdown
MemberAuthor

Ping @nodejs/collaborators.

@lpinca

Copy link
Copy Markdown
MemberAuthor

@lpincalpinca added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Mar 4, 2018
Comment threadlib/net.js Outdated

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

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
Copy Markdown
MemberAuthor

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
Copy Markdown
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-enumerableDuplex.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
Copy Markdown
MemberAuthor

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.

Comment threadlib/net.js Outdated

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

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.

@addaleaxaddaleax 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
Copy Markdown
Member

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

@lpinca
lpincaforce-pushed the avoid/inheriting-onend-listener branch from 1d5513d to a1c475dCompareMarch 5, 2018 10:15
@lpinca

lpinca commented Mar 5, 2018

Copy link
Copy Markdown
MemberAuthor

@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
Copy Markdown
Member

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
Copy Markdown
MemberAuthor

@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
Copy Markdown
Member

@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
Copy Markdown
MemberAuthor

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
Copy Markdown
Member

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

@lpinca

Copy link
Copy Markdown
MemberAuthor

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

constsocket=newSocket({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

lpinca commented Mar 5, 2018

Copy link
Copy Markdown
MemberAuthor

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().

@benjamingrbenjamingr left a comment

Copy link
Copy Markdown
Member

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.

@mcollinamcollina 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
Copy Markdown
Member

The destroySoon() method uses this._writableState.finishedhttps://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.

@mcollinamcollina left a comment

Copy link
Copy Markdown
Member

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
Copy Markdown
Member

because socket instanceof Duplex would now return false.

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

Comment threadlib/net.js Outdated

Copy link
Copy Markdown
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
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Will do.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

@mcollina updated, PTAL.

@mcollinamcollina left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM with a nit.

@lpincalpinca removed the semver-major PRs that contain breaking changes and should be released in the next major version. label Mar 5, 2018
@lpinca
lpincaforce-pushed the avoid/inheriting-onend-listener branch from a1c475d to a40a803CompareMarch 5, 2018 15:48
@lpinca
lpincaforce-pushed the avoid/inheriting-onend-listener branch from a40a803 to e530c3fCompareMarch 5, 2018 20:14
@BridgeAR

Copy link
Copy Markdown
Member

@BridgeARBridgeAR 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
lpincaforce-pushed the avoid/inheriting-onend-listener branch from e530c3f to 5b73a79CompareMarch 7, 2018 10:12
@lpinca

Copy link
Copy Markdown
MemberAuthor

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

@mcollinamcollina left a comment

Copy link
Copy Markdown
Member

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 <benjamingr@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Chen Gang <gangc.cxy@foxmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
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 <benjamingr@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Chen Gang <gangc.cxy@foxmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
@lpinca

Copy link
Copy Markdown
MemberAuthor

Landed in 42e9b48...4e86f9b.

@lpincalpinca closed this Mar 7, 2018
@lpinca
lpinca deleted the avoid/inheriting-onend-listener branch March 7, 2018 15:07
@targostargos 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 <benjamingr@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Chen Gang <gangc.cxy@foxmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
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 <benjamingr@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Chen Gang <gangc.cxy@foxmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
@targostargos 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 <benjamingr@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Chen Gang <gangc.cxy@foxmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
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 <benjamingr@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Chen Gang <gangc.cxy@foxmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
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 <benjamingr@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Chen Gang <gangc.cxy@foxmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
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 <benjamingr@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Chen Gang <gangc.cxy@foxmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
@jasnell

Copy link
Copy Markdown
Member

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

@lpinca

Copy link
Copy Markdown
MemberAuthor

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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

netIssues and PRs related to the net subsystem.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants

@lpinca@benjamingr@mcollina@addaleax@BridgeAR@jasnell@MoonBall@targos@nodejs-github-bot