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.
12
uint32Max = ^uint32(0)
13
initialHeaderTableSize = 4096
18
// minSize is the minimum table size set by
19
// SetMaxDynamicTableSize after the previous Header Table Size
22
// maxSizeLimit is the maximum table size this encoder
23
// supports. This will protect the encoder from too large
26
// tableSizeUpdate indicates whether "Header Table Size
27
// Update" is required.
33
// NewEncoder returns a new Encoder which performs HPACK encoding. An
34
// encoded data is written to w.
35
func NewEncoder(w io.Writer) *Encoder {
38
maxSizeLimit: initialHeaderTableSize,
39
tableSizeUpdate: false,
43
e.dynTab.setMaxSize(initialHeaderTableSize)
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.
50
func (e *Encoder) WriteField(f HeaderField) error {
53
if e.tableSizeUpdate {
54
e.tableSizeUpdate = false
55
if e.minSize < e.dynTab.maxSize {
56
e.buf = appendTableSize(e.buf, e.minSize)
59
e.buf = appendTableSize(e.buf, e.dynTab.maxSize)
62
idx, nameValueMatch := e.searchTable(f)
64
e.buf = appendIndexed(e.buf, idx)
66
indexing := e.shouldIndex(f)
72
e.buf = appendNewName(e.buf, f, indexing)
74
e.buf = appendIndexedName(e.buf, f, idx, indexing)
77
n, err := e.w.Write(e.buf)
78
if err == nil && n != len(e.buf) {
79
err = io.ErrShortWrite
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
91
func (e *Encoder) searchTable(f HeaderField) (i uint64, nameValueMatch bool) {
92
i, nameValueMatch = staticTable.search(f)
97
j, nameValueMatch := e.dynTab.table.search(f)
98
if nameValueMatch || (i == 0 && j != 0) {
99
return j + uint64(staticTable.len()), nameValueMatch
105
// SetMaxDynamicTableSize changes the dynamic header table size to v.
106
// The actual size is bounded by the value passed to
107
// SetMaxDynamicTableSizeLimit.
108
func (e *Encoder) SetMaxDynamicTableSize(v uint32) {
109
if v > e.maxSizeLimit {
115
e.tableSizeUpdate = true
116
e.dynTab.setMaxSize(v)
119
// MaxDynamicTableSize returns the current dynamic header table size.
120
func (e *Encoder) MaxDynamicTableSize() (v uint32) {
121
return e.dynTab.maxSize
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.
131
func (e *Encoder) SetMaxDynamicTableSizeLimit(v uint32) {
133
if e.dynTab.maxSize > v {
134
e.tableSizeUpdate = true
135
e.dynTab.setMaxSize(v)
139
// shouldIndex reports whether f should be indexed.
140
func (e *Encoder) shouldIndex(f HeaderField) bool {
141
return !f.Sensitive && f.Size() <= e.dynTab.maxSize
144
// appendIndexed appends index i, as encoded in "Indexed Header Field"
145
// representation, to dst and returns the extended buffer.
146
func appendIndexed(dst []byte, i uint64) []byte {
148
dst = appendVarInt(dst, 7, i)
153
// appendNewName appends f, as encoded in one of "Literal Header field
154
// - New Name" representation variants, to dst and returns the
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.
160
func appendNewName(dst []byte, f HeaderField, indexing bool) []byte {
161
dst = append(dst, encodeTypeByte(indexing, f.Sensitive))
162
dst = appendHpackString(dst, f.Name)
163
return appendHpackString(dst, f.Value)
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.
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.
173
func appendIndexedName(dst []byte, f HeaderField, i uint64, indexing bool) []byte {
181
dst = appendVarInt(dst, n, i)
182
dst[first] |= encodeTypeByte(indexing, f.Sensitive)
183
return appendHpackString(dst, f.Value)
186
// appendTableSize appends v, as encoded in "Header Table Size Update"
187
// representation, to dst and returns the extended buffer.
188
func appendTableSize(dst []byte, v uint32) []byte {
190
dst = appendVarInt(dst, 5, uint64(v))
195
// appendVarInt appends i, as encoded in variable integer form using n
196
// bit prefix, to dst and returns the extended buffer.
199
// https://httpwg.org/specs/rfc7541.html#integer.representation
200
func appendVarInt(dst []byte, n byte, i uint64) []byte {
201
k := uint64((1 << n) - 1)
203
return append(dst, byte(i))
205
dst = append(dst, byte(k))
207
for ; i >= 128; i >>= 7 {
208
dst = append(dst, byte(0x80|(i&0x7f)))
210
return append(dst, byte(i))
213
// appendHpackString appends s, as encoded in "String Literal"
214
// representation, to dst and returns the extended buffer.
216
// s will be encoded in Huffman codes only when it produces strictly
217
// shorter byte string.
218
func appendHpackString(dst []byte, s string) []byte {
219
huffmanLength := HuffmanEncodeLength(s)
220
if huffmanLength < uint64(len(s)) {
222
dst = appendVarInt(dst, 7, huffmanLength)
223
dst = AppendHuffmanString(dst, s)
226
dst = appendVarInt(dst, 7, uint64(len(s)))
227
dst = append(dst, s...)
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.
237
func encodeTypeByte(indexing, sensitive bool) byte {