| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495 |
- package qpack
- import (
- "io"
- "golang.org/x/net/http2/hpack"
- )
- // An Encoder performs QPACK encoding.
- type Encoder struct {
- wrotePrefix bool
- w io.Writer
- buf []byte
- }
- // NewEncoder returns a new Encoder which performs QPACK encoding. An
- // encoded data is written to w.
- func NewEncoder(w io.Writer) *Encoder {
- return &Encoder{w: w}
- }
- // WriteField encodes f into a single Write to e's underlying Writer.
- // This function may also produce bytes for the Header Block Prefix
- // if necessary. If produced, it is done before encoding f.
- func (e *Encoder) WriteField(f HeaderField) error {
- // write the Header Block Prefix
- if !e.wrotePrefix {
- e.buf = appendVarInt(e.buf, 8, 0)
- e.buf = appendVarInt(e.buf, 7, 0)
- e.wrotePrefix = true
- }
- idxAndVals, nameFound := encoderMap[f.Name]
- if nameFound {
- if idxAndVals.values == nil {
- if len(f.Value) == 0 {
- e.writeIndexedField(idxAndVals.idx)
- } else {
- e.writeLiteralFieldWithNameReference(&f, idxAndVals.idx)
- }
- } else {
- valIdx, valueFound := idxAndVals.values[f.Value]
- if valueFound {
- e.writeIndexedField(valIdx)
- } else {
- e.writeLiteralFieldWithNameReference(&f, idxAndVals.idx)
- }
- }
- } else {
- e.writeLiteralFieldWithoutNameReference(f)
- }
- _, err := e.w.Write(e.buf)
- e.buf = e.buf[:0]
- return err
- }
- // Close declares that the encoding is complete and resets the Encoder
- // to be reused again for a new header block.
- func (e *Encoder) Close() error {
- e.wrotePrefix = false
- return nil
- }
- func (e *Encoder) writeLiteralFieldWithoutNameReference(f HeaderField) {
- offset := len(e.buf)
- e.buf = appendVarInt(e.buf, 3, hpack.HuffmanEncodeLength(f.Name))
- e.buf[offset] ^= 0x20 ^ 0x8
- e.buf = hpack.AppendHuffmanString(e.buf, f.Name)
- offset = len(e.buf)
- e.buf = appendVarInt(e.buf, 7, hpack.HuffmanEncodeLength(f.Value))
- e.buf[offset] ^= 0x80
- e.buf = hpack.AppendHuffmanString(e.buf, f.Value)
- }
- // Encodes a header field whose name is present in one of the tables.
- func (e *Encoder) writeLiteralFieldWithNameReference(f *HeaderField, id uint8) {
- offset := len(e.buf)
- e.buf = appendVarInt(e.buf, 4, uint64(id))
- // Set the 01NTxxxx pattern, forcing N to 0 and T to 1
- e.buf[offset] ^= 0x50
- offset = len(e.buf)
- e.buf = appendVarInt(e.buf, 7, hpack.HuffmanEncodeLength(f.Value))
- e.buf[offset] ^= 0x80
- e.buf = hpack.AppendHuffmanString(e.buf, f.Value)
- }
- // Encodes an indexed field, meaning it's entirely defined in one of the tables.
- func (e *Encoder) writeIndexedField(id uint8) {
- offset := len(e.buf)
- e.buf = appendVarInt(e.buf, 6, uint64(id))
- // Set the 1Txxxxxx pattern, forcing T to 1
- e.buf[offset] ^= 0xc0
- }
|