Source file src/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, "Incremental 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 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  }
   241  

View as plain text