diff --git a/stream.go b/stream.go new file mode 100644 index 00000000..7cfba331 --- /dev/null +++ b/stream.go @@ -0,0 +1,60 @@ +package quic + +import ( + "errors" + "io" + + "github.com/lucas-clemente/quic-go/frames" +) + +// A Stream assembles the data from StreamFrames and provides a super-convenient Read-Interface +type Stream struct { + StreamFrames []*frames.StreamFrame + DataLen uint64 + ReadPosFrameNo int + ReadPosInFrame int +} + +// NewStream creates a new Stream +func NewStream() *Stream { + return &Stream{} +} + +func (s *Stream) readByte() (byte, error) { + if s.ReadPosInFrame == len(s.StreamFrames[s.ReadPosFrameNo].Data) { + s.ReadPosFrameNo++ + if s.ReadPosFrameNo == len(s.StreamFrames) { + return 0, io.EOF + } + s.ReadPosInFrame = 0 + } + b := s.StreamFrames[s.ReadPosFrameNo].Data[s.ReadPosInFrame] + s.ReadPosInFrame++ + return b, nil +} + +// Read reads data +func (s *Stream) Read(p []byte) (int, error) { + var err error + n := 0 + if c := cap(p); c > 0 { + for n < c { + p[n], err = s.readByte() + n++ + if err != nil { + break + } + } + } + return n, nil +} + +// AddStreamFrame adds a new stream frame +func (s *Stream) AddStreamFrame(frame *frames.StreamFrame) error { + if frame.Offset != s.DataLen { + return errors.New("Stream: Wrong offset") + } + s.StreamFrames = append(s.StreamFrames, frame) + s.DataLen += uint64(len(frame.Data)) + return nil +} diff --git a/stream_test.go b/stream_test.go new file mode 100644 index 00000000..f0b15253 --- /dev/null +++ b/stream_test.go @@ -0,0 +1,59 @@ +package quic + +import ( + "github.com/lucas-clemente/quic-go/frames" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Stream", func() { + It("reads a single StreamFrame", func() { + frame := frames.StreamFrame{ + Offset: 0, + Data: []byte{0xDE, 0xAD, 0xBE, 0xEF}, + } + stream := NewStream() + stream.AddStreamFrame(&frame) + b := make([]byte, 4) + n, err := stream.Read(b) + Expect(err).ToNot(HaveOccurred()) + Expect(n).To(Equal(4)) + Expect(stream.DataLen).To(Equal(uint64(4))) + Expect(b).To(Equal([]byte{0xDE, 0xAD, 0xBE, 0xEF})) + }) + + It("assembles multiple StreamFrames", func() { + frame1 := frames.StreamFrame{ + Offset: 0, + Data: []byte{0xDE, 0xAD}, + } + frame2 := frames.StreamFrame{ + Offset: 2, + Data: []byte{0xBE, 0xEF}, + } + stream := NewStream() + stream.AddStreamFrame(&frame1) + stream.AddStreamFrame(&frame2) + b := make([]byte, 4) + n, err := stream.Read(b) + Expect(err).ToNot(HaveOccurred()) + Expect(n).To(Equal(4)) + Expect(stream.DataLen).To(Equal(uint64(4))) + Expect(b).To(Equal([]byte{0xDE, 0xAD, 0xBE, 0xEF})) + }) + + It("rejects StreamFrames with wrong Offsets", func() { + frame1 := frames.StreamFrame{ + Offset: 0, + Data: []byte{0xDE, 0xAD}, + } + frame2 := frames.StreamFrame{ + Offset: 1, + Data: []byte{0xBE, 0xEF}, + } + stream := NewStream() + stream.AddStreamFrame(&frame1) + err := stream.AddStreamFrame(&frame2) + Expect(err).To(HaveOccurred()) + }) +})