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

http: fix no dumping after maybeReadMore #7211

Closed
wants to merge 2 commits into from

Conversation

indutny
Copy link
Member

@indutny indutny commented Jun 8, 2016

Checklist
  • make -j4 test (UNIX) or vcbuild test nosign (Windows) passes
  • a test and/or benchmark is included
  • documentation is changed or added
  • the commit message follows commit guidelines
Affected core subsystem(s)

http

Description of change

When maybeReadMore kicks in on a first bytes of incoming data, the
req.read(0) will be invoked and the req._consuming will be set to
true. This seemingly harmless property leads to a dire consequences:
the server won't call req._dump() and the whole HTTP/1.1 pipeline will
hang (single connection).

When `maybeReadMore` kicks in on a first bytes of incoming data, the
`req.read(0)` will be invoked and the `req._consuming` will be set to
`true`. This seemingly harmless property leads to a dire consequences:
the server won't call `req._dump()` and the whole HTTP/1.1 pipeline will
hang (single connection).
@nodejs-github-bot nodejs-github-bot added the http Issues or PRs related to the http subsystem. label Jun 8, 2016
@indutny
Copy link
Member Author

indutny commented Jun 8, 2016

cc @nodejs/http @bnoordhuis

@indutny
Copy link
Member Author

indutny commented Jun 8, 2016

@rvagg
Copy link
Member

rvagg commented Jun 8, 2016

lgtm

Jenkins drama on one of the ARM machines, I can't decipher that unfortunately.

Marked for backporting to v0.12 as it suffers from the same problem and we should get on top of this there (it's close to critical but not quite). I don't think 0.10 does but can't verify because of the lack of flushHeaders(). @indutny do you have time to figure out an alternative test for v0.10? Is it good enough to just remove flushHeaders() from the test?

@indutny
Copy link
Member Author

indutny commented Jun 8, 2016

@rvagg I guess it can do res.write('') or something like this should make it work.

@indutny
Copy link
Member Author

indutny commented Jun 8, 2016

cc @nodejs/collaborators just in case.

@ronkorving
Copy link
Contributor

When was this issue introduced?

@rvagg
Copy link
Member

rvagg commented Jun 8, 2016

market as lts-watch-v0.10, will defer the backporting pain for now

@rvagg
Copy link
Member

rvagg commented Jun 8, 2016

Confirmed that v0.10 exhibits the same behaviour but res.write('') or even res._send(new Buffer(0)) doesn't make the test pass, so we're going to need some help with v0.10 @indutny

Also, can you explore a way to reduce the time it takes for this test to fail instead of relying on a socket timeout? or perhaps we can shorten the socket timeout? Would req.setTimeout(100) have a negative impact on the reliability of the test?

@indutny
Copy link
Member Author

indutny commented Jun 8, 2016

@ronkorving I think at the introduction of Streams2.

@@ -67,6 +69,8 @@ IncomingMessage.prototype.setTimeout = function(msecs, callback) {


IncomingMessage.prototype.read = function(n) {
if (!this._consuming)
this._readableState.readingMore = false;
Copy link
Member

Choose a reason for hiding this comment

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

Out of curiosity, would there be a behavioral difference if you dropped the if statement and wrote it as this._readableState.readingMore = this._consuming;?

Also, this could use a comment explaining why it's necessary to fiddle with this._readableState and why this._consuming should be true when monkey-patching this.read..

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I believe it would make a difference. _consuming can flip only into true, and can't really go back. This is a hack to detect following scenario:

http.createServer((req, res) => {
  res.end('123');
});

Clearly in this case no one has attempted to read from req. Thus _consuming is false here, and in _http_server.js the request can be _dumped. However, initial version (prior to this patch) of this hack wasn't working as expected, because _stream_readable.js may call .read() by itself. Luckily it calls it only from maybeReadMore, so we could test it here.

Copy link
Member

Choose a reason for hiding this comment

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

Okay, does that mean that in practice this._readableState.readingMore can be false when this._consuming is true?

A comment in the source explaining all that would definitely be appreciated. :-)

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, it can be false. _stream_readable.js flips it on and off.

@indutny
Copy link
Member Author

indutny commented Jun 13, 2016

@bnoordhuis how about this? ^

@bnoordhuis
Copy link
Member

LGTM but all this stream state hacking makes it very brittle, evidently so.

@mcollina
Copy link
Member

LGTM, and I agree with @bnoordhuis.

@indutny
Copy link
Member Author

indutny commented Jun 15, 2016

I don't have any better ideas, though :(

indutny added a commit that referenced this pull request Jun 15, 2016
When `maybeReadMore` kicks in on a first bytes of incoming data, the
`req.read(0)` will be invoked and the `req._consuming` will be set to
`true`. This seemingly harmless property leads to a dire consequences:
the server won't call `req._dump()` and the whole HTTP/1.1 pipeline will
hang (single connection).

PR-URL: #7211
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
@indutny
Copy link
Member Author

indutny commented Jun 15, 2016

@rvagg sorry, your LGTM was lowercase, didn't see it! Landed in 6842aa7, thank you everyone!

@indutny indutny closed this Jun 15, 2016
@indutny indutny deleted the fix/http-no-read-no-dump branch June 15, 2016 16:50
@MylesBorins
Copy link
Contributor

@indutny it would appear the fix landed in 357f904 as opposed to 6842aa7

Just want to make sure that is what you expected

@indutny
Copy link
Member Author

indutny commented Jun 15, 2016

Yikes, my mistake. 6842 was a local one that I cherry-picked, sorry about this!

evanlucas pushed a commit that referenced this pull request Jun 16, 2016
When `maybeReadMore` kicks in on a first bytes of incoming data, the
`req.read(0)` will be invoked and the `req._consuming` will be set to
`true`. This seemingly harmless property leads to a dire consequences:
the server won't call `req._dump()` and the whole HTTP/1.1 pipeline will
hang (single connection).

PR-URL: #7211
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
evanlucas added a commit that referenced this pull request Jun 16, 2016
Notable changes:

* **http**:
  - When maybeReadMore kicks in on a first bytes of incoming data, the
    req.read(0) will be invoked and the `req._consuming` will be set to
    true. This seemingly harmless property leads to a dire consequences:
    the server won't call `req._dump()` and the whole HTTP/1.1 pipeline
    will hang (single connection). (Fedor Indutny) [#7211](#7211)
  - When freeing the socket to be reused in keep-alive Agent wait for
    both prefinish and end events. Otherwise the next request may be
    written before the previous one has finished sending the body, leading
    to a parser errors. (Fedor Indutny) [#7149](#7149)
* **npm**: upgrade npm to 3.9.5 (Kat Marchán) [#7139](#7139)
@evanlucas evanlucas mentioned this pull request Jun 16, 2016
evanlucas added a commit that referenced this pull request Jun 16, 2016
Notable changes:

* **http**:
  - req.read(0) could cause incoming connections to stall and time out
    under certain conditions. (Fedor Indutny) [#7211](#7211)
  - When freeing the socket to be reused in keep-alive Agent wait for
    both prefinish and end events. Otherwise the next request may be
    written before the previous one has finished sending the body, leading
    to a parser errors. (Fedor Indutny) [#7149](#7149)
* **npm**: upgrade npm to 3.9.5 (Kat Marchán) [#7139](#7139)

#7323
evanlucas added a commit that referenced this pull request Jun 17, 2016
Notable changes:

* **http**:
  - req.read(0) could cause incoming connections to stall and time out
    under certain conditions. (Fedor Indutny) [#7211](#7211)
  - When freeing the socket to be reused in keep-alive Agent wait for
    both prefinish and end events. Otherwise the next request may be
    written before the previous one has finished sending the body, leading
    to a parser errors. (Fedor Indutny) [#7149](#7149)
* **npm**: upgrade npm to 3.9.5 (Kat Marchán) [#7139](#7139)

#7323
evanlucas added a commit that referenced this pull request Jun 17, 2016
Notable changes:

* **http**:
  - req.read(0) could cause incoming connections to stall and time out
    under certain conditions. (Fedor Indutny) [#7211](#7211)
  - When freeing the socket to be reused in keep-alive Agent wait for
    both prefinish and end events. Otherwise the next request may be
    written before the previous one has finished sending the body, leading
    to a parser errors. (Fedor Indutny) [#7149](#7149)
* **npm**: upgrade npm to 3.9.5 (Kat Marchán) [#7139](#7139)

PR-URL: #7323
MylesBorins pushed a commit that referenced this pull request Jul 11, 2016
When `maybeReadMore` kicks in on a first bytes of incoming data, the
`req.read(0)` will be invoked and the `req._consuming` will be set to
`true`. This seemingly harmless property leads to a dire consequences:
the server won't call `req._dump()` and the whole HTTP/1.1 pipeline will
hang (single connection).

PR-URL: #7211
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
MylesBorins pushed a commit that referenced this pull request Jul 12, 2016
When `maybeReadMore` kicks in on a first bytes of incoming data, the
`req.read(0)` will be invoked and the `req._consuming` will be set to
`true`. This seemingly harmless property leads to a dire consequences:
the server won't call `req._dump()` and the whole HTTP/1.1 pipeline will
hang (single connection).

PR-URL: #7211
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
MylesBorins pushed a commit that referenced this pull request Jul 12, 2016
When `maybeReadMore` kicks in on a first bytes of incoming data, the
`req.read(0)` will be invoked and the `req._consuming` will be set to
`true`. This seemingly harmless property leads to a dire consequences:
the server won't call `req._dump()` and the whole HTTP/1.1 pipeline will
hang (single connection).

PR-URL: #7211
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
@MylesBorins MylesBorins mentioned this pull request Jul 12, 2016
MylesBorins pushed a commit that referenced this pull request Jul 14, 2016
When `maybeReadMore` kicks in on a first bytes of incoming data, the
`req.read(0)` will be invoked and the `req._consuming` will be set to
`true`. This seemingly harmless property leads to a dire consequences:
the server won't call `req._dump()` and the whole HTTP/1.1 pipeline will
hang (single connection).

PR-URL: #7211
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
MylesBorins pushed a commit that referenced this pull request Jul 14, 2016
When `maybeReadMore` kicks in on a first bytes of incoming data, the
`req.read(0)` will be invoked and the `req._consuming` will be set to
`true`. This seemingly harmless property leads to a dire consequences:
the server won't call `req._dump()` and the whole HTTP/1.1 pipeline will
hang (single connection).

PR-URL: #7211
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
http Issues or PRs related to the http subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants