package wire import ( "bytes" "io" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/qerr" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("STREAM frame (for gQUIC)", func() { Context("when parsing", func() { It("accepts a sample frame", func() { // a STREAM frame, plus 3 additional bytes, not belonging to this frame b := bytes.NewReader([]byte{0x80 ^ 0x20, 0x1, // stream id 0x0, 0x6, // data length 'f', 'o', 'o', 'b', 'a', 'r', 'f', 'o', 'o', // additional bytes }) frame, err := ParseStreamFrame(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(frame.FinBit).To(BeFalse()) Expect(frame.StreamID).To(Equal(protocol.StreamID(1))) Expect(frame.Offset).To(BeZero()) Expect(frame.DataLenPresent).To(BeTrue()) Expect(frame.Data).To(Equal([]byte("foobar"))) Expect(b.Len()).To(Equal(3)) }) It("accepts frames with offsets", func() { b := bytes.NewReader([]byte{0x80 ^ 0x20 /* 2 byte offset */ ^ 0x4, 0x1, // stream id 0x0, 0x42, // offset 0x0, 0x6, // data length 'f', 'o', 'o', 'b', 'a', 'r', }) frame, err := ParseStreamFrame(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(frame.FinBit).To(BeFalse()) Expect(frame.StreamID).To(Equal(protocol.StreamID(1))) Expect(frame.Offset).To(Equal(protocol.ByteCount(0x42))) Expect(frame.DataLenPresent).To(BeTrue()) Expect(frame.Data).To(Equal([]byte("foobar"))) Expect(b.Len()).To(BeZero()) }) It("errors on EOFs", func() { data := []byte{0x80 ^ 0x20 ^ 0x4, 0x1, // stream id 0x0, 0x2a, // offset 0x0, 0x6, // data length, 'f', 'o', 'o', 'b', 'a', 'r', } _, err := ParseStreamFrame(bytes.NewReader(data), versionBigEndian) Expect(err).NotTo(HaveOccurred()) for i := range data { _, err := ParseStreamFrame(bytes.NewReader(data[0:i]), versionBigEndian) Expect(err).To(HaveOccurred()) } }) It("accepts frame without data length", func() { b := bytes.NewReader([]byte{0x80, 0x1, // stream id 'f', 'o', 'o', 'b', 'a', 'r', }) frame, err := ParseStreamFrame(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(frame.FinBit).To(BeFalse()) Expect(frame.StreamID).To(Equal(protocol.StreamID(1))) Expect(frame.Offset).To(BeZero()) Expect(frame.DataLenPresent).To(BeFalse()) Expect(frame.Data).To(Equal([]byte("foobar"))) Expect(b.Len()).To(BeZero()) }) It("accepts an empty frame with FinBit set, with data length set", func() { // the STREAM frame, plus 3 additional bytes, not belonging to this frame b := bytes.NewReader([]byte{0x80 ^ 0x40 ^ 0x20, 0x1, // stream id 0, 0, // data length 'f', 'o', 'o', // additional bytes }) frame, err := ParseStreamFrame(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(frame.FinBit).To(BeTrue()) Expect(frame.DataLenPresent).To(BeTrue()) Expect(frame.Data).To(BeEmpty()) Expect(b.Len()).To(Equal(3)) }) It("accepts an empty frame with the FinBit set", func() { b := bytes.NewReader([]byte{0x80 ^ 0x40, 0x1, // stream id 'f', 'o', 'o', 'b', 'a', 'r', }) frame, err := ParseStreamFrame(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(frame.FinBit).To(BeTrue()) Expect(frame.DataLenPresent).To(BeFalse()) Expect(frame.Data).To(Equal([]byte("foobar"))) Expect(b.Len()).To(BeZero()) }) It("errors on empty stream frames that don't have the FinBit set", func() { b := bytes.NewReader([]byte{0x80 ^ 0x20, 0x1, // stream id 0, 0, // data length }) _, err := ParseStreamFrame(b, versionBigEndian) Expect(err).To(MatchError(qerr.EmptyStreamFrameNoFin)) }) It("rejects frames to too large dataLen", func() { b := bytes.NewReader([]byte{0xa0, 0x1, 0xff, 0xff}) _, err := ParseStreamFrame(b, versionBigEndian) Expect(err).To(MatchError(io.EOF)) }) It("rejects frames that overflow the offset", func() { // Offset + len(Data) overflows MaxByteCount f := &StreamFrame{ StreamID: 1, Offset: protocol.MaxByteCount, Data: []byte{'f'}, } b := &bytes.Buffer{} err := f.Write(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) _, err = ParseStreamFrame(bytes.NewReader(b.Bytes()), versionBigEndian) Expect(err).To(MatchError(qerr.Error(qerr.InvalidStreamData, "data overflows maximum offset"))) }) }) Context("when writing", func() { Context("in big endian", func() { It("writes sample frame", func() { b := &bytes.Buffer{} err := (&StreamFrame{ StreamID: 1, Data: []byte("foobar"), DataLenPresent: true, }).Write(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()).To(Equal([]byte{0x80 ^ 0x20, 0x1, // stream id 0x0, 0x6, // data length 'f', 'o', 'o', 'b', 'a', 'r', })) }) }) It("sets the FinBit", func() { b := &bytes.Buffer{} err := (&StreamFrame{ StreamID: 1, Data: []byte("foobar"), FinBit: true, }).Write(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()[0] & 0x40).To(Equal(byte(0x40))) }) It("errors when length is zero and FIN is not set", func() { b := &bytes.Buffer{} err := (&StreamFrame{ StreamID: 1, }).Write(b, versionBigEndian) Expect(err).To(MatchError("StreamFrame: attempting to write empty frame without FIN")) }) It("has proper min length for a short StreamID and a short offset", func() { b := &bytes.Buffer{} f := &StreamFrame{ StreamID: 1, Data: []byte{}, Offset: 0, FinBit: true, } err := f.Write(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(f.MinLength(0)).To(Equal(protocol.ByteCount(b.Len()))) }) It("has proper min length for a long StreamID and a big offset", func() { b := &bytes.Buffer{} f := &StreamFrame{ StreamID: 0xdecafbad, Data: []byte{}, Offset: 0xdeadbeefcafe, FinBit: true, } err := f.Write(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(f.MinLength(versionBigEndian)).To(Equal(protocol.ByteCount(b.Len()))) }) Context("data length field", func() { It("writes the data length", func() { dataLen := 0x1337 b := &bytes.Buffer{} f := &StreamFrame{ StreamID: 1, Data: bytes.Repeat([]byte{'f'}, dataLen), DataLenPresent: true, Offset: 0, } err := f.Write(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) minLength := f.MinLength(0) Expect(b.Bytes()[0] & 0x20).To(Equal(uint8(0x20))) Expect(b.Bytes()[minLength-2 : minLength]).To(Equal([]byte{0x13, 0x37})) }) }) It("omits the data length field", func() { dataLen := 0x1337 b := &bytes.Buffer{} f := &StreamFrame{ StreamID: 1, Data: bytes.Repeat([]byte{'f'}, dataLen), DataLenPresent: false, Offset: 0, } err := f.Write(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()[0] & 0x20).To(Equal(uint8(0))) Expect(b.Bytes()[1 : b.Len()-dataLen]).ToNot(ContainSubstring(string([]byte{0x37, 0x13}))) minLength := f.MinLength(versionBigEndian) f.DataLenPresent = true minLengthWithoutDataLen := f.MinLength(versionBigEndian) Expect(minLength).To(Equal(minLengthWithoutDataLen - 2)) }) It("calculates the correct min-length", func() { f := &StreamFrame{ StreamID: 0xcafe, Data: []byte("foobar"), DataLenPresent: false, Offset: 0xdeadbeef, } minLengthWithoutDataLen := f.MinLength(versionBigEndian) f.DataLenPresent = true Expect(f.MinLength(versionBigEndian)).To(Equal(minLengthWithoutDataLen + 2)) }) Context("offset lengths", func() { It("does not write an offset if the offset is 0", func() { b := &bytes.Buffer{} err := (&StreamFrame{ StreamID: 1, Data: []byte("foobar"), Offset: 0, }).Write(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x0))) }) It("writes a 2-byte offset if the offset is larger than 0", func() { b := &bytes.Buffer{} err := (&StreamFrame{ StreamID: 1, Data: []byte("foobar"), Offset: 0x1337, }).Write(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x1 << 2))) Expect(b.Bytes()[2:4]).To(Equal([]byte{0x13, 0x37})) }) It("writes a 3-byte offset if the offset", func() { b := &bytes.Buffer{} (&StreamFrame{ StreamID: 1, Data: []byte("foobar"), Offset: 0x13cafe, }).Write(b, versionBigEndian) Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x2 << 2))) Expect(b.Bytes()[2:5]).To(Equal([]byte{0x13, 0xca, 0xfe})) }) It("writes a 4-byte offset if the offset", func() { b := &bytes.Buffer{} err := (&StreamFrame{ StreamID: 1, Data: []byte("foobar"), Offset: 0xdeadbeef, }).Write(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x3 << 2))) Expect(b.Bytes()[2:6]).To(Equal([]byte{0xde, 0xad, 0xbe, 0xef})) }) It("writes a 5-byte offset if the offset", func() { b := &bytes.Buffer{} err := (&StreamFrame{ StreamID: 1, Data: []byte("foobar"), Offset: 0x13deadbeef, }).Write(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x4 << 2))) Expect(b.Bytes()[2:7]).To(Equal([]byte{0x13, 0xde, 0xad, 0xbe, 0xef})) }) It("writes a 6-byte offset if the offset", func() { b := &bytes.Buffer{} err := (&StreamFrame{ StreamID: 1, Data: []byte("foobar"), Offset: 0xdeadbeefcafe, }).Write(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x5 << 2))) Expect(b.Bytes()[2:8]).To(Equal([]byte{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe})) }) It("writes a 7-byte offset if the offset", func() { b := &bytes.Buffer{} err := (&StreamFrame{ StreamID: 1, Data: []byte("foobar"), Offset: 0x13deadbeefcafe, }).Write(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x6 << 2))) Expect(b.Bytes()[2:9]).To(Equal([]byte{0x13, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe})) }) It("writes a 8-byte offset if the offset", func() { b := &bytes.Buffer{} err := (&StreamFrame{ StreamID: 1, Data: []byte("foobar"), Offset: 0x1337deadbeefcafe, }).Write(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()[0] & 0x1c).To(Equal(uint8(0x7 << 2))) Expect(b.Bytes()[2:10]).To(Equal([]byte{0x13, 0x37, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe})) }) }) Context("lengths of StreamIDs", func() { It("writes a 1 byte StreamID", func() { b := &bytes.Buffer{} err := (&StreamFrame{ StreamID: 13, Data: []byte("foobar"), }).Write(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()[0] & 0x3).To(Equal(uint8(0x0))) Expect(b.Bytes()[1]).To(Equal(uint8(13))) }) It("writes a 2 byte StreamID", func() { b := &bytes.Buffer{} err := (&StreamFrame{ StreamID: 0xcafe, Data: []byte("foobar"), }).Write(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()[0] & 0x3).To(Equal(uint8(0x1))) Expect(b.Bytes()[1:3]).To(Equal([]byte{0xca, 0xfe})) }) It("writes a 3 byte StreamID", func() { b := &bytes.Buffer{} err := (&StreamFrame{ StreamID: 0x13beef, Data: []byte("foobar"), }).Write(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()[0] & 0x3).To(Equal(uint8(0x2))) Expect(b.Bytes()[1:4]).To(Equal([]byte{0x13, 0xbe, 0xef})) }) It("writes a 4 byte StreamID", func() { b := &bytes.Buffer{} err := (&StreamFrame{ StreamID: 0xdecafbad, Data: []byte("foobar"), }).Write(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()[0] & 0x3).To(Equal(uint8(0x3))) Expect(b.Bytes()[1:5]).To(Equal([]byte{0xde, 0xca, 0xfb, 0xad})) }) It("writes a multiple byte StreamID, after the Stream length was already determined by MinLenght()", func() { b := &bytes.Buffer{} frame := &StreamFrame{ StreamID: 0xdecafbad, Data: []byte("foobar"), } frame.MinLength(0) err := frame.Write(b, versionBigEndian) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()[0] & 0x3).To(Equal(uint8(0x3))) Expect(b.Bytes()[1:5]).To(Equal([]byte{0xde, 0xca, 0xfb, 0xad})) }) }) }) Context("shortening of StreamIDs", func() { It("determines the length of a 1 byte StreamID", func() { f := &StreamFrame{StreamID: 0xFF} Expect(f.calculateStreamIDLength()).To(Equal(uint8(1))) }) It("determines the length of a 2 byte StreamID", func() { f := &StreamFrame{StreamID: 0xFFFF} Expect(f.calculateStreamIDLength()).To(Equal(uint8(2))) }) It("determines the length of a 3 byte StreamID", func() { f := &StreamFrame{StreamID: 0xFFFFFF} Expect(f.calculateStreamIDLength()).To(Equal(uint8(3))) }) It("determines the length of a 4 byte StreamID", func() { f := &StreamFrame{StreamID: 0xFFFFFFFF} Expect(f.calculateStreamIDLength()).To(Equal(uint8(4))) }) }) Context("shortening of Offsets", func() { It("determines length 0 of offset 0", func() { f := &StreamFrame{Offset: 0} Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(0))) }) It("determines the length of a 2 byte offset", func() { f := &StreamFrame{Offset: 0xFFFF} Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(2))) }) It("determines the length of a 2 byte offset, even if it would fit into 1 byte", func() { f := &StreamFrame{Offset: 0x1} Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(2))) }) It("determines the length of a 3 byte offset", func() { f := &StreamFrame{Offset: 0xFFFFFF} Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(3))) }) It("determines the length of a 4 byte offset", func() { f := &StreamFrame{Offset: 0xFFFFFFFF} Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(4))) }) It("determines the length of a 5 byte offset", func() { f := &StreamFrame{Offset: 0xFFFFFFFFFF} Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(5))) }) It("determines the length of a 6 byte offset", func() { f := &StreamFrame{Offset: 0xFFFFFFFFFFFF} Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(6))) }) It("determines the length of a 7 byte offset", func() { f := &StreamFrame{Offset: 0xFFFFFFFFFFFFFF} Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(7))) }) It("determines the length of an 8 byte offset", func() { f := &StreamFrame{Offset: 0xFFFFFFFFFFFFFFFF} Expect(f.getOffsetLength()).To(Equal(protocol.ByteCount(8))) }) }) Context("DataLen", func() { It("determines the length of the data", func() { frame := StreamFrame{ Data: []byte("foobar"), } Expect(frame.DataLen()).To(Equal(protocol.ByteCount(6))) }) }) })