package qpack
import (
"bytes"
"errors"
"fmt"
"sync"
"golang.org/x/net/http2/hpack"
)
type decodingError struct {
err error
}
func (de decodingError ) Error () string {
return fmt .Sprintf ("decoding error: %v" , de .err )
}
type invalidIndexError int
func (e invalidIndexError ) Error () string {
return fmt .Sprintf ("invalid indexed representation index %d" , int (e ))
}
var errNoDynamicTable = decodingError {errors .New ("no dynamic table" )}
var errNeedMore = errors .New ("need more data" )
type Decoder struct {
mutex sync .Mutex
emitFunc func (f HeaderField )
readRequiredInsertCount bool
readDeltaBase bool
buf []byte
saveBuf bytes .Buffer
}
func NewDecoder (emitFunc func (f HeaderField )) *Decoder {
return &Decoder {emitFunc : emitFunc }
}
func (d *Decoder ) Write (p []byte ) (int , error ) {
if len (p ) == 0 {
return 0 , nil
}
d .mutex .Lock ()
n , err := d .writeLocked (p )
d .mutex .Unlock ()
return n , err
}
func (d *Decoder ) writeLocked (p []byte ) (int , error ) {
if d .saveBuf .Len () == 0 {
d .buf = p
} else {
d .saveBuf .Write (p )
d .buf = d .saveBuf .Bytes ()
d .saveBuf .Reset ()
}
if err := d .decode (); err != nil {
if err != errNeedMore {
return 0 , err
}
d .saveBuf .Write (d .buf )
}
return len (p ), nil
}
func (d *Decoder ) DecodeFull (p []byte ) ([]HeaderField , error ) {
if len (p ) == 0 {
return []HeaderField {}, nil
}
d .mutex .Lock ()
defer d .mutex .Unlock ()
saveFunc := d .emitFunc
defer func () { d .emitFunc = saveFunc }()
var hf []HeaderField
d .emitFunc = func (f HeaderField ) { hf = append (hf , f ) }
if _ , err := d .writeLocked (p ); err != nil {
return nil , err
}
if err := d .Close (); err != nil {
return nil , err
}
return hf , nil
}
func (d *Decoder ) Close () error {
if d .saveBuf .Len () > 0 {
d .saveBuf .Reset ()
return decodingError {errors .New ("truncated headers" )}
}
d .readRequiredInsertCount = false
d .readDeltaBase = false
return nil
}
func (d *Decoder ) decode () error {
if !d .readRequiredInsertCount {
requiredInsertCount , rest , err := readVarInt (8 , d .buf )
if err != nil {
return err
}
d .readRequiredInsertCount = true
if requiredInsertCount != 0 {
return decodingError {errors .New ("expected Required Insert Count to be zero" )}
}
d .buf = rest
}
if !d .readDeltaBase {
base , rest , err := readVarInt (7 , d .buf )
if err != nil {
return err
}
d .readDeltaBase = true
if base != 0 {
return decodingError {errors .New ("expected Base to be zero" )}
}
d .buf = rest
}
if len (d .buf ) == 0 {
return errNeedMore
}
for len (d .buf ) > 0 {
b := d .buf [0 ]
var err error
switch {
case b &0x80 > 0 :
err = d .parseIndexedHeaderField ()
case b &0xc0 == 0x40 :
err = d .parseLiteralHeaderField ()
case b &0xe0 == 0x20 :
err = d .parseLiteralHeaderFieldWithoutNameReference ()
default :
err = fmt .Errorf ("unexpected type byte: %#x" , b )
}
if err != nil {
return err
}
}
return nil
}
func (d *Decoder ) parseIndexedHeaderField () error {
buf := d .buf
if buf [0 ]&0x40 == 0 {
return errNoDynamicTable
}
index , buf , err := readVarInt (6 , buf )
if err != nil {
return err
}
hf , ok := d .at (index )
if !ok {
return decodingError {invalidIndexError (index )}
}
d .emitFunc (hf )
d .buf = buf
return nil
}
func (d *Decoder ) parseLiteralHeaderField () error {
buf := d .buf
if buf [0 ]&0x10 == 0 {
return errNoDynamicTable
}
index , buf , err := readVarInt (4 , buf )
if err != nil {
return err
}
hf , ok := d .at (index )
if !ok {
return decodingError {invalidIndexError (index )}
}
if len (buf ) == 0 {
return errNeedMore
}
usesHuffman := buf [0 ]&0x80 > 0
val , buf , err := d .readString (buf , 7 , usesHuffman )
if err != nil {
return err
}
hf .Value = val
d .emitFunc (hf )
d .buf = buf
return nil
}
func (d *Decoder ) parseLiteralHeaderFieldWithoutNameReference () error {
buf := d .buf
usesHuffmanForName := buf [0 ]&0x8 > 0
name , buf , err := d .readString (buf , 3 , usesHuffmanForName )
if err != nil {
return err
}
if len (buf ) == 0 {
return errNeedMore
}
usesHuffmanForVal := buf [0 ]&0x80 > 0
val , buf , err := d .readString (buf , 7 , usesHuffmanForVal )
if err != nil {
return err
}
d .emitFunc (HeaderField {Name : name , Value : val })
d .buf = buf
return nil
}
func (d *Decoder ) readString (buf []byte , n uint8 , usesHuffman bool ) (string , []byte , error ) {
l , buf , err := readVarInt (n , buf )
if err != nil {
return "" , nil , err
}
if uint64 (len (buf )) < l {
return "" , nil , errNeedMore
}
var val string
if usesHuffman {
var err error
val , err = hpack .HuffmanDecodeToString (buf [:l ])
if err != nil {
return "" , nil , err
}
} else {
val = string (buf [:l ])
}
buf = buf [l :]
return val , buf , nil
}
func (d *Decoder ) at (i uint64 ) (hf HeaderField , ok bool ) {
if i >= uint64 (len (staticTableEntries )) {
return
}
return staticTableEntries [i ], true
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .