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

http2: fix endless loop when writing empty string #18924

Closed
wants to merge 3 commits into from

Conversation

addaleax
Copy link
Member

Alternative to #18673 that addresses the problem on the HTTP/2 level rather than the streams level itself. The test is taken from @XadillaX, whom I’d be very thankful if he could take a look at this :)

Refs: #18673
Fixes: #18169
Refs: https://github.com/nodejs/node/blob/v9.5.0/src/node_http2.cc#L1481-L1484
Refs: https://github.com/nodejs/node/blob/v9.5.0/lib/_http_outgoing.js#L659-L661

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Affected core subsystem(s)

http2

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. dont-land-on-v4.x http2 Issues or PRs related to the http2 subsystem. labels Feb 22, 2018
@apapirovski
Copy link
Member

Is there a way to standardize the behaviour between the various StreamBase implementations? As far as I can tell, the only place where we truly need these empty writes is TLSSocket#renegotiate...

(One side-effect of having a unique implementation of this logic for each StreamBase implementation is that http, for example, doesn't actually call the user-provided callback in the case of an empty write. That actually seems like a bug...)

@addaleax
Copy link
Member Author

@apapirovski I think the fact that TLS uses them makes “empty writes flush the underlying protocol” part of the StreamBase API, and that’s a meaningful thing to do in the context of HTTP/2.

I’d be open to changing that. In reality, TLS should probably work more like HTTP/2 and flush state automatically after making changes to the protocol state, like what we do with Http2Scope right now.

That actually seems like a bug

Yeah, but it’s part of the write() method in _http_outgoing.js – it’s not related to our native streams impl, is it? (I also thought somebody had opened a PR for that. But I can’t find it, so I guess I’m wrong?)

@apapirovski
Copy link
Member

Yeah, but it’s part of the write() method in _http_outgoing.js – it’s not related to our native streams impl, is it? (I also thought somebody had opened a PR for that. But I can’t find it, so I guess I’m wrong?)

It's not, just commenting that the way it works is kind of problematic. We probably can't do much about it now but the http implementation makes a lot of non-obvious state assumptions. Like it's not at all clear that doing an empty write won't send the headers or trigger any of the follow-up logic that a non-empty write triggers.

I’d be open to changing that. In reality, TLS should probably work more like HTTP/2 and flush state automatically after making changes to the protocol state, like what we do with Http2Scope right now.

Yeah, that would be nice. Anyway, it's a bit hard for me to find time to think about much of this right now but will revisit when I have a bit more time.


In relation to this PR, I don't know how I feel about it vs the earlier one. There seem to be trade-offs with both.

// This is done here so that .write('', cb) is still a meaningful way to
// find out when the HTTP2 stream wants to consume data, and because the
// StreamBase API allows empty input chunks.
while (!stream->queue_.empty() && stream->queue_.front().buf.len == 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

One question, if we remove the empty buffer and do nothing, will the header be sent?

Refs: https://github.com/nodejs/node/pull/18673/files#diff-696b2cc418addca5f3fe5020058f8b15R1622

Copy link
Member Author

Choose a reason for hiding this comment

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

@XadillaX Hm – I’m not sure I can wrap my head around everything, but this code here is only ever called by nghttp2 after the headers were sent anyway… does that answer your question?

So, yes, writing an empty buffer would trigger the headers to be sent, as I understand it.

@BridgeAR
Copy link
Member

BridgeAR commented Mar 2, 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 2, 2018
@addaleax
Copy link
Member Author

addaleax commented Mar 2, 2018

Landed in 9bc333c, and the test from @XadillaX in 16facd7.

Thanks for the reviews!

@addaleax addaleax closed this Mar 2, 2018
@addaleax addaleax removed the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Mar 2, 2018
@addaleax addaleax deleted the http2-fix-endless-loop branch March 2, 2018 11:50
@MylesBorins MylesBorins mentioned this pull request Mar 6, 2018
@MylesBorins MylesBorins mentioned this pull request May 2, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. http2 Issues or PRs related to the http2 subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants