.github
alerting
client
config
core
docs
example
jsonpath
k8s
k8stest
metric
pattern
security
static
vendor
cloud.google.com
github.com
golang.org
x
crypto
net
context
http
http2
hpack
encode.go
hpack.go
huffman.go
tables.go
.gitignore
Dockerfile
Makefile
README
ciphers.go
client_conn_pool.go
databuffer.go
errors.go
flow.go
frame.go
go111.go
gotrack.go
headermap.go
http2.go
not_go111.go
pipe.go
server.go
transport.go
write.go
writesched.go
writesched_priority.go
writesched_random.go
idna
AUTHORS
CONTRIBUTORS
LICENSE
PATENTS
oauth2
sys
text
time
google.golang.org
gopkg.in
k8s.io
sigs.k8s.io
modules.txt
watchdog
.dockerignore
.gitattributes
.gitignore
Dockerfile
LICENSE.md
README.md
config.yaml
go.mod
go.sum
gzip.go
main.go
241 lines
7.0 KiB
Go
241 lines
7.0 KiB
Go
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package hpack
|
|
|
|
import (
|
|
"io"
|
|
)
|
|
|
|
const (
|
|
uint32Max = ^uint32(0)
|
|
initialHeaderTableSize = 4096
|
|
)
|
|
|
|
type Encoder struct {
|
|
dynTab dynamicTable
|
|
// minSize is the minimum table size set by
|
|
// SetMaxDynamicTableSize after the previous Header Table Size
|
|
// Update.
|
|
minSize uint32
|
|
// maxSizeLimit is the maximum table size this encoder
|
|
// supports. This will protect the encoder from too large
|
|
// size.
|
|
maxSizeLimit uint32
|
|
// tableSizeUpdate indicates whether "Header Table Size
|
|
// Update" is required.
|
|
tableSizeUpdate bool
|
|
w io.Writer
|
|
buf []byte
|
|
}
|
|
|
|
// NewEncoder returns a new Encoder which performs HPACK encoding. An
|
|
// encoded data is written to w.
|
|
func NewEncoder(w io.Writer) *Encoder {
|
|
e := &Encoder{
|
|
minSize: uint32Max,
|
|
maxSizeLimit: initialHeaderTableSize,
|
|
tableSizeUpdate: false,
|
|
w: w,
|
|
}
|
|
e.dynTab.table.init()
|
|
e.dynTab.setMaxSize(initialHeaderTableSize)
|
|
return e
|
|
}
|
|
|
|
// WriteField encodes f into a single Write to e's underlying Writer.
|
|
// This function may also produce bytes for "Header Table Size Update"
|
|
// if necessary. If produced, it is done before encoding f.
|
|
func (e *Encoder) WriteField(f HeaderField) error {
|
|
e.buf = e.buf[:0]
|
|
|
|
if e.tableSizeUpdate {
|
|
e.tableSizeUpdate = false
|
|
if e.minSize < e.dynTab.maxSize {
|
|
e.buf = appendTableSize(e.buf, e.minSize)
|
|
}
|
|
e.minSize = uint32Max
|
|
e.buf = appendTableSize(e.buf, e.dynTab.maxSize)
|
|
}
|
|
|
|
idx, nameValueMatch := e.searchTable(f)
|
|
if nameValueMatch {
|
|
e.buf = appendIndexed(e.buf, idx)
|
|
} else {
|
|
indexing := e.shouldIndex(f)
|
|
if indexing {
|
|
e.dynTab.add(f)
|
|
}
|
|
|
|
if idx == 0 {
|
|
e.buf = appendNewName(e.buf, f, indexing)
|
|
} else {
|
|
e.buf = appendIndexedName(e.buf, f, idx, indexing)
|
|
}
|
|
}
|
|
n, err := e.w.Write(e.buf)
|
|
if err == nil && n != len(e.buf) {
|
|
err = io.ErrShortWrite
|
|
}
|
|
return err
|
|
}
|
|
|
|
// searchTable searches f in both stable and dynamic header tables.
|
|
// The static header table is searched first. Only when there is no
|
|
// exact match for both name and value, the dynamic header table is
|
|
// then searched. If there is no match, i is 0. If both name and value
|
|
// match, i is the matched index and nameValueMatch becomes true. If
|
|
// only name matches, i points to that index and nameValueMatch
|
|
// becomes false.
|
|
func (e *Encoder) searchTable(f HeaderField) (i uint64, nameValueMatch bool) {
|
|
i, nameValueMatch = staticTable.search(f)
|
|
if nameValueMatch {
|
|
return i, true
|
|
}
|
|
|
|
j, nameValueMatch := e.dynTab.table.search(f)
|
|
if nameValueMatch || (i == 0 && j != 0) {
|
|
return j + uint64(staticTable.len()), nameValueMatch
|
|
}
|
|
|
|
return i, false
|
|
}
|
|
|
|
// SetMaxDynamicTableSize changes the dynamic header table size to v.
|
|
// The actual size is bounded by the value passed to
|
|
// SetMaxDynamicTableSizeLimit.
|
|
func (e *Encoder) SetMaxDynamicTableSize(v uint32) {
|
|
if v > e.maxSizeLimit {
|
|
v = e.maxSizeLimit
|
|
}
|
|
if v < e.minSize {
|
|
e.minSize = v
|
|
}
|
|
e.tableSizeUpdate = true
|
|
e.dynTab.setMaxSize(v)
|
|
}
|
|
|
|
// SetMaxDynamicTableSizeLimit changes the maximum value that can be
|
|
// specified in SetMaxDynamicTableSize to v. By default, it is set to
|
|
// 4096, which is the same size of the default dynamic header table
|
|
// size described in HPACK specification. If the current maximum
|
|
// dynamic header table size is strictly greater than v, "Header Table
|
|
// Size Update" will be done in the next WriteField call and the
|
|
// maximum dynamic header table size is truncated to v.
|
|
func (e *Encoder) SetMaxDynamicTableSizeLimit(v uint32) {
|
|
e.maxSizeLimit = v
|
|
if e.dynTab.maxSize > v {
|
|
e.tableSizeUpdate = true
|
|
e.dynTab.setMaxSize(v)
|
|
}
|
|
}
|
|
|
|
// shouldIndex reports whether f should be indexed.
|
|
func (e *Encoder) shouldIndex(f HeaderField) bool {
|
|
return !f.Sensitive && f.Size() <= e.dynTab.maxSize
|
|
}
|
|
|
|
// appendIndexed appends index i, as encoded in "Indexed Header Field"
|
|
// representation, to dst and returns the extended buffer.
|
|
func appendIndexed(dst []byte, i uint64) []byte {
|
|
first := len(dst)
|
|
dst = appendVarInt(dst, 7, i)
|
|
dst[first] |= 0x80
|
|
return dst
|
|
}
|
|
|
|
// appendNewName appends f, as encoded in one of "Literal Header field
|
|
// - New Name" representation variants, to dst and returns the
|
|
// extended buffer.
|
|
//
|
|
// If f.Sensitive is true, "Never Indexed" representation is used. If
|
|
// f.Sensitive is false and indexing is true, "Incremental Indexing"
|
|
// representation is used.
|
|
func appendNewName(dst []byte, f HeaderField, indexing bool) []byte {
|
|
dst = append(dst, encodeTypeByte(indexing, f.Sensitive))
|
|
dst = appendHpackString(dst, f.Name)
|
|
return appendHpackString(dst, f.Value)
|
|
}
|
|
|
|
// appendIndexedName appends f and index i referring indexed name
|
|
// entry, as encoded in one of "Literal Header field - Indexed Name"
|
|
// representation variants, to dst and returns the extended buffer.
|
|
//
|
|
// If f.Sensitive is true, "Never Indexed" representation is used. If
|
|
// f.Sensitive is false and indexing is true, "Incremental Indexing"
|
|
// representation is used.
|
|
func appendIndexedName(dst []byte, f HeaderField, i uint64, indexing bool) []byte {
|
|
first := len(dst)
|
|
var n byte
|
|
if indexing {
|
|
n = 6
|
|
} else {
|
|
n = 4
|
|
}
|
|
dst = appendVarInt(dst, n, i)
|
|
dst[first] |= encodeTypeByte(indexing, f.Sensitive)
|
|
return appendHpackString(dst, f.Value)
|
|
}
|
|
|
|
// appendTableSize appends v, as encoded in "Header Table Size Update"
|
|
// representation, to dst and returns the extended buffer.
|
|
func appendTableSize(dst []byte, v uint32) []byte {
|
|
first := len(dst)
|
|
dst = appendVarInt(dst, 5, uint64(v))
|
|
dst[first] |= 0x20
|
|
return dst
|
|
}
|
|
|
|
// appendVarInt appends i, as encoded in variable integer form using n
|
|
// bit prefix, to dst and returns the extended buffer.
|
|
//
|
|
// See
|
|
// http://http2.github.io/http2-spec/compression.html#integer.representation
|
|
func appendVarInt(dst []byte, n byte, i uint64) []byte {
|
|
k := uint64((1 << n) - 1)
|
|
if i < k {
|
|
return append(dst, byte(i))
|
|
}
|
|
dst = append(dst, byte(k))
|
|
i -= k
|
|
for ; i >= 128; i >>= 7 {
|
|
dst = append(dst, byte(0x80|(i&0x7f)))
|
|
}
|
|
return append(dst, byte(i))
|
|
}
|
|
|
|
// appendHpackString appends s, as encoded in "String Literal"
|
|
// representation, to dst and returns the extended buffer.
|
|
//
|
|
// s will be encoded in Huffman codes only when it produces strictly
|
|
// shorter byte string.
|
|
func appendHpackString(dst []byte, s string) []byte {
|
|
huffmanLength := HuffmanEncodeLength(s)
|
|
if huffmanLength < uint64(len(s)) {
|
|
first := len(dst)
|
|
dst = appendVarInt(dst, 7, huffmanLength)
|
|
dst = AppendHuffmanString(dst, s)
|
|
dst[first] |= 0x80
|
|
} else {
|
|
dst = appendVarInt(dst, 7, uint64(len(s)))
|
|
dst = append(dst, s...)
|
|
}
|
|
return dst
|
|
}
|
|
|
|
// encodeTypeByte returns type byte. If sensitive is true, type byte
|
|
// for "Never Indexed" representation is returned. If sensitive is
|
|
// false and indexing is true, type byte for "Incremental Indexing"
|
|
// representation is returned. Otherwise, type byte for "Without
|
|
// Indexing" is returned.
|
|
func encodeTypeByte(indexing, sensitive bool) byte {
|
|
if sensitive {
|
|
return 0x10
|
|
}
|
|
if indexing {
|
|
return 0x40
|
|
}
|
|
return 0
|
|
}
|