From 551d5798e448e37b6d914f257ede7dd4d818397e Mon Sep 17 00:00:00 2001 From: Lucas Clemente Date: Sun, 10 Apr 2016 19:42:12 +0200 Subject: [PATCH] implement stream frame writer --- frame.go | 55 ++++++++++++++++++++++++++++--------------- frame_test.go | 65 +++++++++++++++++++++++++++++++-------------------- utils.go | 11 +++++++++ 3 files changed, 87 insertions(+), 44 deletions(-) diff --git a/frame.go b/frame.go index 43dd22b2..eea6d472 100644 --- a/frame.go +++ b/frame.go @@ -8,14 +8,10 @@ import ( // A StreamFrame of QUIC // TODO: Maybe remove unneeded stuff, e.g. lengths? type StreamFrame struct { - FinBit bool - DataLengthPresent bool - OffsetLength uint8 - StreamIDLength uint8 - StreamID uint32 - Offset uint64 - DataLength uint16 - Data []byte + FinBit bool + StreamID uint32 + Offset uint64 + Data []byte } // ParseStreamFrame reads a stream frame. The type byte must not have been read yet. @@ -27,39 +23,40 @@ func ParseStreamFrame(r *bytes.Reader) (*StreamFrame, error) { return nil, err } frame.FinBit = typeByte&0x40 > 0 - frame.DataLengthPresent = typeByte&0x20 > 0 - frame.OffsetLength = typeByte & 0x1C >> 2 - if frame.OffsetLength != 0 { - frame.OffsetLength++ + dataLenPresent := typeByte&0x20 > 0 + offsetLen := typeByte & 0x1C >> 2 + if offsetLen != 0 { + offsetLen++ } - frame.StreamIDLength = typeByte&0x03 + 1 + streamIDLen := typeByte&0x03 + 1 - sid, err := readUintN(r, frame.StreamIDLength) + sid, err := readUintN(r, streamIDLen) if err != nil { return nil, err } frame.StreamID = uint32(sid) - frame.Offset, err = readUintN(r, frame.OffsetLength) + frame.Offset, err = readUintN(r, offsetLen) if err != nil { return nil, err } - if frame.DataLengthPresent { - frame.DataLength, err = readUint16(r) + var dataLen uint16 + if dataLenPresent { + dataLen, err = readUint16(r) if err != nil { return nil, err } } - if frame.DataLength == 0 { + if dataLen == 0 { // The rest of the packet is data frame.Data, err = ioutil.ReadAll(r) if err != nil { return nil, err } } else { - frame.Data = make([]byte, frame.DataLength) + frame.Data = make([]byte, dataLen) if _, err := r.Read(frame.Data); err != nil { return nil, err } @@ -67,3 +64,23 @@ func ParseStreamFrame(r *bytes.Reader) (*StreamFrame, error) { return frame, nil } + +// WriteStreamFrame writes a stream frame. +func WriteStreamFrame(b *bytes.Buffer, f *StreamFrame) { + typeByte := uint8(0x80) + if f.FinBit { + typeByte ^= 0x40 + } + typeByte ^= 0x20 + if f.Offset != 0 { + typeByte ^= 0x1c // TODO: Send shorter offset if possible + } + typeByte ^= 0x03 // TODO: Send shorter stream ID if possible + b.WriteByte(typeByte) + writeUint32(b, f.StreamID) + if f.Offset != 0 { + writeUint64(b, f.Offset) + } + writeUint16(b, uint16(len(f.Data))) + b.Write(f.Data) +} diff --git a/frame_test.go b/frame_test.go index cc3ab8ad..74675e29 100644 --- a/frame_test.go +++ b/frame_test.go @@ -8,33 +8,48 @@ import ( ) var _ = Describe("Frame", func() { - Context("when parsing", func() { - It("accepts sample frame", func() { - b := bytes.NewReader([]byte{0xa0, 0x1, 0x06, 0x00, 'f', 'o', 'o', 'b', 'a', 'r'}) - frame, err := ParseStreamFrame(b) - Expect(err).ToNot(HaveOccurred()) - Expect(frame.FinBit).To(BeFalse()) - Expect(frame.DataLengthPresent).To(BeTrue()) - Expect(frame.OffsetLength).To(BeZero()) - Expect(frame.StreamIDLength).To(Equal(uint8(1))) - Expect(frame.StreamID).To(Equal(uint32(1))) - Expect(frame.Offset).To(BeZero()) - Expect(frame.DataLength).To(Equal(uint16(6))) - Expect(frame.Data).To(Equal([]byte("foobar"))) + Context("stream frames", func() { + Context("when parsing", func() { + It("accepts sample frame", func() { + b := bytes.NewReader([]byte{0xa0, 0x1, 0x06, 0x00, 'f', 'o', 'o', 'b', 'a', 'r'}) + frame, err := ParseStreamFrame(b) + Expect(err).ToNot(HaveOccurred()) + Expect(frame.FinBit).To(BeFalse()) + Expect(frame.StreamID).To(Equal(uint32(1))) + Expect(frame.Offset).To(BeZero()) + Expect(frame.Data).To(Equal([]byte("foobar"))) + }) + + It("accepts frame without datalength", func() { + b := bytes.NewReader([]byte{0x80, 0x1, 'f', 'o', 'o', 'b', 'a', 'r'}) + frame, err := ParseStreamFrame(b) + Expect(err).ToNot(HaveOccurred()) + Expect(frame.FinBit).To(BeFalse()) + Expect(frame.StreamID).To(Equal(uint32(1))) + Expect(frame.Offset).To(BeZero()) + Expect(frame.Data).To(Equal([]byte("foobar"))) + }) }) - It("accepts frame without datalength", func() { - b := bytes.NewReader([]byte{0x80, 0x1, 'f', 'o', 'o', 'b', 'a', 'r'}) - frame, err := ParseStreamFrame(b) - Expect(err).ToNot(HaveOccurred()) - Expect(frame.FinBit).To(BeFalse()) - Expect(frame.DataLengthPresent).To(BeFalse()) - Expect(frame.OffsetLength).To(BeZero()) - Expect(frame.StreamIDLength).To(Equal(uint8(1))) - Expect(frame.StreamID).To(Equal(uint32(1))) - Expect(frame.Offset).To(BeZero()) - Expect(frame.DataLength).To(Equal(uint16(0))) - Expect(frame.Data).To(Equal([]byte("foobar"))) + Context("when writing", func() { + It("writes sample frame", func() { + b := &bytes.Buffer{} + WriteStreamFrame(b, &StreamFrame{ + StreamID: 1, + Data: []byte("foobar"), + }) + Expect(b.Bytes()).To(Equal([]byte{0xa3, 0x1, 0, 0, 0, 0x06, 0x00, 'f', 'o', 'o', 'b', 'a', 'r'})) + }) + + It("writes offsets", func() { + b := &bytes.Buffer{} + WriteStreamFrame(b, &StreamFrame{ + StreamID: 1, + Offset: 16, + Data: []byte("foobar"), + }) + Expect(b.Bytes()).To(Equal([]byte{0xbf, 0x1, 0, 0, 0, 0x10, 0, 0, 0, 0, 0, 0, 0, 0x06, 0x00, 'f', 'o', 'o', 'b', 'a', 'r'})) + }) }) }) }) diff --git a/utils.go b/utils.go index 4d2ceca8..e799b1b9 100644 --- a/utils.go +++ b/utils.go @@ -47,6 +47,17 @@ func readUint16(b io.ByteReader) (uint16, error) { return uint16(b1) + uint16(b2)<<8, nil } +func writeUint64(b *bytes.Buffer, i uint64) { + b.WriteByte(uint8(i & 0xff)) + b.WriteByte(uint8((i >> 8) & 0xff)) + b.WriteByte(uint8((i >> 16) & 0xff)) + b.WriteByte(uint8((i >> 24) & 0xff)) + b.WriteByte(uint8((i >> 32) & 0xff)) + b.WriteByte(uint8((i >> 40) & 0xff)) + b.WriteByte(uint8((i >> 48) & 0xff)) + b.WriteByte(uint8((i >> 56) & 0xff)) +} + func writeUint32(b *bytes.Buffer, i uint32) { b.WriteByte(uint8(i & 0xff)) b.WriteByte(uint8((i >> 8) & 0xff))