OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / net / http2 / hpack / encode.go
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 // SetMaxDynamicTableSizeLimit changes the maximum value that can be
120 // specified in SetMaxDynamicTableSize to v. By default, it is set to
121 // 4096, which is the same size of the default dynamic header table
122 // size described in HPACK specification. If the current maximum
123 // dynamic header table size is strictly greater than v, "Header Table
124 // Size Update" will be done in the next WriteField call and the
125 // maximum dynamic header table size is truncated to v.
126 func (e *Encoder) SetMaxDynamicTableSizeLimit(v uint32) {
127         e.maxSizeLimit = v
128         if e.dynTab.maxSize > v {
129                 e.tableSizeUpdate = true
130                 e.dynTab.setMaxSize(v)
131         }
132 }
133
134 // shouldIndex reports whether f should be indexed.
135 func (e *Encoder) shouldIndex(f HeaderField) bool {
136         return !f.Sensitive && f.Size() <= e.dynTab.maxSize
137 }
138
139 // appendIndexed appends index i, as encoded in "Indexed Header Field"
140 // representation, to dst and returns the extended buffer.
141 func appendIndexed(dst []byte, i uint64) []byte {
142         first := len(dst)
143         dst = appendVarInt(dst, 7, i)
144         dst[first] |= 0x80
145         return dst
146 }
147
148 // appendNewName appends f, as encoded in one of "Literal Header field
149 // - New Name" representation variants, to dst and returns the
150 // extended buffer.
151 //
152 // If f.Sensitive is true, "Never Indexed" representation is used. If
153 // f.Sensitive is false and indexing is true, "Inremental Indexing"
154 // representation is used.
155 func appendNewName(dst []byte, f HeaderField, indexing bool) []byte {
156         dst = append(dst, encodeTypeByte(indexing, f.Sensitive))
157         dst = appendHpackString(dst, f.Name)
158         return appendHpackString(dst, f.Value)
159 }
160
161 // appendIndexedName appends f and index i referring indexed name
162 // entry, as encoded in one of "Literal Header field - Indexed Name"
163 // representation variants, to dst and returns the extended buffer.
164 //
165 // If f.Sensitive is true, "Never Indexed" representation is used. If
166 // f.Sensitive is false and indexing is true, "Incremental Indexing"
167 // representation is used.
168 func appendIndexedName(dst []byte, f HeaderField, i uint64, indexing bool) []byte {
169         first := len(dst)
170         var n byte
171         if indexing {
172                 n = 6
173         } else {
174                 n = 4
175         }
176         dst = appendVarInt(dst, n, i)
177         dst[first] |= encodeTypeByte(indexing, f.Sensitive)
178         return appendHpackString(dst, f.Value)
179 }
180
181 // appendTableSize appends v, as encoded in "Header Table Size Update"
182 // representation, to dst and returns the extended buffer.
183 func appendTableSize(dst []byte, v uint32) []byte {
184         first := len(dst)
185         dst = appendVarInt(dst, 5, uint64(v))
186         dst[first] |= 0x20
187         return dst
188 }
189
190 // appendVarInt appends i, as encoded in variable integer form using n
191 // bit prefix, to dst and returns the extended buffer.
192 //
193 // See
194 // http://http2.github.io/http2-spec/compression.html#integer.representation
195 func appendVarInt(dst []byte, n byte, i uint64) []byte {
196         k := uint64((1 << n) - 1)
197         if i < k {
198                 return append(dst, byte(i))
199         }
200         dst = append(dst, byte(k))
201         i -= k
202         for ; i >= 128; i >>= 7 {
203                 dst = append(dst, byte(0x80|(i&0x7f)))
204         }
205         return append(dst, byte(i))
206 }
207
208 // appendHpackString appends s, as encoded in "String Literal"
209 // representation, to dst and returns the the extended buffer.
210 //
211 // s will be encoded in Huffman codes only when it produces strictly
212 // shorter byte string.
213 func appendHpackString(dst []byte, s string) []byte {
214         huffmanLength := HuffmanEncodeLength(s)
215         if huffmanLength < uint64(len(s)) {
216                 first := len(dst)
217                 dst = appendVarInt(dst, 7, huffmanLength)
218                 dst = AppendHuffmanString(dst, s)
219                 dst[first] |= 0x80
220         } else {
221                 dst = appendVarInt(dst, 7, uint64(len(s)))
222                 dst = append(dst, s...)
223         }
224         return dst
225 }
226
227 // encodeTypeByte returns type byte. If sensitive is true, type byte
228 // for "Never Indexed" representation is returned. If sensitive is
229 // false and indexing is true, type byte for "Incremental Indexing"
230 // representation is returned. Otherwise, type byte for "Without
231 // Indexing" is returned.
232 func encodeTypeByte(indexing, sensitive bool) byte {
233         if sensitive {
234                 return 0x10
235         }
236         if indexing {
237                 return 0x40
238         }
239         return 0
240 }