podman

Форк
0
245 строк · 7.1 Кб
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

5
package hpack
6

7
import (
8
	"io"
9
)
10

11
const (
12
	uint32Max              = ^uint32(0)
13
	initialHeaderTableSize = 4096
14
)
15

16
type Encoder struct {
17
	dynTab dynamicTable
18
	// minSize is the minimum table size set by
19
	// SetMaxDynamicTableSize after the previous Header Table Size
20
	// Update.
21
	minSize uint32
22
	// maxSizeLimit is the maximum table size this encoder
23
	// supports. This will protect the encoder from too large
24
	// size.
25
	maxSizeLimit uint32
26
	// tableSizeUpdate indicates whether "Header Table Size
27
	// Update" is required.
28
	tableSizeUpdate bool
29
	w               io.Writer
30
	buf             []byte
31
}
32

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 {
36
	e := &Encoder{
37
		minSize:         uint32Max,
38
		maxSizeLimit:    initialHeaderTableSize,
39
		tableSizeUpdate: false,
40
		w:               w,
41
	}
42
	e.dynTab.table.init()
43
	e.dynTab.setMaxSize(initialHeaderTableSize)
44
	return e
45
}
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.
50
func (e *Encoder) WriteField(f HeaderField) error {
51
	e.buf = e.buf[:0]
52

53
	if e.tableSizeUpdate {
54
		e.tableSizeUpdate = false
55
		if e.minSize < e.dynTab.maxSize {
56
			e.buf = appendTableSize(e.buf, e.minSize)
57
		}
58
		e.minSize = uint32Max
59
		e.buf = appendTableSize(e.buf, e.dynTab.maxSize)
60
	}
61

62
	idx, nameValueMatch := e.searchTable(f)
63
	if nameValueMatch {
64
		e.buf = appendIndexed(e.buf, idx)
65
	} else {
66
		indexing := e.shouldIndex(f)
67
		if indexing {
68
			e.dynTab.add(f)
69
		}
70

71
		if idx == 0 {
72
			e.buf = appendNewName(e.buf, f, indexing)
73
		} else {
74
			e.buf = appendIndexedName(e.buf, f, idx, indexing)
75
		}
76
	}
77
	n, err := e.w.Write(e.buf)
78
	if err == nil && n != len(e.buf) {
79
		err = io.ErrShortWrite
80
	}
81
	return err
82
}
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.
91
func (e *Encoder) searchTable(f HeaderField) (i uint64, nameValueMatch bool) {
92
	i, nameValueMatch = staticTable.search(f)
93
	if nameValueMatch {
94
		return i, true
95
	}
96

97
	j, nameValueMatch := e.dynTab.table.search(f)
98
	if nameValueMatch || (i == 0 && j != 0) {
99
		return j + uint64(staticTable.len()), nameValueMatch
100
	}
101

102
	return i, false
103
}
104

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 {
110
		v = e.maxSizeLimit
111
	}
112
	if v < e.minSize {
113
		e.minSize = v
114
	}
115
	e.tableSizeUpdate = true
116
	e.dynTab.setMaxSize(v)
117
}
118

119
// MaxDynamicTableSize returns the current dynamic header table size.
120
func (e *Encoder) MaxDynamicTableSize() (v uint32) {
121
	return e.dynTab.maxSize
122
}
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.
131
func (e *Encoder) SetMaxDynamicTableSizeLimit(v uint32) {
132
	e.maxSizeLimit = v
133
	if e.dynTab.maxSize > v {
134
		e.tableSizeUpdate = true
135
		e.dynTab.setMaxSize(v)
136
	}
137
}
138

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
142
}
143

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 {
147
	first := len(dst)
148
	dst = appendVarInt(dst, 7, i)
149
	dst[first] |= 0x80
150
	return dst
151
}
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.
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)
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.
173
func appendIndexedName(dst []byte, f HeaderField, i uint64, indexing bool) []byte {
174
	first := len(dst)
175
	var n byte
176
	if indexing {
177
		n = 6
178
	} else {
179
		n = 4
180
	}
181
	dst = appendVarInt(dst, n, i)
182
	dst[first] |= encodeTypeByte(indexing, f.Sensitive)
183
	return 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.
188
func appendTableSize(dst []byte, v uint32) []byte {
189
	first := len(dst)
190
	dst = appendVarInt(dst, 5, uint64(v))
191
	dst[first] |= 0x20
192
	return dst
193
}
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
200
func appendVarInt(dst []byte, n byte, i uint64) []byte {
201
	k := uint64((1 << n) - 1)
202
	if i < k {
203
		return append(dst, byte(i))
204
	}
205
	dst = append(dst, byte(k))
206
	i -= k
207
	for ; i >= 128; i >>= 7 {
208
		dst = append(dst, byte(0x80|(i&0x7f)))
209
	}
210
	return 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.
218
func appendHpackString(dst []byte, s string) []byte {
219
	huffmanLength := HuffmanEncodeLength(s)
220
	if huffmanLength < uint64(len(s)) {
221
		first := len(dst)
222
		dst = appendVarInt(dst, 7, huffmanLength)
223
		dst = AppendHuffmanString(dst, s)
224
		dst[first] |= 0x80
225
	} else {
226
		dst = appendVarInt(dst, 7, uint64(len(s)))
227
		dst = append(dst, s...)
228
	}
229
	return dst
230
}
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.
237
func encodeTypeByte(indexing, sensitive bool) byte {
238
	if sensitive {
239
		return 0x10
240
	}
241
	if indexing {
242
		return 0x40
243
	}
244
	return 0
245
}
246

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.