gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

encode.go (7267B)


      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 }