github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/net/http2/hpack/encode.go (about) 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.setMaxSize(initialHeaderTableSize) 43 return e 44 } 45 46 // WriteField encodes f into a single Write to e's underlying Writer. 47 // This function may also produce bytes for "Header Table Size Update" 48 // if necessary. If produced, it is done before encoding f. 49 func (e *Encoder) WriteField(f HeaderField) error { 50 e.buf = e.buf[:0] 51 52 if e.tableSizeUpdate { 53 e.tableSizeUpdate = false 54 if e.minSize < e.dynTab.maxSize { 55 e.buf = appendTableSize(e.buf, e.minSize) 56 } 57 e.minSize = uint32Max 58 e.buf = appendTableSize(e.buf, e.dynTab.maxSize) 59 } 60 61 idx, nameValueMatch := e.searchTable(f) 62 if nameValueMatch { 63 e.buf = appendIndexed(e.buf, idx) 64 } else { 65 indexing := e.shouldIndex(f) 66 if indexing { 67 e.dynTab.add(f) 68 } 69 70 if idx == 0 { 71 e.buf = appendNewName(e.buf, f, indexing) 72 } else { 73 e.buf = appendIndexedName(e.buf, f, idx, indexing) 74 } 75 } 76 n, err := e.w.Write(e.buf) 77 if err == nil && n != len(e.buf) { 78 err = io.ErrShortWrite 79 } 80 return err 81 } 82 83 // searchTable searches f in both stable and dynamic header tables. 84 // The static header table is searched first. Only when there is no 85 // exact match for both name and value, the dynamic header table is 86 // then searched. If there is no match, i is 0. If both name and value 87 // match, i is the matched index and nameValueMatch becomes true. If 88 // only name matches, i points to that index and nameValueMatch 89 // becomes false. 90 func (e *Encoder) searchTable(f HeaderField) (i uint64, nameValueMatch bool) { 91 for idx, hf := range staticTable { 92 if !constantTimeStringCompare(hf.Name, f.Name) { 93 continue 94 } 95 if i == 0 { 96 i = uint64(idx + 1) 97 } 98 if f.Sensitive { 99 continue 100 } 101 if !constantTimeStringCompare(hf.Value, f.Value) { 102 continue 103 } 104 i = uint64(idx + 1) 105 nameValueMatch = true 106 return 107 } 108 109 j, nameValueMatch := e.dynTab.search(f) 110 if nameValueMatch || (i == 0 && j != 0) { 111 i = j + uint64(len(staticTable)) 112 } 113 return 114 } 115 116 // SetMaxDynamicTableSize changes the dynamic header table size to v. 117 // The actual size is bounded by the value passed to 118 // SetMaxDynamicTableSizeLimit. 119 func (e *Encoder) SetMaxDynamicTableSize(v uint32) { 120 if v > e.maxSizeLimit { 121 v = e.maxSizeLimit 122 } 123 if v < e.minSize { 124 e.minSize = v 125 } 126 e.tableSizeUpdate = true 127 e.dynTab.setMaxSize(v) 128 } 129 130 // SetMaxDynamicTableSizeLimit changes the maximum value that can be 131 // specified in SetMaxDynamicTableSize to v. By default, it is set to 132 // 4096, which is the same size of the default dynamic header table 133 // size described in HPACK specification. If the current maximum 134 // dynamic header table size is strictly greater than v, "Header Table 135 // Size Update" will be done in the next WriteField call and the 136 // maximum dynamic header table size is truncated to v. 137 func (e *Encoder) SetMaxDynamicTableSizeLimit(v uint32) { 138 e.maxSizeLimit = v 139 if e.dynTab.maxSize > v { 140 e.tableSizeUpdate = true 141 e.dynTab.setMaxSize(v) 142 } 143 } 144 145 // shouldIndex reports whether f should be indexed. 146 func (e *Encoder) shouldIndex(f HeaderField) bool { 147 return !f.Sensitive && f.size() <= e.dynTab.maxSize 148 } 149 150 // appendIndexed appends index i, as encoded in "Indexed Header Field" 151 // representation, to dst and returns the extended buffer. 152 func appendIndexed(dst []byte, i uint64) []byte { 153 first := len(dst) 154 dst = appendVarInt(dst, 7, i) 155 dst[first] |= 0x80 156 return dst 157 } 158 159 // appendNewName appends f, as encoded in one of "Literal Header field 160 // - New Name" representation variants, to dst and returns the 161 // extended buffer. 162 // 163 // If f.Sensitive is true, "Never Indexed" representation is used. If 164 // f.Sensitive is false and indexing is true, "Inremental Indexing" 165 // representation is used. 166 func appendNewName(dst []byte, f HeaderField, indexing bool) []byte { 167 dst = append(dst, encodeTypeByte(indexing, f.Sensitive)) 168 dst = appendHpackString(dst, f.Name) 169 return appendHpackString(dst, f.Value) 170 } 171 172 // appendIndexedName appends f and index i referring indexed name 173 // entry, as encoded in one of "Literal Header field - Indexed Name" 174 // representation variants, to dst and returns the extended buffer. 175 // 176 // If f.Sensitive is true, "Never Indexed" representation is used. If 177 // f.Sensitive is false and indexing is true, "Incremental Indexing" 178 // representation is used. 179 func appendIndexedName(dst []byte, f HeaderField, i uint64, indexing bool) []byte { 180 first := len(dst) 181 var n byte 182 if indexing { 183 n = 6 184 } else { 185 n = 4 186 } 187 dst = appendVarInt(dst, n, i) 188 dst[first] |= encodeTypeByte(indexing, f.Sensitive) 189 return appendHpackString(dst, f.Value) 190 } 191 192 // appendTableSize appends v, as encoded in "Header Table Size Update" 193 // representation, to dst and returns the extended buffer. 194 func appendTableSize(dst []byte, v uint32) []byte { 195 first := len(dst) 196 dst = appendVarInt(dst, 5, uint64(v)) 197 dst[first] |= 0x20 198 return dst 199 } 200 201 // appendVarInt appends i, as encoded in variable integer form using n 202 // bit prefix, to dst and returns the extended buffer. 203 // 204 // See 205 // http://http2.github.io/http2-spec/compression.html#integer.representation 206 func appendVarInt(dst []byte, n byte, i uint64) []byte { 207 k := uint64((1 << n) - 1) 208 if i < k { 209 return append(dst, byte(i)) 210 } 211 dst = append(dst, byte(k)) 212 i -= k 213 for ; i >= 128; i >>= 7 { 214 dst = append(dst, byte(0x80|(i&0x7f))) 215 } 216 return append(dst, byte(i)) 217 } 218 219 // appendHpackString appends s, as encoded in "String Literal" 220 // representation, to dst and returns the the extended buffer. 221 // 222 // s will be encoded in Huffman codes only when it produces strictly 223 // shorter byte string. 224 func appendHpackString(dst []byte, s string) []byte { 225 huffmanLength := HuffmanEncodeLength(s) 226 if huffmanLength < uint64(len(s)) { 227 first := len(dst) 228 dst = appendVarInt(dst, 7, huffmanLength) 229 dst = AppendHuffmanString(dst, s) 230 dst[first] |= 0x80 231 } else { 232 dst = appendVarInt(dst, 7, uint64(len(s))) 233 dst = append(dst, s...) 234 } 235 return dst 236 } 237 238 // encodeTypeByte returns type byte. If sensitive is true, type byte 239 // for "Never Indexed" representation is returned. If sensitive is 240 // false and indexing is true, type byte for "Incremental Indexing" 241 // representation is returned. Otherwise, type byte for "Without 242 // Indexing" is returned. 243 func encodeTypeByte(indexing, sensitive bool) byte { 244 if sensitive { 245 return 0x10 246 } 247 if indexing { 248 return 0x40 249 } 250 return 0 251 }