diff options
Diffstat (limited to 'protocol/pktline/decoder.go')
| -rw-r--r-- | protocol/pktline/decoder.go | 187 |
1 files changed, 0 insertions, 187 deletions
diff --git a/protocol/pktline/decoder.go b/protocol/pktline/decoder.go deleted file mode 100644 index 898d8ad6..00000000 --- a/protocol/pktline/decoder.go +++ /dev/null @@ -1,187 +0,0 @@ -package pktline - -import ( - "errors" - "fmt" - "io" -) - -// ReadOptions controls decoding behavior. -type ReadOptions struct { - // ChompLF removes one trailing '\n' from PacketData payloads. - ChompLF bool -} - -// Decoder reads pkt-line frames from an io.Reader. -// -// It is advisable to supply a buffered reader. -// -// It preserves frame boundaries and supports one-frame lookahead via PeekFrame. -type Decoder struct { - r io.Reader - maxData int - opts ReadOptions - - peeked bool - peek Frame - peekErr error -} - -// NewDecoder creates a decoder over r. -func NewDecoder(r io.Reader, opts ReadOptions) *Decoder { - return &Decoder{ - r: r, - maxData: LargePacketDataMax, - opts: opts, - } -} - -// SetMaxData sets maximum payload size accepted for one data packet. -// -// Non-positive n resets to LargePacketDataMax. -func (d *Decoder) SetMaxData(n int) { - if n <= 0 { - d.maxData = LargePacketDataMax - - return - } - - d.maxData = n -} - -func cloneFrame(f Frame) Frame { - if f.Type != PacketData { - return Frame{Type: f.Type} - } - - out := Frame{Type: f.Type} - if f.Payload != nil { - out.Payload = append([]byte(nil), f.Payload...) - } - - return out -} - -// ReadFrame reads one frame. -// -// 0000 is a PacketFlush -// 0001 is a PacketDelim -// 0002 is a PacketResponseEnd -// 0004 is a PacketData with empty payload -// -// 0003 and malformed headers return *ProtocolError. -func (d *Decoder) ReadFrame() (Frame, error) { - if d.peeked { - d.peeked = false - - return cloneFrame(d.peek), d.peekErr - } - - return d.readFrame() -} - -// PeekFrame returns the next frame without consuming it. -// -// A subsequent ReadFrame returns the same frame. -func (d *Decoder) PeekFrame() (Frame, error) { - if !d.peeked { - d.peek, d.peekErr = d.readFrame() - d.peeked = true - } - - return cloneFrame(d.peek), d.peekErr -} - -func (d *Decoder) readFrame() (Frame, error) { - var hdr [4]byte - - _, err := io.ReadFull(d.r, hdr[:]) - if err != nil { - if errors.Is(err, io.EOF) { - return Frame{}, io.EOF - } - - if errors.Is(err, io.ErrUnexpectedEOF) { - return Frame{}, io.ErrUnexpectedEOF - } - - return Frame{}, err - } - - n, err := ParseLengthHeader(hdr) - if err != nil { - return Frame{}, &ProtocolError{Header: hdr, Reason: err.Error()} - } - - switch n { - case 0: - return Frame{Type: PacketFlush}, nil - case 1: - return Frame{Type: PacketDelim}, nil - case 2: - return Frame{Type: PacketResponseEnd}, nil - case 3: - return Frame{}, &ProtocolError{Header: hdr, Reason: "invalid pkt-line length 3"} - } - - if n < 4 { - return Frame{}, &ProtocolError{Header: hdr, Reason: fmt.Sprintf("invalid pkt-line length %d", n)} - } - - if n > LargePacketMax { - perr := &ProtocolError{Header: hdr, Reason: fmt.Sprintf("pkt-line length %d exceeds max %d", n, LargePacketMax)} - - err := d.discardPayload(n - 4) - if err != nil { - return Frame{}, errors.Join(perr, err) - } - - return Frame{}, perr - } - - payloadLen := n - 4 - if payloadLen > d.maxData { - serr := fmt.Errorf("%w: %d > %d", ErrTooLarge, payloadLen, d.maxData) - - err := d.discardPayload(payloadLen) - if err != nil { - return Frame{}, errors.Join(serr, err) - } - - return Frame{}, serr - } - - payload := make([]byte, payloadLen) - - _, err = io.ReadFull(d.r, payload) - if err != nil { - if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { - return Frame{}, io.ErrUnexpectedEOF - } - - return Frame{}, err - } - - if d.opts.ChompLF && len(payload) > 0 && payload[len(payload)-1] == '\n' { - payload = payload[:len(payload)-1] - } - - return Frame{Type: PacketData, Payload: payload}, nil -} - -func (d *Decoder) discardPayload(n int) error { - if n <= 0 { - return nil - } - - _, err := io.CopyN(io.Discard, d.r, int64(n)) - if err == nil { - return nil - } - - if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { - return io.ErrUnexpectedEOF - } - - return err -} |
