encode.mx raw

   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 []byte) []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