github.com/yandex/pandora@v0.5.32/core/provider/chunk_decoder.go (about) 1 package provider 2 3 import ( 4 "bufio" 5 "fmt" 6 7 "github.com/pkg/errors" 8 "github.com/yandex/pandora/core" 9 ) 10 11 var ErrNoAmmoDecoded = fmt.Errorf("no ammo has been decoded from chunk") 12 13 // ChunkAmmoDecoder accept data chunks that can contain encoded ammo or some meta information that 14 // changes ChunkAmmoDecoder state and affects next ammo decoding. 15 // For example, chunks are lines that contains HTTP URI to be transformed to http.Request or 16 // HTTP header to be added to next decoded http.Requests. 17 type ChunkAmmoDecoder interface { 18 // DecodeChunk accepts chunk of data, than decode it to ammo or change ChunkAmmoDecoder internal 19 // state. 20 // Returns nil on when ammo was successfully decoded. 21 // ErrNoAmmoDecoded MAY be returned, to indicate that chunk was accepted, but ammo were not 22 // decoded. 23 // Returns other non nil error, on chunk decode fail. 24 // Panics if ammo type is not supported. 25 DecodeChunk(chunk []byte, ammo core.Ammo) error 26 } 27 28 func NewScanDecoder(scanner Scanner, decoder ChunkAmmoDecoder) *ScanAmmoDecoder { 29 return &ScanAmmoDecoder{scanner: scanner, decoder: decoder} 30 } 31 32 // Scanner is interface of bufio.Scanner like scanners. 33 type Scanner interface { 34 Scan() bool 35 Bytes() []byte 36 Err() error 37 } 38 39 var _ Scanner = &bufio.Scanner{} 40 41 type ScanAmmoDecoder struct { 42 chunkCounter int 43 scanner Scanner 44 decoder ChunkAmmoDecoder 45 } 46 47 var _ AmmoDecoder = &ScanAmmoDecoder{} 48 49 func (d *ScanAmmoDecoder) Decode(ammo core.Ammo) error { 50 for { 51 if !d.scanner.Scan() { 52 return d.scanner.Err() 53 } 54 chunk := d.scanner.Bytes() 55 err := d.decoder.DecodeChunk(chunk, ammo) 56 if err == ErrNoAmmoDecoded { 57 continue 58 } 59 if err != nil { 60 return errors.Wrapf(err, "chunk %v decode failed", d.chunkCounter) 61 } 62 d.chunkCounter++ 63 return nil 64 } 65 66 }