podman
1// Copyright 2014 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package hpack6
7import (8"io"9)
10
11const (12uint32Max = ^uint32(0)13initialHeaderTableSize = 409614)
15
16type Encoder struct {17dynTab dynamicTable
18// minSize is the minimum table size set by19// SetMaxDynamicTableSize after the previous Header Table Size20// Update.21minSize uint3222// maxSizeLimit is the maximum table size this encoder23// supports. This will protect the encoder from too large24// size.25maxSizeLimit uint3226// tableSizeUpdate indicates whether "Header Table Size27// Update" is required.28tableSizeUpdate bool29w io.Writer30buf []byte31}
32
33// NewEncoder returns a new Encoder which performs HPACK encoding. An
34// encoded data is written to w.
35func NewEncoder(w io.Writer) *Encoder {36e := &Encoder{37minSize: uint32Max,38maxSizeLimit: initialHeaderTableSize,39tableSizeUpdate: false,40w: w,41}42e.dynTab.table.init()43e.dynTab.setMaxSize(initialHeaderTableSize)44return e45}
46
47// WriteField encodes f into a single Write to e's underlying Writer.
48// This function may also produce bytes for "Header Table Size Update"
49// if necessary. If produced, it is done before encoding f.
50func (e *Encoder) WriteField(f HeaderField) error {51e.buf = e.buf[:0]52
53if e.tableSizeUpdate {54e.tableSizeUpdate = false55if e.minSize < e.dynTab.maxSize {56e.buf = appendTableSize(e.buf, e.minSize)57}58e.minSize = uint32Max59e.buf = appendTableSize(e.buf, e.dynTab.maxSize)60}61
62idx, nameValueMatch := e.searchTable(f)63if nameValueMatch {64e.buf = appendIndexed(e.buf, idx)65} else {66indexing := e.shouldIndex(f)67if indexing {68e.dynTab.add(f)69}70
71if idx == 0 {72e.buf = appendNewName(e.buf, f, indexing)73} else {74e.buf = appendIndexedName(e.buf, f, idx, indexing)75}76}77n, err := e.w.Write(e.buf)78if err == nil && n != len(e.buf) {79err = io.ErrShortWrite80}81return err82}
83
84// searchTable searches f in both stable and dynamic header tables.
85// The static header table is searched first. Only when there is no
86// exact match for both name and value, the dynamic header table is
87// then searched. If there is no match, i is 0. If both name and value
88// match, i is the matched index and nameValueMatch becomes true. If
89// only name matches, i points to that index and nameValueMatch
90// becomes false.
91func (e *Encoder) searchTable(f HeaderField) (i uint64, nameValueMatch bool) {92i, nameValueMatch = staticTable.search(f)93if nameValueMatch {94return i, true95}96
97j, nameValueMatch := e.dynTab.table.search(f)98if nameValueMatch || (i == 0 && j != 0) {99return j + uint64(staticTable.len()), nameValueMatch100}101
102return i, false103}
104
105// SetMaxDynamicTableSize changes the dynamic header table size to v.
106// The actual size is bounded by the value passed to
107// SetMaxDynamicTableSizeLimit.
108func (e *Encoder) SetMaxDynamicTableSize(v uint32) {109if v > e.maxSizeLimit {110v = e.maxSizeLimit111}112if v < e.minSize {113e.minSize = v114}115e.tableSizeUpdate = true116e.dynTab.setMaxSize(v)117}
118
119// MaxDynamicTableSize returns the current dynamic header table size.
120func (e *Encoder) MaxDynamicTableSize() (v uint32) {121return e.dynTab.maxSize122}
123
124// SetMaxDynamicTableSizeLimit changes the maximum value that can be
125// specified in SetMaxDynamicTableSize to v. By default, it is set to
126// 4096, which is the same size of the default dynamic header table
127// size described in HPACK specification. If the current maximum
128// dynamic header table size is strictly greater than v, "Header Table
129// Size Update" will be done in the next WriteField call and the
130// maximum dynamic header table size is truncated to v.
131func (e *Encoder) SetMaxDynamicTableSizeLimit(v uint32) {132e.maxSizeLimit = v133if e.dynTab.maxSize > v {134e.tableSizeUpdate = true135e.dynTab.setMaxSize(v)136}137}
138
139// shouldIndex reports whether f should be indexed.
140func (e *Encoder) shouldIndex(f HeaderField) bool {141return !f.Sensitive && f.Size() <= e.dynTab.maxSize142}
143
144// appendIndexed appends index i, as encoded in "Indexed Header Field"
145// representation, to dst and returns the extended buffer.
146func appendIndexed(dst []byte, i uint64) []byte {147first := len(dst)148dst = appendVarInt(dst, 7, i)149dst[first] |= 0x80150return dst151}
152
153// appendNewName appends f, as encoded in one of "Literal Header field
154// - New Name" representation variants, to dst and returns the
155// extended buffer.
156//
157// If f.Sensitive is true, "Never Indexed" representation is used. If
158// f.Sensitive is false and indexing is true, "Incremental Indexing"
159// representation is used.
160func appendNewName(dst []byte, f HeaderField, indexing bool) []byte {161dst = append(dst, encodeTypeByte(indexing, f.Sensitive))162dst = appendHpackString(dst, f.Name)163return appendHpackString(dst, f.Value)164}
165
166// appendIndexedName appends f and index i referring indexed name
167// entry, as encoded in one of "Literal Header field - Indexed Name"
168// representation variants, to dst and returns the extended buffer.
169//
170// If f.Sensitive is true, "Never Indexed" representation is used. If
171// f.Sensitive is false and indexing is true, "Incremental Indexing"
172// representation is used.
173func appendIndexedName(dst []byte, f HeaderField, i uint64, indexing bool) []byte {174first := len(dst)175var n byte176if indexing {177n = 6178} else {179n = 4180}181dst = appendVarInt(dst, n, i)182dst[first] |= encodeTypeByte(indexing, f.Sensitive)183return appendHpackString(dst, f.Value)184}
185
186// appendTableSize appends v, as encoded in "Header Table Size Update"
187// representation, to dst and returns the extended buffer.
188func appendTableSize(dst []byte, v uint32) []byte {189first := len(dst)190dst = appendVarInt(dst, 5, uint64(v))191dst[first] |= 0x20192return dst193}
194
195// appendVarInt appends i, as encoded in variable integer form using n
196// bit prefix, to dst and returns the extended buffer.
197//
198// See
199// https://httpwg.org/specs/rfc7541.html#integer.representation
200func appendVarInt(dst []byte, n byte, i uint64) []byte {201k := uint64((1 << n) - 1)202if i < k {203return append(dst, byte(i))204}205dst = append(dst, byte(k))206i -= k207for ; i >= 128; i >>= 7 {208dst = append(dst, byte(0x80|(i&0x7f)))209}210return append(dst, byte(i))211}
212
213// appendHpackString appends s, as encoded in "String Literal"
214// representation, to dst and returns the extended buffer.
215//
216// s will be encoded in Huffman codes only when it produces strictly
217// shorter byte string.
218func appendHpackString(dst []byte, s string) []byte {219huffmanLength := HuffmanEncodeLength(s)220if huffmanLength < uint64(len(s)) {221first := len(dst)222dst = appendVarInt(dst, 7, huffmanLength)223dst = AppendHuffmanString(dst, s)224dst[first] |= 0x80225} else {226dst = appendVarInt(dst, 7, uint64(len(s)))227dst = append(dst, s...)228}229return dst230}
231
232// encodeTypeByte returns type byte. If sensitive is true, type byte
233// for "Never Indexed" representation is returned. If sensitive is
234// false and indexing is true, type byte for "Incremental Indexing"
235// representation is returned. Otherwise, type byte for "Without
236// Indexing" is returned.
237func encodeTypeByte(indexing, sensitive bool) byte {238if sensitive {239return 0x10240}241if indexing {242return 0x40243}244return 0245}
246