From a6e44f3bfc0ca536bcd4af833cd27ccaa5b758f9 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Fri, 8 Dec 2017 09:52:49 +0700 Subject: [PATCH] implement parsing and writing of the STOP_SENDING frame --- internal/wire/stop_sending_frame.go | 48 ++++++++++++++++++ internal/wire/stop_sending_frame_test.go | 64 ++++++++++++++++++++++++ packet_unpacker.go | 5 ++ packet_unpacker_test.go | 14 +++++- 4 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 internal/wire/stop_sending_frame.go create mode 100644 internal/wire/stop_sending_frame_test.go diff --git a/internal/wire/stop_sending_frame.go b/internal/wire/stop_sending_frame.go new file mode 100644 index 00000000..14156494 --- /dev/null +++ b/internal/wire/stop_sending_frame.go @@ -0,0 +1,48 @@ +package wire + +import ( + "bytes" + + "github.com/lucas-clemente/quic-go/internal/protocol" + "github.com/lucas-clemente/quic-go/internal/utils" + "github.com/lucas-clemente/quic-go/qerr" +) + +// A StopSendingFrame is a STOP_SENDING frame +type StopSendingFrame struct { + StreamID protocol.StreamID + ErrorCode qerr.ErrorCode +} + +// ParseStopSendingFrame parses a STOP_SENDING frame +func ParseStopSendingFrame(r *bytes.Reader, _ protocol.VersionNumber) (*StopSendingFrame, error) { + if _, err := r.ReadByte(); err != nil { // read the TypeByte + return nil, err + } + + streamID, err := utils.ReadVarInt(r) + if err != nil { + return nil, err + } + errorCode, err := utils.BigEndian.ReadUint16(r) + if err != nil { + return nil, err + } + + return &StopSendingFrame{ + StreamID: protocol.StreamID(streamID), + ErrorCode: qerr.ErrorCode(errorCode), + }, nil +} + +// MinLength of a written frame +func (f *StopSendingFrame) MinLength(_ protocol.VersionNumber) protocol.ByteCount { + return 1 + utils.VarIntLen(uint64(f.StreamID)) + 2 +} + +func (f *StopSendingFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { + b.WriteByte(0x0c) + utils.WriteVarInt(b, uint64(f.StreamID)) + utils.BigEndian.WriteUint16(b, uint16(f.ErrorCode)) + return nil +} diff --git a/internal/wire/stop_sending_frame_test.go b/internal/wire/stop_sending_frame_test.go new file mode 100644 index 00000000..76b9d0f4 --- /dev/null +++ b/internal/wire/stop_sending_frame_test.go @@ -0,0 +1,64 @@ +package wire + +import ( + "bytes" + + "github.com/lucas-clemente/quic-go/internal/protocol" + "github.com/lucas-clemente/quic-go/internal/utils" + "github.com/lucas-clemente/quic-go/qerr" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("STOP_SENDING frame", func() { + Context("when parsing", func() { + It("parses a sample frame", func() { + data := []byte{0x0c} + data = append(data, encodeVarInt(0xdecafbad)...) // stream ID + data = append(data, []byte{0x13, 0x37}...) // error code + b := bytes.NewReader(data) + frame, err := ParseStopSendingFrame(b, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + Expect(frame.StreamID).To(Equal(protocol.StreamID(0xdecafbad))) + Expect(frame.ErrorCode).To(Equal(qerr.ErrorCode(0x1337))) + Expect(b.Len()).To(BeZero()) + }) + + It("errors on EOFs", func() { + data := []byte{0x0c} + data = append(data, encodeVarInt(0xdecafbad)...) // stream ID + data = append(data, []byte{0x13, 0x37}...) // error code + _, err := ParseStopSendingFrame(bytes.NewReader(data), versionIETFFrames) + Expect(err).NotTo(HaveOccurred()) + for i := range data { + _, err := ParseStopSendingFrame(bytes.NewReader(data[:i]), versionIETFFrames) + Expect(err).To(HaveOccurred()) + } + }) + }) + + Context("when writing", func() { + It("writes", func() { + frame := &StopSendingFrame{ + StreamID: 0xdeadbeefcafe, + ErrorCode: 0x10, + } + buf := &bytes.Buffer{} + err := frame.Write(buf, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + expected := []byte{0x0c} + expected = append(expected, encodeVarInt(0xdeadbeefcafe)...) + expected = append(expected, []byte{0x0, 0x10}...) + Expect(buf.Bytes()).To(Equal(expected)) + }) + + It("has the correct min length", func() { + frame := &StopSendingFrame{ + StreamID: 0xdeadbeef, + ErrorCode: 0x10, + } + Expect(frame.MinLength(versionIETFFrames)).To(Equal(1 + 2 + utils.VarIntLen(0xdeadbeef))) + }) + }) +}) diff --git a/packet_unpacker.go b/packet_unpacker.go index 7291dc23..9b11085f 100644 --- a/packet_unpacker.go +++ b/packet_unpacker.go @@ -122,6 +122,11 @@ func (u *packetUnpacker) parseIETFFrame(r *bytes.Reader, typeByte byte, hdr *wir if err != nil { err = qerr.Error(qerr.InvalidBlockedData, err.Error()) } + case 0xc: + frame, err = wire.ParseStopSendingFrame(r, u.version) + if err != nil { + err = qerr.Error(qerr.InvalidFrameData, err.Error()) + } case 0xe: frame, err = wire.ParseAckFrame(r, u.version) if err != nil { diff --git a/packet_unpacker_test.go b/packet_unpacker_test.go index 91e0656a..22816948 100644 --- a/packet_unpacker_test.go +++ b/packet_unpacker_test.go @@ -364,6 +364,17 @@ var _ = Describe("Packet unpacker", func() { Expect(packet.frames).To(Equal([]wire.Frame{f})) }) + It("unpacks STOP_SENDING frames", func() { + f := &wire.StopSendingFrame{StreamID: 0x42} + buf := &bytes.Buffer{} + err := f.Write(buf, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + setData(buf.Bytes()) + packet, err := unpacker.Unpack(hdrBin, hdr, data) + Expect(err).ToNot(HaveOccurred()) + Expect(packet.frames).To(Equal([]wire.Frame{f})) + }) + It("unpacks ACK frames", func() { f := &wire.AckFrame{ LargestAcked: 0x13, @@ -393,8 +404,9 @@ var _ = Describe("Packet unpacker", func() { 0x04: qerr.InvalidWindowUpdateData, 0x05: qerr.InvalidWindowUpdateData, 0x09: qerr.InvalidBlockedData, + 0x0c: qerr.InvalidFrameData, + 0x0e: qerr.InvalidAckData, 0x10: qerr.InvalidStreamData, - 0xe: qerr.InvalidAckData, } { setData([]byte{b}) _, err := unpacker.Unpack(hdrBin, hdr, data)