-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
x/net/http2/hpack: Regression in dynamic table size updates #29187
Comments
@bradfitz FYI You are correct. The fix isn't quite right since the Decoder is missing a bit of state.
I did investigate other hpack implementations to see how they handled this spec issue. Most do not. Lua actually documented it as a TODO, and another handled it appropriately because they decoded the entire header as one big blob. |
I'm not sure you need to change the API to fix this, but my review of your implementation was rather superficial. You definitely need more state tracking, but as long as this remains as an internal/vendor package you probably don't need more documentation, only references to the RFC like the mention of section 4.2 above. Any reason why it's not part of the public go API? I'm asking for a friend :-p |
There is no need to change the API, but a new API needs to be added. On the flip side, the Encoder doesn't guarantee that it would generate the update as the beginning of the first block since it too lacks state but it's less of an issue there. |
You already have the Regarding the out of band resize (done via h2 settings) you need to keep track up to two of them and remember the lowest and the latest. I think the decoder can grow new internal state for both without the need for a new API. |
IMO the first thing to do is to godoc the go/src/internal/x/net/http2/hpack/hpack.go Lines 215 to 227 in 944a9c7
Lines 2775 to 2797 in 944a9c7
I must admit I found the name surprising initially :) |
I had my eyes on the Encoder when I added go to the cashpack test suite but it didn't seem flexible enough to meet the needs of the test suite. So my review there was even more superficial and I didn't revisit it on Monday while I was hunting this bug. |
Good catch on using Close(). I think I can get something to work quickly. |
Change https://golang.org/cl/153978 mentions this issue: |
dynamic table size updates must occur at the beginning of the first header block. The original fix, golang/go#25023, guaranteed it was at the beginning of the very first block. The Close method implicitly marked the end of the current header. We now document the Close behavior and can track when we are at the beginning of the first block. Updates golang/go#29187 Change-Id: I83ec39546527cb17d7de8a88ec417a46443d2baa Reviewed-on: https://go-review.googlesource.com/c/153978 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
Change https://golang.org/cl/154118 mentions this issue: |
Updates to x/net git rev 891ebc4b82d6e74f468c533b06f983c7be918a96 for: http2/hpack: track the beginning of a header block https://go-review.googlesource.com/c/153978 Updates #29187 Change-Id: Ie2568b3f8d6aaa3f097a4ac25d3acdc794f5ff5c Reviewed-on: https://go-review.googlesource.com/c/154118 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Matt Layher <mdlayher@gmail.com>
I believe this can be closed now since the changes have been merged. |
dynamic table size updates must occur at the beginning of the first header block. The original fix, golang/go#25023, guaranteed it was at the beginning of the very first block. The Close method implicitly marked the end of the current header. We now document the Close behavior and can track when we are at the beginning of the first block. Updates golang/go#29187 Change-Id: I83ec39546527cb17d7de8a88ec417a46443d2baa Reviewed-on: https://go-review.googlesource.com/c/153978 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
I had forgotten about this one and indeed, closing it was long overdue. I was able to test the |
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
I'm not sure, I install go from fedora repositories:
But the code from the master looks very similar if not identical, so probably a yes. I only browsed the pieces of code I discuss below.
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
Before we added support for HTTP/2 to Varnish Cache I wrote a single-allocation HPACK implementation called cashpack (it does not amortize dynamic tables across concurrent h2 sessions). Once I had a 100% coverage of the spec I added partial support for nghttp2 decoding with the same test suite and partial support for go's hpack implementation. The support is partial because when I baked in golang support there was no way to probe the dynamic table and check its contents.
The goal was to get some confidence in terms of interoperability with other implementations, in addition to prototyping an HPACK implementation that would work under Varnish's memory constraints.
The code is here:
https://github.com/Dridi/cashpack
The test suite is explained in great details here:
https://github.com/Dridi/cashpack/blob/master/tst/README.rst
Including a word on interop checks:
https://github.com/Dridi/cashpack/blob/master/tst/README.rst#interoperability-checks
What did you expect to see?
When I worked on cashpack for the first time since I upgraded to Fedora 29 I ran into two test failures only for the go implementation. Ever since go 1.6 I have been used to seeing the test suite pass and was surprised to see it fail!
What did you see instead?
The error log from the test suite looks like this:
I checked the code from the golang-src package on Fedora and came to the conclusion that the logic is broken if my reading of the RFC is correct.
go/src/internal/x/net/http2/hpack/hpack.go
Lines 392 to 396 in 2012227
Here the code is looking at the size of the dynamic table to figure whether we are at the beginning of a block, but the dynamic table is completely orthogonal to the organization of blocks. The block from stream 1 may insert fields into the table, and the block from stream 3 may wish to enlarge the table to make room for cookies.
There is also a convoluted use case to help clear the dynamic table without changing its size:
$size
$size
$size
It looks like in
hpack.go
the out of band resize takes effect immediately instead of being deferred to the next HPACK block (which in practice shouldn't make any difference). The actionable bug is that assuming that a table update that must happen at the beginning of a block is correlated to the dynamic table being empty.If I understand the purpose correctly of
hpack.Decode.Close()
(the only function I used that is not documented) it is meant to signal the end of an HPACK block, however there is no state tracking of that fact, only a check that there is no further data left to be parsed:go/src/vendor/golang_org/x/net/http2/hpack/hpack.go
Lines 229 to 234 in 1ae7397
Test cases for RFC coverage include a copy of the section they cover, for example the double-resize single-update case:
https://github.com/Dridi/cashpack/blob/master/tst/rfc7541_4_2#L24-L35
If you wish to look closer at the test suite, you will sometimes see that some interop checks are ignored because either the HPACK implementation leaves some checks to its h2 stack or we run into undefined behavior like for example the limits of packed integers. This is done with the
tst_ignore
fixture to leave out the go or nghttp2 implementation.I'm not a go developer so I don't feel confident enough to attempt a patch.
godecode
was my very first go program, please be gentle :)https://github.com/Dridi/cashpack/blob/master/tst/godecode.go
The text was updated successfully, but these errors were encountered: