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  }