golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/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.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  }