TUN-2796: Implement HTTP2 CONTINUATION headers correctly
This commit is contained in:
parent
a368fbbe9b
commit
80f387214c
|
@ -250,28 +250,52 @@ func (w *MuxWriter) encodeHeaders(headers []Header) ([]byte, error) {
|
||||||
// writeHeaders writes a block of encoded headers, splitting it into multiple frames if necessary.
|
// writeHeaders writes a block of encoded headers, splitting it into multiple frames if necessary.
|
||||||
func (w *MuxWriter) writeHeaders(streamID uint32, headers []Header) error {
|
func (w *MuxWriter) writeHeaders(streamID uint32, headers []Header) error {
|
||||||
encodedHeaders, err := w.encodeHeaders(headers)
|
encodedHeaders, err := w.encodeHeaders(headers)
|
||||||
if err != nil {
|
if err != nil || len(encodedHeaders) == 0 {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
blockSize := int(w.maxFrameSize)
|
blockSize := int(w.maxFrameSize)
|
||||||
endHeaders := len(encodedHeaders) == 0
|
// CONTINUATION is unnecessary; the headers fit within the blockSize
|
||||||
for !endHeaders && err == nil {
|
if len(encodedHeaders) < blockSize {
|
||||||
blockFragment := encodedHeaders
|
return w.f.WriteHeaders(http2.HeadersFrameParam{
|
||||||
if len(encodedHeaders) > blockSize {
|
StreamID: streamID,
|
||||||
blockFragment = blockFragment[:blockSize]
|
EndHeaders: true,
|
||||||
encodedHeaders = encodedHeaders[blockSize:]
|
BlockFragment: encodedHeaders,
|
||||||
// Send CONTINUATION frame if the headers can't be fit into 1 frame
|
})
|
||||||
err = w.f.WriteContinuation(streamID, endHeaders, blockFragment)
|
}
|
||||||
} else {
|
|
||||||
endHeaders = true
|
choppedHeaders := chopEncodedHeaders(encodedHeaders, blockSize)
|
||||||
err = w.f.WriteHeaders(http2.HeadersFrameParam{
|
// len(choppedHeaders) is at least 2
|
||||||
StreamID: streamID,
|
if err := w.f.WriteHeaders(http2.HeadersFrameParam{StreamID: streamID, EndHeaders: false, BlockFragment: choppedHeaders[0]}); err != nil {
|
||||||
EndHeaders: endHeaders,
|
return err
|
||||||
BlockFragment: blockFragment,
|
}
|
||||||
})
|
for i := 1; i < len(choppedHeaders)-1; i++ {
|
||||||
|
if err := w.f.WriteContinuation(streamID, false, choppedHeaders[i]); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err
|
if err := w.f.WriteContinuation(streamID, true, choppedHeaders[len(choppedHeaders)-1]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Partition a slice of bytes into `len(slice) / blockSize` slices of length `blockSize`
|
||||||
|
func chopEncodedHeaders(headers []byte, chunkSize int) [][]byte {
|
||||||
|
var divided [][]byte
|
||||||
|
|
||||||
|
for i := 0; i < len(headers); i += chunkSize {
|
||||||
|
end := i + chunkSize
|
||||||
|
|
||||||
|
if end > len(headers) {
|
||||||
|
end = len(headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
divided = append(divided, headers[i:end])
|
||||||
|
}
|
||||||
|
|
||||||
|
return divided
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *MuxWriter) writeUseDictionary(dictRequest useDictRequest) error {
|
func (w *MuxWriter) writeUseDictionary(dictRequest useDictRequest) error {
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package h2mux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestChopEncodedHeaders(t *testing.T) {
|
||||||
|
mockEncodedHeaders := make([]byte, 5)
|
||||||
|
for i := range mockEncodedHeaders {
|
||||||
|
mockEncodedHeaders[i] = byte(i)
|
||||||
|
}
|
||||||
|
chopped := chopEncodedHeaders(mockEncodedHeaders, 4)
|
||||||
|
|
||||||
|
assert.Equal(t, 2, len(chopped))
|
||||||
|
assert.Equal(t, []byte{0, 1, 2, 3}, chopped[0])
|
||||||
|
assert.Equal(t, []byte{4}, chopped[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChopEncodedEmptyHeaders(t *testing.T) {
|
||||||
|
mockEncodedHeaders := make([]byte, 0)
|
||||||
|
chopped := chopEncodedHeaders(mockEncodedHeaders, 3)
|
||||||
|
|
||||||
|
assert.Equal(t, 0, len(chopped))
|
||||||
|
}
|
Loading…
Reference in New Issue