From eefd2b73f75f6f260cf5d6bc47386f2706da4194 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Sat, 5 Oct 2019 18:47:57 +0200 Subject: [PATCH] implement parsing and writing of the DATAGRAM frames --- internal/wire/datagram_frame.go | 67 +++++++++++++++++++ internal/wire/datagram_frame_test.go | 98 ++++++++++++++++++++++++++++ internal/wire/frame_parser.go | 2 + internal/wire/frame_parser_test.go | 1 + 4 files changed, 168 insertions(+) create mode 100644 internal/wire/datagram_frame.go create mode 100644 internal/wire/datagram_frame_test.go diff --git a/internal/wire/datagram_frame.go b/internal/wire/datagram_frame.go new file mode 100644 index 00000000..653167f2 --- /dev/null +++ b/internal/wire/datagram_frame.go @@ -0,0 +1,67 @@ +package wire + +import ( + "bytes" + "io" + + "github.com/lucas-clemente/quic-go/internal/protocol" + "github.com/lucas-clemente/quic-go/internal/utils" +) + +// A DatagramFrame is a DATAGRAM frame +type DatagramFrame struct { + DataLenPresent bool + Data []byte +} + +func parseDatagramFrame(r *bytes.Reader, _ protocol.VersionNumber) (*DatagramFrame, error) { + typeByte, err := r.ReadByte() + if err != nil { + return nil, err + } + + f := &DatagramFrame{} + f.DataLenPresent = typeByte&0x1 > 0 + + var length uint64 + if f.DataLenPresent { + var err error + len, err := utils.ReadVarInt(r) + if err != nil { + return nil, err + } + if len > uint64(r.Len()) { + return nil, io.EOF + } + length = len + } else { + length = uint64(r.Len()) + } + f.Data = make([]byte, length) + if _, err := io.ReadFull(r, f.Data); err != nil { + return nil, err + } + return f, nil +} + +func (f *DatagramFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { + typeByte := uint8(0x30) + if f.DataLenPresent { + typeByte ^= 0x1 + } + b.WriteByte(typeByte) + if f.DataLenPresent { + utils.WriteVarInt(b, uint64(len(f.Data))) + } + b.Write(f.Data) + return nil +} + +// Length of a written frame +func (f *DatagramFrame) Length(_ protocol.VersionNumber) protocol.ByteCount { + length := 1 + protocol.ByteCount(len(f.Data)) + if f.DataLenPresent { + length += utils.VarIntLen(uint64(len(f.Data))) + } + return length +} diff --git a/internal/wire/datagram_frame_test.go b/internal/wire/datagram_frame_test.go new file mode 100644 index 00000000..52ab1bfc --- /dev/null +++ b/internal/wire/datagram_frame_test.go @@ -0,0 +1,98 @@ +package wire + +import ( + "bytes" + "io" + + "github.com/lucas-clemente/quic-go/internal/protocol" + "github.com/lucas-clemente/quic-go/internal/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("STREAM frame", func() { + Context("when parsing", func() { + It("parses a frame containing a length", func() { + data := []byte{0x30 ^ 0x1} + data = append(data, encodeVarInt(0x6)...) // length + data = append(data, []byte("foobar")...) + r := bytes.NewReader(data) + frame, err := parseDatagramFrame(r, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + Expect(frame.Data).To(Equal([]byte("foobar"))) + Expect(frame.DataLenPresent).To(BeTrue()) + Expect(r.Len()).To(BeZero()) + }) + + It("parses a frame without length", func() { + data := []byte{0x30} + data = append(data, []byte("Lorem ipsum dolor sit amet")...) + r := bytes.NewReader(data) + frame, err := parseDatagramFrame(r, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + Expect(frame.Data).To(Equal([]byte("Lorem ipsum dolor sit amet"))) + Expect(frame.DataLenPresent).To(BeFalse()) + Expect(r.Len()).To(BeZero()) + }) + + It("errors when the length is longer than the rest of the frame", func() { + data := []byte{0x30 ^ 0x1} + data = append(data, encodeVarInt(0x6)...) // length + data = append(data, []byte("fooba")...) + r := bytes.NewReader(data) + _, err := parseDatagramFrame(r, versionIETFFrames) + Expect(err).To(MatchError(io.EOF)) + }) + + It("errors on EOFs", func() { + data := []byte{0x30 ^ 0x1} + data = append(data, encodeVarInt(6)...) // length + data = append(data, []byte("foobar")...) + _, err := parseDatagramFrame(bytes.NewReader(data), versionIETFFrames) + Expect(err).NotTo(HaveOccurred()) + for i := range data { + _, err := parseDatagramFrame(bytes.NewReader(data[0:i]), versionIETFFrames) + Expect(err).To(MatchError(io.EOF)) + } + }) + }) + + Context("when writing", func() { + It("writes a frame with length", func() { + f := &DatagramFrame{ + DataLenPresent: true, + Data: []byte("foobar"), + } + buf := &bytes.Buffer{} + Expect(f.Write(buf, versionIETFFrames)).To(Succeed()) + expected := []byte{0x30 ^ 0x1} + expected = append(expected, encodeVarInt(0x6)...) + expected = append(expected, []byte("foobar")...) + Expect(buf.Bytes()).To(Equal(expected)) + }) + + It("writes a frame without length", func() { + f := &DatagramFrame{Data: []byte("Lorem ipsum")} + buf := &bytes.Buffer{} + Expect(f.Write(buf, versionIETFFrames)).To(Succeed()) + expected := []byte{0x30} + expected = append(expected, []byte("Lorem ipsum")...) + Expect(buf.Bytes()).To(Equal(expected)) + }) + }) + + Context("length", func() { + It("has the right length for a frame with length", func() { + f := &DatagramFrame{ + DataLenPresent: true, + Data: []byte("foobar"), + } + Expect(f.Length(versionIETFFrames)).To(Equal(1 + utils.VarIntLen(6) + 6)) + }) + + It("has the right length for a frame without length", func() { + f := &DatagramFrame{Data: []byte("foobar")} + Expect(f.Length(versionIETFFrames)).To(Equal(protocol.ByteCount(1 + 6))) + }) + }) +}) diff --git a/internal/wire/frame_parser.go b/internal/wire/frame_parser.go index 9659883d..5d2ae038 100644 --- a/internal/wire/frame_parser.go +++ b/internal/wire/frame_parser.go @@ -87,6 +87,8 @@ func (p *frameParser) parseFrame(r *bytes.Reader, typeByte byte, encLevel protoc frame, err = parseConnectionCloseFrame(r, p.version) case 0x1e: frame, err = parseHandshakeDoneFrame(r, p.version) + case 0x30, 0x31: + frame, err = parseDatagramFrame(r, p.version) default: err = errors.New("unknown frame type") } diff --git a/internal/wire/frame_parser_test.go b/internal/wire/frame_parser_test.go index bfbbfd7a..47cacdd4 100644 --- a/internal/wire/frame_parser_test.go +++ b/internal/wire/frame_parser_test.go @@ -318,6 +318,7 @@ var _ = Describe("Frame parsing", func() { &PathResponseFrame{}, &ConnectionCloseFrame{}, &HandshakeDoneFrame{}, + &DatagramFrame{}, } var framesSerialized [][]byte