| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 | 
							- // Copyright 2014 The Go Authors. All rights reserved.
 
- // Use of this source code is governed by a BSD-style
 
- // license that can be found in the LICENSE file.
 
- package hpack
 
- import (
 
- 	"io"
 
- )
 
- const (
 
- 	uint32Max              = ^uint32(0)
 
- 	initialHeaderTableSize = 4096
 
- )
 
- type Encoder struct {
 
- 	dynTab dynamicTable
 
- 	// minSize is the minimum table size set by
 
- 	// SetMaxDynamicTableSize after the previous Header Table Size
 
- 	// Update.
 
- 	minSize uint32
 
- 	// maxSizeLimit is the maximum table size this encoder
 
- 	// supports. This will protect the encoder from too large
 
- 	// size.
 
- 	maxSizeLimit uint32
 
- 	// tableSizeUpdate indicates whether "Header Table Size
 
- 	// Update" is required.
 
- 	tableSizeUpdate bool
 
- 	w               io.Writer
 
- 	buf             []byte
 
- }
 
- // NewEncoder returns a new Encoder which performs HPACK encoding. An
 
- // encoded data is written to w.
 
- func NewEncoder(w io.Writer) *Encoder {
 
- 	e := &Encoder{
 
- 		minSize:         uint32Max,
 
- 		maxSizeLimit:    initialHeaderTableSize,
 
- 		tableSizeUpdate: false,
 
- 		w:               w,
 
- 	}
 
- 	e.dynTab.table.init()
 
- 	e.dynTab.setMaxSize(initialHeaderTableSize)
 
- 	return e
 
- }
 
- // WriteField encodes f into a single Write to e's underlying Writer.
 
- // This function may also produce bytes for "Header Table Size Update"
 
- // if necessary. If produced, it is done before encoding f.
 
- func (e *Encoder) WriteField(f HeaderField) error {
 
- 	e.buf = e.buf[:0]
 
- 	if e.tableSizeUpdate {
 
- 		e.tableSizeUpdate = false
 
- 		if e.minSize < e.dynTab.maxSize {
 
- 			e.buf = appendTableSize(e.buf, e.minSize)
 
- 		}
 
- 		e.minSize = uint32Max
 
- 		e.buf = appendTableSize(e.buf, e.dynTab.maxSize)
 
- 	}
 
- 	idx, nameValueMatch := e.searchTable(f)
 
- 	if nameValueMatch {
 
- 		e.buf = appendIndexed(e.buf, idx)
 
- 	} else {
 
- 		indexing := e.shouldIndex(f)
 
- 		if indexing {
 
- 			e.dynTab.add(f)
 
- 		}
 
- 		if idx == 0 {
 
- 			e.buf = appendNewName(e.buf, f, indexing)
 
- 		} else {
 
- 			e.buf = appendIndexedName(e.buf, f, idx, indexing)
 
- 		}
 
- 	}
 
- 	n, err := e.w.Write(e.buf)
 
- 	if err == nil && n != len(e.buf) {
 
- 		err = io.ErrShortWrite
 
- 	}
 
- 	return err
 
- }
 
- // searchTable searches f in both stable and dynamic header tables.
 
- // The static header table is searched first. Only when there is no
 
- // exact match for both name and value, the dynamic header table is
 
- // then searched. If there is no match, i is 0. If both name and value
 
- // match, i is the matched index and nameValueMatch becomes true. If
 
- // only name matches, i points to that index and nameValueMatch
 
- // becomes false.
 
- func (e *Encoder) searchTable(f HeaderField) (i uint64, nameValueMatch bool) {
 
- 	i, nameValueMatch = staticTable.search(f)
 
- 	if nameValueMatch {
 
- 		return i, true
 
- 	}
 
- 	j, nameValueMatch := e.dynTab.table.search(f)
 
- 	if nameValueMatch || (i == 0 && j != 0) {
 
- 		return j + uint64(staticTable.len()), nameValueMatch
 
- 	}
 
- 	return i, false
 
- }
 
- // SetMaxDynamicTableSize changes the dynamic header table size to v.
 
- // The actual size is bounded by the value passed to
 
- // SetMaxDynamicTableSizeLimit.
 
- func (e *Encoder) SetMaxDynamicTableSize(v uint32) {
 
- 	if v > e.maxSizeLimit {
 
- 		v = e.maxSizeLimit
 
- 	}
 
- 	if v < e.minSize {
 
- 		e.minSize = v
 
- 	}
 
- 	e.tableSizeUpdate = true
 
- 	e.dynTab.setMaxSize(v)
 
- }
 
- // SetMaxDynamicTableSizeLimit changes the maximum value that can be
 
- // specified in SetMaxDynamicTableSize to v. By default, it is set to
 
- // 4096, which is the same size of the default dynamic header table
 
- // size described in HPACK specification. If the current maximum
 
- // dynamic header table size is strictly greater than v, "Header Table
 
- // Size Update" will be done in the next WriteField call and the
 
- // maximum dynamic header table size is truncated to v.
 
- func (e *Encoder) SetMaxDynamicTableSizeLimit(v uint32) {
 
- 	e.maxSizeLimit = v
 
- 	if e.dynTab.maxSize > v {
 
- 		e.tableSizeUpdate = true
 
- 		e.dynTab.setMaxSize(v)
 
- 	}
 
- }
 
- // shouldIndex reports whether f should be indexed.
 
- func (e *Encoder) shouldIndex(f HeaderField) bool {
 
- 	return !f.Sensitive && f.Size() <= e.dynTab.maxSize
 
- }
 
- // appendIndexed appends index i, as encoded in "Indexed Header Field"
 
- // representation, to dst and returns the extended buffer.
 
- func appendIndexed(dst []byte, i uint64) []byte {
 
- 	first := len(dst)
 
- 	dst = appendVarInt(dst, 7, i)
 
- 	dst[first] |= 0x80
 
- 	return dst
 
- }
 
- // appendNewName appends f, as encoded in one of "Literal Header field
 
- // - New Name" representation variants, to dst and returns the
 
- // extended buffer.
 
- //
 
- // If f.Sensitive is true, "Never Indexed" representation is used. If
 
- // f.Sensitive is false and indexing is true, "Inremental Indexing"
 
- // representation is used.
 
- func appendNewName(dst []byte, f HeaderField, indexing bool) []byte {
 
- 	dst = append(dst, encodeTypeByte(indexing, f.Sensitive))
 
- 	dst = appendHpackString(dst, f.Name)
 
- 	return appendHpackString(dst, f.Value)
 
- }
 
- // appendIndexedName appends f and index i referring indexed name
 
- // entry, as encoded in one of "Literal Header field - Indexed Name"
 
- // representation variants, to dst and returns the extended buffer.
 
- //
 
- // If f.Sensitive is true, "Never Indexed" representation is used. If
 
- // f.Sensitive is false and indexing is true, "Incremental Indexing"
 
- // representation is used.
 
- func appendIndexedName(dst []byte, f HeaderField, i uint64, indexing bool) []byte {
 
- 	first := len(dst)
 
- 	var n byte
 
- 	if indexing {
 
- 		n = 6
 
- 	} else {
 
- 		n = 4
 
- 	}
 
- 	dst = appendVarInt(dst, n, i)
 
- 	dst[first] |= encodeTypeByte(indexing, f.Sensitive)
 
- 	return appendHpackString(dst, f.Value)
 
- }
 
- // appendTableSize appends v, as encoded in "Header Table Size Update"
 
- // representation, to dst and returns the extended buffer.
 
- func appendTableSize(dst []byte, v uint32) []byte {
 
- 	first := len(dst)
 
- 	dst = appendVarInt(dst, 5, uint64(v))
 
- 	dst[first] |= 0x20
 
- 	return dst
 
- }
 
- // appendVarInt appends i, as encoded in variable integer form using n
 
- // bit prefix, to dst and returns the extended buffer.
 
- //
 
- // See
 
- // http://http2.github.io/http2-spec/compression.html#integer.representation
 
- func appendVarInt(dst []byte, n byte, i uint64) []byte {
 
- 	k := uint64((1 << n) - 1)
 
- 	if i < k {
 
- 		return append(dst, byte(i))
 
- 	}
 
- 	dst = append(dst, byte(k))
 
- 	i -= k
 
- 	for ; i >= 128; i >>= 7 {
 
- 		dst = append(dst, byte(0x80|(i&0x7f)))
 
- 	}
 
- 	return append(dst, byte(i))
 
- }
 
- // appendHpackString appends s, as encoded in "String Literal"
 
- // representation, to dst and returns the the extended buffer.
 
- //
 
- // s will be encoded in Huffman codes only when it produces strictly
 
- // shorter byte string.
 
- func appendHpackString(dst []byte, s string) []byte {
 
- 	huffmanLength := HuffmanEncodeLength(s)
 
- 	if huffmanLength < uint64(len(s)) {
 
- 		first := len(dst)
 
- 		dst = appendVarInt(dst, 7, huffmanLength)
 
- 		dst = AppendHuffmanString(dst, s)
 
- 		dst[first] |= 0x80
 
- 	} else {
 
- 		dst = appendVarInt(dst, 7, uint64(len(s)))
 
- 		dst = append(dst, s...)
 
- 	}
 
- 	return dst
 
- }
 
- // encodeTypeByte returns type byte. If sensitive is true, type byte
 
- // for "Never Indexed" representation is returned. If sensitive is
 
- // false and indexing is true, type byte for "Incremental Indexing"
 
- // representation is returned. Otherwise, type byte for "Without
 
- // Indexing" is returned.
 
- func encodeTypeByte(indexing, sensitive bool) byte {
 
- 	if sensitive {
 
- 		return 0x10
 
- 	}
 
- 	if indexing {
 
- 		return 0x40
 
- 	}
 
- 	return 0
 
- }
 
 
  |