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: refactor Http2Stream and inbound headers #16676

Closed
wants to merge 6 commits into from

Conversation

jasnell
Copy link
Member

@jasnell jasnell commented Nov 2, 2017

  1. Eliminate pooling for Nghttp2Stream instances
  2. Use a MaybeStackBuffer for inbound headers
  3. Enforce a maximum number of headers per block
  4. Enforce a maximum number of header octets per block using MAX_HEADERS_LIST setting
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)

http2

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. labels Nov 2, 2017
@mscdex mscdex added http2 Issues or PRs related to the http2 subsystem. and removed lib / src Issues and PRs related to general changes in the lib or src directory. labels Nov 2, 2017
@jasnell
Copy link
Member Author

jasnell commented Nov 2, 2017

of header list that will be accepted. There is no default value. The minimum
allowed value is 0. The maximum allowed value is 2<sup>32</sup>-1.
of header list that will be accepted. The minimum allowed value is 0. The
maximum allowed value is 2<sup>32</sup>-1. **Default:** 65535.
Copy link
Member

Choose a reason for hiding this comment

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

we should probably update the changelog in the docs somehow for this.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point

uint32_t maxHeaderListSize = session->GetMaxHeaderPairs();
if (maxHeaderListSize == 0)
maxHeaderListSize = DEFAULT_MAX_HEADER_LIST_PAIRS;
current_headers_.AllocateSufficientStorage(maxHeaderListSize);
Copy link
Member

Choose a reason for hiding this comment

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

does this mean we are preallocating an array of maxHeaderListSize? Do we need this to be 65535 as the default? IMHO we are wasting memory and time for each 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.

There are two limits: the max number of header pairs, and the max number of header octets. We are preallocating an array of nhgttp2_header structs. These consist of two pointers and an 8 bit flag. Current number is a generous 1024, which can likely be tuned much much lower. The 65535 is the total number of octets and is maintained as a size_t counter only. There's no preallocation for that.

We should be able to tune the number of header pairs down to something much much smaller than 1024.

Copy link
Member

Choose a reason for hiding this comment

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

I would go for 128

Copy link
Member

Choose a reason for hiding this comment

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

If you’re calling AllocateSufficientStorage(), you kind of shouldn’t be using a MaybeStackBuffer in the first place – all this will give you is unused memory inside the Nghttp2Stream instance.

I’d suggest just going with a std::vector here … you don’t need to care about pre-allocating memory in that case, plus you can still reserve memory ahead of time using e.g. vector.reserve(128).

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 keep going back and forth on this, just playing with the various options to see which is going to provide the best performance. std::vector is likely the best approach based on everything I've seen.

// block is 1024, including the HTTP pseudo-header fields, by
// setting one here, no request should be acccepted because it
// does not allow for even the basic HTTP pseudo-headers
const server = http2.createServer({ maxHeaderListPairs: 1 });
Copy link
Member

Choose a reason for hiding this comment

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

I think these types of settings should throw. What's the minimum?

Copy link
Member Author

Choose a reason for hiding this comment

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

We can set whatever minimum we want here. For the request to work, we need at least 4 to account for the required pseudoheaders.

Copy link
Member

Choose a reason for hiding this comment

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

I would say we validate it's greater than 4.

Copy link
Member Author

Choose a reason for hiding this comment

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

how about rather than report an error, we just automatically allow for the number of pseudo-headers? So if a user sets 0, the internal value is 4....

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 the default changed.

buffer[IDX_SETTINGS_COUNT] =
(1 << IDX_SETTINGS_HEADER_TABLE_SIZE) |
(1 << IDX_SETTINGS_ENABLE_PUSH) |
(1 << IDX_SETTINGS_INITIAL_WINDOW_SIZE) |
(1 << IDX_SETTINGS_MAX_FRAME_SIZE);
(1 << IDX_SETTINGS_MAX_FRAME_SIZE) |
(1 << IDX_SETTINGS_MAX_HEADER_LIST_SIZE);
Copy link
Member

Choose a reason for hiding this comment

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

btw, this is … clever 😄

while (!headers->empty() && j < arraysize(argv) / 2) {
nghttp2_header item = headers->front();
while (count > 0 && j < arraysize(argv) / 2) {
nghttp2_header item = headers[n++];
Copy link
Member

Choose a reason for hiding this comment

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

Can’t this just be headers[--count]? I don’t think you really need the n if you didn’t need it before

uint32_t maxHeaderListSize = session->GetMaxHeaderPairs();
if (maxHeaderListSize == 0)
maxHeaderListSize = DEFAULT_MAX_HEADER_LIST_PAIRS;
current_headers_.AllocateSufficientStorage(maxHeaderListSize);
Copy link
Member

Choose a reason for hiding this comment

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

If you’re calling AllocateSufficientStorage(), you kind of shouldn’t be using a MaybeStackBuffer in the first place – all this will give you is unused memory inside the Nghttp2Stream instance.

I’d suggest just going with a std::vector here … you don’t need to care about pre-allocating memory in that case, plus you can still reserve memory ahead of time using e.g. vector.reserve(128).

After testing, the pooling is not having any tangible benefit
and makes things more complicated. Simplify. Simplify.
Enforce MAX_HEADERS_LIST setting and limit the number of header
pairs accepted from the peer. Use the ENHANCE_YOUR_CALM error
code when receiving either too many headers or too many octets.
Use a MaybeStackBuffer to store the headers instead of a queue
@jasnell
Copy link
Member Author

jasnell commented Nov 3, 2017

@mcollina @addaleax ... updated.

  1. Set the default header pairs limit to 128
  2. Updated so that on the server, 4 is the minimum. On the client, 1 is the minimum.
  3. Updated to use a std::vector

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

@jasnell
Copy link
Member Author

jasnell commented Nov 5, 2017

jasnell added a commit that referenced this pull request Nov 5, 2017
* eliminate pooling of Nghttp2Stream instances. After testing,
  the pooling is not having any tangible benefit
  and makes things more complicated. Simplify. Simplify.

* refactor inbound headers

* Enforce MAX_HEADERS_LIST setting and limit the number of header
  pairs accepted from the peer. Use the ENHANCE_YOUR_CALM error
  code when receiving either too many headers or too many octets.
  Use a vector to store the headers instead of a queue

PR-URL: #16676
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
@jasnell
Copy link
Member Author

jasnell commented Nov 5, 2017

Landed in 9f3d59e

@jasnell jasnell closed this Nov 5, 2017
cjihrig pushed a commit to cjihrig/node that referenced this pull request Nov 6, 2017
* eliminate pooling of Nghttp2Stream instances. After testing,
  the pooling is not having any tangible benefit
  and makes things more complicated. Simplify. Simplify.

* refactor inbound headers

* Enforce MAX_HEADERS_LIST setting and limit the number of header
  pairs accepted from the peer. Use the ENHANCE_YOUR_CALM error
  code when receiving either too many headers or too many octets.
  Use a vector to store the headers instead of a queue

PR-URL: nodejs#16676
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
@cjihrig cjihrig mentioned this pull request Nov 6, 2017
MylesBorins pushed a commit that referenced this pull request Dec 7, 2017
* eliminate pooling of Nghttp2Stream instances. After testing,
  the pooling is not having any tangible benefit
  and makes things more complicated. Simplify. Simplify.

* refactor inbound headers

* Enforce MAX_HEADERS_LIST setting and limit the number of header
  pairs accepted from the peer. Use the ENHANCE_YOUR_CALM error
  code when receiving either too many headers or too many octets.
  Use a vector to store the headers instead of a queue

PR-URL: #16676
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
@MylesBorins MylesBorins mentioned this pull request Dec 7, 2017
MylesBorins pushed a commit that referenced this pull request Dec 8, 2017
* eliminate pooling of Nghttp2Stream instances. After testing,
  the pooling is not having any tangible benefit
  and makes things more complicated. Simplify. Simplify.

* refactor inbound headers

* Enforce MAX_HEADERS_LIST setting and limit the number of header
  pairs accepted from the peer. Use the ENHANCE_YOUR_CALM error
  code when receiving either too many headers or too many octets.
  Use a vector to store the headers instead of a queue

PR-URL: #16676
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
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