diff --git a/internal/protocol/version.go b/internal/protocol/version.go index 600b508b..3135ca85 100644 --- a/internal/protocol/version.go +++ b/internal/protocol/version.go @@ -66,9 +66,9 @@ func (vn VersionNumber) CryptoStreamID() StreamID { return 0 } -// UsesMaxDataFrame tells if this version uses MAX_DATA, MAX_STREAM_DATA, BLOCKED and STREAM_BLOCKED instead of WINDOW_UDPATE and BLOCKED frames -func (vn VersionNumber) UsesMaxDataFrame() bool { - return vn.CryptoStreamID() == 0 +// UsesIETFFrameFormat tells if this version uses the IETF frame format +func (vn VersionNumber) UsesIETFFrameFormat() bool { + return vn != Version39 } // StreamContributesToConnectionFlowControl says if a stream contributes to connection-level flow control diff --git a/internal/protocol/version_test.go b/internal/protocol/version_test.go index 67d600ab..9ca26550 100644 --- a/internal/protocol/version_test.go +++ b/internal/protocol/version_test.go @@ -51,9 +51,9 @@ var _ = Describe("Version", func() { Expect(VersionTLS.CryptoStreamID()).To(Equal(StreamID(0))) }) - It("tells if a version uses the MAX_DATA, MAX_STREAM_DATA, BLOCKED and STREAM_BLOCKED frames", func() { - Expect(Version39.UsesMaxDataFrame()).To(BeFalse()) - Expect(VersionTLS.UsesMaxDataFrame()).To(BeTrue()) + It("tells if a version uses the IETF frame types", func() { + Expect(Version39.UsesIETFFrameFormat()).To(BeFalse()) + Expect(VersionTLS.UsesIETFFrameFormat()).To(BeTrue()) }) It("says if a stream contributes to connection-level flowcontrol, for gQUIC", func() { diff --git a/internal/wire/blocked_frame.go b/internal/wire/blocked_frame.go index 08dc051e..cc6a0165 100644 --- a/internal/wire/blocked_frame.go +++ b/internal/wire/blocked_frame.go @@ -18,7 +18,7 @@ func ParseBlockedFrame(r *bytes.Reader, version protocol.VersionNumber) (*Blocke } func (f *BlockedFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { - if !version.UsesMaxDataFrame() { + if !version.UsesIETFFrameFormat() { return (&blockedFrameLegacy{}).Write(b, version) } typeByte := uint8(0x08) @@ -28,7 +28,7 @@ func (f *BlockedFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) er // MinLength of a written frame func (f *BlockedFrame) MinLength(version protocol.VersionNumber) (protocol.ByteCount, error) { - if !version.UsesMaxDataFrame() { // writing this frame would result in a legacy BLOCKED being written, which is longer + if !version.UsesIETFFrameFormat() { // writing this frame would result in a legacy BLOCKED being written, which is longer return 1 + 4, nil } return 1, nil diff --git a/internal/wire/blocked_frame_test.go b/internal/wire/blocked_frame_test.go index b48065b6..9a3e2dde 100644 --- a/internal/wire/blocked_frame_test.go +++ b/internal/wire/blocked_frame_test.go @@ -35,7 +35,7 @@ var _ = Describe("BLOCKED frame", func() { It("has the correct min length", func() { frame := BlockedFrame{} - Expect(frame.MinLength(versionMaxDataFrame)).To(Equal(protocol.ByteCount(1))) + Expect(frame.MinLength(versionIETFFrames)).To(Equal(protocol.ByteCount(1))) }) }) }) diff --git a/internal/wire/max_data_frame.go b/internal/wire/max_data_frame.go index cd3ff655..45f82b20 100644 --- a/internal/wire/max_data_frame.go +++ b/internal/wire/max_data_frame.go @@ -30,7 +30,7 @@ func ParseMaxDataFrame(r *bytes.Reader, version protocol.VersionNumber) (*MaxDat //Write writes a MAX_STREAM_DATA frame func (f *MaxDataFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { - if !version.UsesMaxDataFrame() { + if !version.UsesIETFFrameFormat() { // write a gQUIC WINDOW_UPDATE frame (with stream ID 0, which means connection-level there) return (&windowUpdateFrame{ StreamID: 0, @@ -44,7 +44,7 @@ func (f *MaxDataFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) er // MinLength of a written frame func (f *MaxDataFrame) MinLength(version protocol.VersionNumber) (protocol.ByteCount, error) { - if !version.UsesMaxDataFrame() { // writing this frame would result in a gQUIC WINDOW_UPDATE being written, which is longer + if !version.UsesIETFFrameFormat() { // writing this frame would result in a gQUIC WINDOW_UPDATE being written, which is longer return 1 + 4 + 8, nil } return 1 + 8, nil diff --git a/internal/wire/max_data_frame_test.go b/internal/wire/max_data_frame_test.go index e4cd75c1..c4507660 100644 --- a/internal/wire/max_data_frame_test.go +++ b/internal/wire/max_data_frame_test.go @@ -24,10 +24,10 @@ var _ = Describe("MAX_DATA frame", func() { data := []byte{0x4, 0x44, 0x33, 0x22, 0x11, 0xad, 0xfb, 0xca, 0xde, // byte offset } - _, err := ParseMaxDataFrame(bytes.NewReader(data), versionMaxDataFrame) + _, err := ParseMaxDataFrame(bytes.NewReader(data), versionIETFFrames) Expect(err).NotTo(HaveOccurred()) for i := range data { - _, err := ParseMaxDataFrame(bytes.NewReader(data[0:i]), versionMaxDataFrame) + _, err := ParseMaxDataFrame(bytes.NewReader(data[0:i]), versionIETFFrames) Expect(err).To(HaveOccurred()) } }) @@ -38,7 +38,7 @@ var _ = Describe("MAX_DATA frame", func() { f := &MaxDataFrame{ ByteOffset: 0xdeadbeef, } - Expect(f.MinLength(versionMaxDataFrame)).To(Equal(protocol.ByteCount(1 + 8))) + Expect(f.MinLength(versionIETFFrames)).To(Equal(protocol.ByteCount(1 + 8))) }) It("writes a MAX_DATA frame", func() { @@ -46,7 +46,7 @@ var _ = Describe("MAX_DATA frame", func() { f := &MaxDataFrame{ ByteOffset: 0xdeadbeefcafe1337, } - err := f.Write(b, versionMaxDataFrame) + err := f.Write(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()).To(Equal([]byte{0x4, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37, // byte offset diff --git a/internal/wire/max_stream_data_frame.go b/internal/wire/max_stream_data_frame.go index 56c44c9f..2f959565 100644 --- a/internal/wire/max_stream_data_frame.go +++ b/internal/wire/max_stream_data_frame.go @@ -38,7 +38,7 @@ func ParseMaxStreamDataFrame(r *bytes.Reader, version protocol.VersionNumber) (* // Write writes a MAX_STREAM_DATA frame func (f *MaxStreamDataFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { - if !version.UsesMaxDataFrame() { + if !version.UsesIETFFrameFormat() { return (&windowUpdateFrame{ StreamID: f.StreamID, ByteOffset: f.ByteOffset, diff --git a/internal/wire/max_stream_data_frame_test.go b/internal/wire/max_stream_data_frame_test.go index 6cad6ac1..7940ab30 100644 --- a/internal/wire/max_stream_data_frame_test.go +++ b/internal/wire/max_stream_data_frame_test.go @@ -15,7 +15,7 @@ var _ = Describe("MAX_STREAM_DATA frame", func() { 0xde, 0xad, 0xbe, 0xef, // stream id 0xde, 0xca, 0xfb, 0xad, 0x11, 0x22, 0x33, 0x44, // byte offset }) - frame, err := ParseMaxStreamDataFrame(b, versionMaxDataFrame) + frame, err := ParseMaxStreamDataFrame(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.StreamID).To(Equal(protocol.StreamID(0xdeadbeef))) Expect(frame.ByteOffset).To(Equal(protocol.ByteCount(0xdecafbad11223344))) @@ -27,10 +27,10 @@ var _ = Describe("MAX_STREAM_DATA frame", func() { 0xef, 0xbe, 0xad, 0xde, // stream id 0x44, 0x33, 0x22, 0x11, 0xad, 0xfb, 0xca, 0xde, // byte offset } - _, err := ParseMaxStreamDataFrame(bytes.NewReader(data), versionMaxDataFrame) + _, err := ParseMaxStreamDataFrame(bytes.NewReader(data), versionIETFFrames) Expect(err).NotTo(HaveOccurred()) for i := range data { - _, err := ParseMaxStreamDataFrame(bytes.NewReader(data[0:i]), versionMaxDataFrame) + _, err := ParseMaxStreamDataFrame(bytes.NewReader(data[0:i]), versionIETFFrames) Expect(err).To(HaveOccurred()) } }) @@ -51,7 +51,7 @@ var _ = Describe("MAX_STREAM_DATA frame", func() { StreamID: 0xdecafbad, ByteOffset: 0xdeadbeefcafe1337, } - err := f.Write(b, versionMaxDataFrame) + err := f.Write(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()).To(Equal([]byte{0x5, 0xde, 0xca, 0xfb, 0xad, // stream id diff --git a/internal/wire/stream_blocked_frame.go b/internal/wire/stream_blocked_frame.go index 981c0ecc..b6415190 100644 --- a/internal/wire/stream_blocked_frame.go +++ b/internal/wire/stream_blocked_frame.go @@ -30,7 +30,7 @@ func ParseStreamBlockedFrame(r *bytes.Reader, version protocol.VersionNumber) (* // Write writes a STREAM_BLOCKED frame func (f *StreamBlockedFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { - if !version.UsesMaxDataFrame() { + if !version.UsesIETFFrameFormat() { return (&blockedFrameLegacy{StreamID: f.StreamID}).Write(b, version) } b.WriteByte(0x09) diff --git a/internal/wire/stream_blocked_frame_test.go b/internal/wire/stream_blocked_frame_test.go index 002e8889..cc7d9b88 100644 --- a/internal/wire/stream_blocked_frame_test.go +++ b/internal/wire/stream_blocked_frame_test.go @@ -15,7 +15,7 @@ var _ = Describe("STREAM_BLOCKED frame", func() { b := bytes.NewReader([]byte{0x9, 0xde, 0xad, 0xbe, 0xef, // stream id }) - frame, err := ParseStreamBlockedFrame(b, versionMaxDataFrame) + frame, err := ParseStreamBlockedFrame(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.StreamID).To(Equal(protocol.StreamID(0xdeadbeef))) Expect(b.Len()).To(BeZero()) @@ -25,10 +25,10 @@ var _ = Describe("STREAM_BLOCKED frame", func() { data := []byte{0x9, 0xef, 0xbe, 0xad, 0xde, // stream id } - _, err := ParseStreamBlockedFrame(bytes.NewReader(data), versionMaxDataFrame) + _, err := ParseStreamBlockedFrame(bytes.NewReader(data), versionIETFFrames) Expect(err).NotTo(HaveOccurred()) for i := range data { - _, err := ParseStreamBlockedFrame(bytes.NewReader(data[0:i]), versionMaxDataFrame) + _, err := ParseStreamBlockedFrame(bytes.NewReader(data[0:i]), versionIETFFrames) Expect(err).To(HaveOccurred()) } }) @@ -47,7 +47,7 @@ var _ = Describe("STREAM_BLOCKED frame", func() { f := &StreamBlockedFrame{ StreamID: 0xdecafbad, } - err := f.Write(b, versionMaxDataFrame) + err := f.Write(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()).To(Equal([]byte{0x9, 0xde, 0xca, 0xfb, 0xad, // stream id diff --git a/internal/wire/wire_suite_test.go b/internal/wire/wire_suite_test.go index df492f73..b0145420 100644 --- a/internal/wire/wire_suite_test.go +++ b/internal/wire/wire_suite_test.go @@ -17,13 +17,13 @@ func TestCrypto(t *testing.T) { const ( // a QUIC version that uses big endian encoding versionBigEndian = protocol.Version39 - // a QUIC version that uses the MAX_DATA / MAX_STREAM_DATA and BLOCKED / STREAM_BLOCKED frames - versionMaxDataFrame = protocol.VersionTLS + // a QUIC version that uses the IETF frame types + versionIETFFrames = protocol.VersionTLS ) var _ = BeforeSuite(func() { Expect(utils.GetByteOrder(versionBigEndian)).To(Equal(utils.BigEndian)) - Expect(utils.GetByteOrder(versionMaxDataFrame)).To(Equal(utils.BigEndian)) - Expect(versionBigEndian.UsesMaxDataFrame()).To(BeFalse()) - Expect(versionMaxDataFrame.UsesMaxDataFrame()).To(BeTrue()) + Expect(utils.GetByteOrder(versionIETFFrames)).To(Equal(utils.BigEndian)) + Expect(versionBigEndian.UsesIETFFrameFormat()).To(BeFalse()) + Expect(versionIETFFrames.UsesIETFFrameFormat()).To(BeTrue()) }) diff --git a/packet_unpacker.go b/packet_unpacker.go index f891e37f..347743cb 100644 --- a/packet_unpacker.go +++ b/packet_unpacker.go @@ -47,77 +47,15 @@ func (u *packetUnpacker) Unpack(headerBinary []byte, hdr *wire.Header, data []by } r.UnreadByte() - var frame wire.Frame - if typeByte&0x80 == 0x80 { - frame, err = wire.ParseStreamFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidStreamData, err.Error()) - } else { - streamID := frame.(*wire.StreamFrame).StreamID - if streamID != u.version.CryptoStreamID() && encryptionLevel <= protocol.EncryptionUnencrypted { - err = qerr.Error(qerr.UnencryptedStreamData, fmt.Sprintf("received unencrypted stream data on stream %d", streamID)) - } - } - } else if typeByte&0xc0 == 0x40 { - frame, err = wire.ParseAckFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidAckData, err.Error()) - } - } else if typeByte == 0x01 { - frame, err = wire.ParseRstStreamFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidRstStreamData, err.Error()) - } - } else if typeByte == 0x02 { - frame, err = wire.ParseConnectionCloseFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidConnectionCloseData, err.Error()) - } - } else if typeByte == 0x3 { - frame, err = wire.ParseGoawayFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidGoawayData, err.Error()) - } - } else if u.version.UsesMaxDataFrame() && typeByte == 0x4 { // in IETF QUIC, 0x4 is a MAX_DATA frame - frame, err = wire.ParseMaxDataFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidWindowUpdateData, err.Error()) - } - } else if typeByte == 0x4 { // in gQUIC, 0x4 is a WINDOW_UPDATE frame - frame, err = wire.ParseWindowUpdateFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidWindowUpdateData, err.Error()) - } - } else if u.version.UsesMaxDataFrame() && typeByte == 0x5 { // in IETF QUIC, 0x5 is a MAX_STREAM_DATA frame - frame, err = wire.ParseMaxStreamDataFrame(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidWindowUpdateData, err.Error()) - } - } else if typeByte == 0x5 { // in gQUIC, 0x5 is a BLOCKED frame - frame, err = wire.ParseBlockedFrameLegacy(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidBlockedData, err.Error()) - } - } else if typeByte == 0x6 { - frame, err = wire.ParseStopWaitingFrame(r, hdr.PacketNumber, hdr.PacketNumberLen, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidStopWaitingData, err.Error()) - } - } else if typeByte == 0x7 { - frame, err = wire.ParsePingFrame(r, u.version) - } else if u.version.UsesMaxDataFrame() && typeByte == 0x8 { // in IETF QUIC, 0x4 is a BLOCKED frame - frame, err = wire.ParseBlockedFrame(r, u.version) - } else if u.version.UsesMaxDataFrame() && typeByte == 0x9 { // in IETF QUIC, 0x4 is a STREAM_BLOCKED frame - frame, err = wire.ParseBlockedFrameLegacy(r, u.version) - if err != nil { - err = qerr.Error(qerr.InvalidBlockedData, err.Error()) - } - } else { - err = qerr.Error(qerr.InvalidFrameData, fmt.Sprintf("unknown type byte 0x%x", typeByte)) - } + frame, err := u.parseFrame(r, typeByte, hdr) if err != nil { return nil, err } + if sf, ok := frame.(*wire.StreamFrame); ok { + if sf.StreamID != u.version.CryptoStreamID() && encryptionLevel <= protocol.EncryptionUnencrypted { + return nil, qerr.Error(qerr.UnencryptedStreamData, fmt.Sprintf("received unencrypted stream data on stream %d", sf.StreamID)) + } + } if frame != nil { fs = append(fs, frame) } @@ -128,3 +66,126 @@ func (u *packetUnpacker) Unpack(headerBinary []byte, hdr *wire.Header, data []by frames: fs, }, nil } + +func (u *packetUnpacker) parseFrame(r *bytes.Reader, typeByte byte, hdr *wire.Header) (wire.Frame, error) { + if u.version.UsesIETFFrameFormat() { + return u.parseIETFFrame(r, typeByte, hdr) + } + return u.parseGQUICFrame(r, typeByte, hdr) +} + +func (u *packetUnpacker) parseIETFFrame(r *bytes.Reader, typeByte byte, hdr *wire.Header) (wire.Frame, error) { + var frame wire.Frame + var err error + // TODO: implement the IETF STREAM frame + if typeByte&0x80 == 0x80 { + frame, err = wire.ParseStreamFrame(r, u.version) + if err != nil { + err = qerr.Error(qerr.InvalidStreamData, err.Error()) + } + return frame, err + } else if typeByte&0xc0 == 0x40 { // TODO: implement the IETF ACK frame + frame, err = wire.ParseAckFrame(r, u.version) + if err != nil { + err = qerr.Error(qerr.InvalidAckData, err.Error()) + } + return frame, err + } + // TODO: implement all IETF QUIC frame types + switch typeByte { + case 0x1: + frame, err = wire.ParseRstStreamFrame(r, u.version) + if err != nil { + err = qerr.Error(qerr.InvalidRstStreamData, err.Error()) + } + case 0x2: + frame, err = wire.ParseConnectionCloseFrame(r, u.version) + if err != nil { + err = qerr.Error(qerr.InvalidConnectionCloseData, err.Error()) + } + case 0x4: + frame, err = wire.ParseMaxDataFrame(r, u.version) + if err != nil { + err = qerr.Error(qerr.InvalidWindowUpdateData, err.Error()) + } + case 0x5: + frame, err = wire.ParseMaxStreamDataFrame(r, u.version) + if err != nil { + err = qerr.Error(qerr.InvalidWindowUpdateData, err.Error()) + } + case 0x6: + // TODO(#964): remove STOP_WAITING frames + // TODO(#878): implement the MAX_STREAM_ID frame + frame, err = wire.ParseStopWaitingFrame(r, hdr.PacketNumber, hdr.PacketNumberLen, u.version) + if err != nil { + err = qerr.Error(qerr.InvalidStopWaitingData, err.Error()) + } + case 0x7: + frame, err = wire.ParsePingFrame(r, u.version) + case 0x8: + frame, err = wire.ParseBlockedFrame(r, u.version) + case 0x9: + frame, err = wire.ParseBlockedFrameLegacy(r, u.version) + if err != nil { + err = qerr.Error(qerr.InvalidBlockedData, err.Error()) + } + default: + err = qerr.Error(qerr.InvalidFrameData, fmt.Sprintf("unknown type byte 0x%x", typeByte)) + } + return frame, err +} + +func (u *packetUnpacker) parseGQUICFrame(r *bytes.Reader, typeByte byte, hdr *wire.Header) (wire.Frame, error) { + var frame wire.Frame + var err error + if typeByte&0x80 == 0x80 { + frame, err = wire.ParseStreamFrame(r, u.version) + if err != nil { + err = qerr.Error(qerr.InvalidStreamData, err.Error()) + } + return frame, err + } else if typeByte&0xc0 == 0x40 { + frame, err = wire.ParseAckFrame(r, u.version) + if err != nil { + err = qerr.Error(qerr.InvalidAckData, err.Error()) + } + return frame, err + } + switch typeByte { + case 0x1: + frame, err = wire.ParseRstStreamFrame(r, u.version) + if err != nil { + err = qerr.Error(qerr.InvalidRstStreamData, err.Error()) + } + case 0x2: + frame, err = wire.ParseConnectionCloseFrame(r, u.version) + if err != nil { + err = qerr.Error(qerr.InvalidConnectionCloseData, err.Error()) + } + case 0x3: + frame, err = wire.ParseGoawayFrame(r, u.version) + if err != nil { + err = qerr.Error(qerr.InvalidGoawayData, err.Error()) + } + case 0x4: + frame, err = wire.ParseWindowUpdateFrame(r, u.version) + if err != nil { + err = qerr.Error(qerr.InvalidWindowUpdateData, err.Error()) + } + case 0x5: + frame, err = wire.ParseBlockedFrameLegacy(r, u.version) + if err != nil { + err = qerr.Error(qerr.InvalidBlockedData, err.Error()) + } + case 0x6: + frame, err = wire.ParseStopWaitingFrame(r, hdr.PacketNumber, hdr.PacketNumberLen, u.version) + if err != nil { + err = qerr.Error(qerr.InvalidStopWaitingData, err.Error()) + } + case 0x7: + frame, err = wire.ParsePingFrame(r, u.version) + default: + err = qerr.Error(qerr.InvalidFrameData, fmt.Sprintf("unknown type byte 0x%x", typeByte)) + } + return frame, err +} diff --git a/packet_unpacker_test.go b/packet_unpacker_test.go index d2a76393..bc24f88a 100644 --- a/packet_unpacker_test.go +++ b/packet_unpacker_test.go @@ -54,19 +54,16 @@ var _ = Describe("Packet unpacker", func() { data, _ = unpacker.aead.(*mockAEAD).Seal(nil, p, 0, hdrBin) } - It("does not read read a private flag for QUIC Version >= 34", func() { - f := &wire.ConnectionCloseFrame{ReasonPhrase: "foo"} - err := f.Write(buf, 0) - 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("errors if the packet doesn't contain any payload", func() { + setData(nil) + _, err := unpacker.Unpack(hdrBin, hdr, data) + Expect(err).To(MatchError(qerr.MissingPayload)) }) It("saves the encryption level", func() { + unpacker.version = versionGQUICFrames f := &wire.ConnectionCloseFrame{ReasonPhrase: "foo"} - err := f.Write(buf, 0) + err := f.Write(buf, versionGQUICFrames) Expect(err).ToNot(HaveOccurred()) setData(buf.Bytes()) unpacker.aead.(*mockAEAD).encLevelOpen = protocol.EncryptionSecure @@ -75,85 +72,247 @@ var _ = Describe("Packet unpacker", func() { Expect(packet.encryptionLevel).To(Equal(protocol.EncryptionSecure)) }) - It("unpacks ACK frames", func() { - unpacker.version = protocol.VersionWhatever - f := &wire.AckFrame{ - LargestAcked: 0x13, - LowestAcked: 1, - } - err := f.Write(buf, protocol.VersionWhatever) - Expect(err).ToNot(HaveOccurred()) - setData(buf.Bytes()) - packet, err := unpacker.Unpack(hdrBin, hdr, data) - Expect(err).ToNot(HaveOccurred()) - Expect(packet.frames).To(HaveLen(1)) - readFrame := packet.frames[0].(*wire.AckFrame) - Expect(readFrame).ToNot(BeNil()) - Expect(readFrame.LargestAcked).To(Equal(protocol.PacketNumber(0x13))) - }) - - It("handles PADDING frames", func() { - setData([]byte{0, 0, 0}) // 3 bytes PADDING - packet, err := unpacker.Unpack(hdrBin, hdr, data) - Expect(err).ToNot(HaveOccurred()) - Expect(packet.frames).To(BeEmpty()) - }) - - It("handles PADDING between two other frames", func() { - f := &wire.PingFrame{} - err := f.Write(buf, protocol.VersionWhatever) - Expect(err).ToNot(HaveOccurred()) - _, err = buf.Write(bytes.Repeat([]byte{0}, 10)) // 10 bytes PADDING - Expect(err).ToNot(HaveOccurred()) - err = f.Write(buf, protocol.VersionWhatever) - Expect(err).ToNot(HaveOccurred()) - setData(buf.Bytes()) - packet, err := unpacker.Unpack(hdrBin, hdr, data) - Expect(err).ToNot(HaveOccurred()) - Expect(packet.frames).To(HaveLen(2)) - }) - - It("unpacks RST_STREAM frames", func() { - f := &wire.RstStreamFrame{ - StreamID: 0xdeadbeef, - ByteOffset: 0xdecafbad11223344, - ErrorCode: 0x13371234, - } - err := f.Write(buf, protocol.VersionWhatever) - 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 CONNECTION_CLOSE frames", func() { - f := &wire.ConnectionCloseFrame{ReasonPhrase: "foo"} - err := f.Write(buf, protocol.VersionWhatever) - 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 GOAWAY frames", func() { - f := &wire.GoawayFrame{ - ErrorCode: 1, - LastGoodStream: 2, - ReasonPhrase: "foo", - } - err := f.Write(buf, 0) - 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})) - }) - - Context("flow control frames, when the crypto stream is stream 0", func() { + Context("for gQUIC frames", func() { BeforeEach(func() { - unpacker.version = versionCryptoStream0 + unpacker.version = versionGQUICFrames + }) + + It("handles PADDING frames", func() { + setData([]byte{0, 0, 0}) // 3 bytes PADDING + packet, err := unpacker.Unpack(hdrBin, hdr, data) + Expect(err).ToNot(HaveOccurred()) + Expect(packet.frames).To(BeEmpty()) + }) + + It("handles PADDING between two other frames", func() { + f := &wire.PingFrame{} + err := f.Write(buf, versionGQUICFrames) + Expect(err).ToNot(HaveOccurred()) + _, err = buf.Write(bytes.Repeat([]byte{0}, 10)) // 10 bytes PADDING + Expect(err).ToNot(HaveOccurred()) + err = f.Write(buf, versionGQUICFrames) + Expect(err).ToNot(HaveOccurred()) + setData(buf.Bytes()) + packet, err := unpacker.Unpack(hdrBin, hdr, data) + Expect(err).ToNot(HaveOccurred()) + Expect(packet.frames).To(HaveLen(2)) + }) + + It("unpacks RST_STREAM frames", func() { + f := &wire.RstStreamFrame{ + StreamID: 0xdeadbeef, + ByteOffset: 0xdecafbad11223344, + ErrorCode: 0x13371234, + } + err := f.Write(buf, versionGQUICFrames) + 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 CONNECTION_CLOSE frames", func() { + f := &wire.ConnectionCloseFrame{ReasonPhrase: "foo"} + err := f.Write(buf, versionGQUICFrames) + 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 GOAWAY frames", func() { + f := &wire.GoawayFrame{ + ErrorCode: 1, + LastGoodStream: 2, + ReasonPhrase: "foo", + } + err := f.Write(buf, 0) + 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 a stream-level WINDOW_UPDATE frame", func() { + f := &wire.MaxStreamDataFrame{ + StreamID: 0xdeadbeef, + ByteOffset: 0xcafe000000001337, + } + buf := &bytes.Buffer{} + err := f.Write(buf, versionGQUICFrames) + 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 a connection-level WINDOW_UPDATE frame", func() { + f := &wire.MaxDataFrame{ + ByteOffset: 0xcafe000000001337, + } + buf := &bytes.Buffer{} + err := f.Write(buf, versionGQUICFrames) + 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 connection-level BLOCKED frames", func() { + f := &wire.BlockedFrame{} + buf := &bytes.Buffer{} + err := f.Write(buf, versionGQUICFrames) + 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 stream-level BLOCKED frames", func() { + f := &wire.StreamBlockedFrame{StreamID: 0xdeadbeef} + buf := &bytes.Buffer{} + err := f.Write(buf, versionGQUICFrames) + 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 STOP_WAITING frames", func() { + setData([]byte{0x06, 0x03}) + packet, err := unpacker.Unpack(hdrBin, hdr, data) + Expect(err).ToNot(HaveOccurred()) + Expect(packet.frames).To(Equal([]wire.Frame{ + &wire.StopWaitingFrame{LeastUnacked: 7}, + })) + }) + + It("unpacks PING frames", func() { + setData([]byte{0x07}) + packet, err := unpacker.Unpack(hdrBin, hdr, data) + Expect(err).ToNot(HaveOccurred()) + Expect(packet.frames).To(Equal([]wire.Frame{ + &wire.PingFrame{}, + })) + }) + + It("errors on invalid type", func() { + setData([]byte{0xf}) + _, err := unpacker.Unpack(hdrBin, hdr, data) + Expect(err).To(MatchError("InvalidFrameData: unknown type byte 0xf")) + }) + + It("errors on invalid frames", func() { + for b, e := range map[byte]qerr.ErrorCode{ + 0x80: qerr.InvalidStreamData, + 0x40: qerr.InvalidAckData, + 0x01: qerr.InvalidRstStreamData, + 0x02: qerr.InvalidConnectionCloseData, + 0x03: qerr.InvalidGoawayData, + 0x04: qerr.InvalidWindowUpdateData, + 0x05: qerr.InvalidBlockedData, + 0x06: qerr.InvalidStopWaitingData, + } { + setData([]byte{b}) + _, err := unpacker.Unpack(hdrBin, hdr, data) + Expect(err.(*qerr.QuicError).ErrorCode).To(Equal(e)) + } + }) + + It("unpacks ACK frames", func() { + f := &wire.AckFrame{ + LargestAcked: 0x13, + LowestAcked: 1, + } + err := f.Write(buf, versionGQUICFrames) + Expect(err).ToNot(HaveOccurred()) + setData(buf.Bytes()) + packet, err := unpacker.Unpack(hdrBin, hdr, data) + Expect(err).ToNot(HaveOccurred()) + Expect(packet.frames).To(HaveLen(1)) + readFrame := packet.frames[0].(*wire.AckFrame) + Expect(readFrame).ToNot(BeNil()) + Expect(readFrame.LargestAcked).To(Equal(protocol.PacketNumber(0x13))) + }) + + Context("unpacking STREAM frames", func() { + It("unpacks unencrypted STREAM frames on the crypto stream", func() { + unpacker.aead.(*mockAEAD).encLevelOpen = protocol.EncryptionUnencrypted + f := &wire.StreamFrame{ + StreamID: versionGQUICFrames.CryptoStreamID(), + Data: []byte("foobar"), + } + err := f.Write(buf, versionGQUICFrames) + 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 encrypted STREAM frames on the crypto stream", func() { + unpacker.aead.(*mockAEAD).encLevelOpen = protocol.EncryptionSecure + f := &wire.StreamFrame{ + StreamID: versionGQUICFrames.CryptoStreamID(), + Data: []byte("foobar"), + } + err := f.Write(buf, versionGQUICFrames) + 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("does not unpack unencrypted STREAM frames on higher streams", func() { + unpacker.aead.(*mockAEAD).encLevelOpen = protocol.EncryptionUnencrypted + f := &wire.StreamFrame{ + StreamID: 3, + Data: []byte("foobar"), + } + err := f.Write(buf, versionGQUICFrames) + Expect(err).ToNot(HaveOccurred()) + setData(buf.Bytes()) + _, err = unpacker.Unpack(hdrBin, hdr, data) + Expect(err).To(MatchError(qerr.Error(qerr.UnencryptedStreamData, "received unencrypted stream data on stream 3"))) + }) + }) + }) + + Context("for IETF draft frames", func() { + BeforeEach(func() { + unpacker.version = versionIETFFrames + }) + + It("unpacks RST_STREAM frames", func() { + f := &wire.RstStreamFrame{ + StreamID: 0xdeadbeef, + ByteOffset: 0xdecafbad11223344, + ErrorCode: 0x13371234, + } + 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 CONNECTION_CLOSE frames", func() { + f := &wire.ConnectionCloseFrame{ReasonPhrase: "foo"} + 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 MAX_DATA frames", func() { @@ -161,7 +320,7 @@ var _ = Describe("Packet unpacker", func() { ByteOffset: 0xcafe000000001337, } buf := &bytes.Buffer{} - err := f.Write(buf, versionCryptoStream0) + err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) setData(buf.Bytes()) packet, err := unpacker.Unpack(hdrBin, hdr, data) @@ -175,7 +334,7 @@ var _ = Describe("Packet unpacker", func() { ByteOffset: 0xcafe000000001337, } buf := &bytes.Buffer{} - err := f.Write(buf, versionCryptoStream0) + err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) setData(buf.Bytes()) packet, err := unpacker.Unpack(hdrBin, hdr, data) @@ -186,7 +345,7 @@ var _ = Describe("Packet unpacker", func() { It("unpacks connection-level BLOCKED frames", func() { f := &wire.BlockedFrame{} buf := &bytes.Buffer{} - err := f.Write(buf, versionCryptoStream0) + err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) setData(buf.Bytes()) packet, err := unpacker.Unpack(hdrBin, hdr, data) @@ -197,7 +356,7 @@ var _ = Describe("Packet unpacker", func() { It("unpacks stream-level BLOCKED frames", func() { f := &wire.StreamBlockedFrame{StreamID: 0xdeadbeef} buf := &bytes.Buffer{} - err := f.Write(buf, versionCryptoStream0) + err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) setData(buf.Bytes()) packet, err := unpacker.Unpack(hdrBin, hdr, data) @@ -205,8 +364,16 @@ var _ = Describe("Packet unpacker", func() { Expect(packet.frames).To(Equal([]wire.Frame{f})) }) + It("errors on invalid type", func() { + setData([]byte{0xf}) + _, err := unpacker.Unpack(hdrBin, hdr, data) + Expect(err).To(MatchError("InvalidFrameData: unknown type byte 0xf")) + }) + It("errors on invalid frames", func() { for b, e := range map[byte]qerr.ErrorCode{ + 0x01: qerr.InvalidRstStreamData, + 0x02: qerr.InvalidConnectionCloseData, 0x04: qerr.InvalidWindowUpdateData, 0x05: qerr.InvalidWindowUpdateData, 0x09: qerr.InvalidBlockedData, @@ -217,152 +384,4 @@ var _ = Describe("Packet unpacker", func() { } }) }) - - Context("flow control frames, when the crypto stream is stream 1", func() { - BeforeEach(func() { - unpacker.version = versionCryptoStream1 - }) - - It("unpacks a stream-level WINDOW_UPDATE frame", func() { - f := &wire.MaxStreamDataFrame{ - StreamID: 0xdeadbeef, - ByteOffset: 0xcafe000000001337, - } - buf := &bytes.Buffer{} - err := f.Write(buf, versionCryptoStream1) - 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 a connection-level WINDOW_UPDATE frame", func() { - f := &wire.MaxDataFrame{ - ByteOffset: 0xcafe000000001337, - } - buf := &bytes.Buffer{} - err := f.Write(buf, versionCryptoStream1) - 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 connection-level BLOCKED frames", func() { - f := &wire.BlockedFrame{} - buf := &bytes.Buffer{} - err := f.Write(buf, versionCryptoStream1) - 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 stream-level BLOCKED frames", func() { - f := &wire.StreamBlockedFrame{StreamID: 0xdeadbeef} - buf := &bytes.Buffer{} - err := f.Write(buf, versionCryptoStream1) - 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("errors on invalid frames", func() { - for b, e := range map[byte]qerr.ErrorCode{ - 0x04: qerr.InvalidWindowUpdateData, - 0x05: qerr.InvalidBlockedData, - } { - setData([]byte{b}) - _, err := unpacker.Unpack(hdrBin, hdr, data) - Expect(err.(*qerr.QuicError).ErrorCode).To(Equal(e)) - } - }) - }) - - It("unpacks STOP_WAITING frames", func() { - setData([]byte{0x06, 0x03}) - packet, err := unpacker.Unpack(hdrBin, hdr, data) - Expect(err).ToNot(HaveOccurred()) - Expect(packet.frames).To(Equal([]wire.Frame{ - &wire.StopWaitingFrame{LeastUnacked: 7}, - })) - }) - - It("unpacks PING frames", func() { - setData([]byte{0x07}) - packet, err := unpacker.Unpack(hdrBin, hdr, data) - Expect(err).ToNot(HaveOccurred()) - Expect(packet.frames).To(Equal([]wire.Frame{ - &wire.PingFrame{}, - })) - }) - - It("errors on invalid type", func() { - setData([]byte{0xf}) - _, err := unpacker.Unpack(hdrBin, hdr, data) - Expect(err).To(MatchError("InvalidFrameData: unknown type byte 0xf")) - }) - - It("errors on invalid frames", func() { - for b, e := range map[byte]qerr.ErrorCode{ - 0x80: qerr.InvalidStreamData, - 0x40: qerr.InvalidAckData, - 0x01: qerr.InvalidRstStreamData, - 0x02: qerr.InvalidConnectionCloseData, - 0x03: qerr.InvalidGoawayData, - 0x06: qerr.InvalidStopWaitingData, - } { - setData([]byte{b}) - _, err := unpacker.Unpack(hdrBin, hdr, data) - Expect(err.(*qerr.QuicError).ErrorCode).To(Equal(e)) - } - }) - - Context("unpacking STREAM frames", func() { - It("unpacks unencrypted STREAM frames on the crypto stream", func() { - unpacker.aead.(*mockAEAD).encLevelOpen = protocol.EncryptionUnencrypted - f := &wire.StreamFrame{ - StreamID: unpacker.version.CryptoStreamID(), - Data: []byte("foobar"), - } - err := f.Write(buf, 0) - 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 encrypted STREAM frames on the crypto stream", func() { - unpacker.aead.(*mockAEAD).encLevelOpen = protocol.EncryptionSecure - f := &wire.StreamFrame{ - StreamID: unpacker.version.CryptoStreamID(), - Data: []byte("foobar"), - } - err := f.Write(buf, 0) - 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("does not unpack unencrypted STREAM frames on higher streams", func() { - unpacker.aead.(*mockAEAD).encLevelOpen = protocol.EncryptionUnencrypted - f := &wire.StreamFrame{ - StreamID: 3, - Data: []byte("foobar"), - } - err := f.Write(buf, 0) - Expect(err).ToNot(HaveOccurred()) - setData(buf.Bytes()) - _, err = unpacker.Unpack(hdrBin, hdr, data) - Expect(err).To(MatchError(qerr.Error(qerr.UnencryptedStreamData, "received unencrypted stream data on stream 3"))) - }) - }) }) diff --git a/quic_suite_test.go b/quic_suite_test.go index 840e1041..fefe277f 100644 --- a/quic_suite_test.go +++ b/quic_suite_test.go @@ -15,15 +15,17 @@ func TestQuicGo(t *testing.T) { } const ( - versionCryptoStream1 = protocol.Version39 - versionCryptoStream0 = protocol.VersionTLS + versionGQUICFrames = protocol.Version39 + versionIETFFrames = protocol.VersionTLS ) var mockCtrl *gomock.Controller var _ = BeforeSuite(func() { - Expect(versionCryptoStream0.CryptoStreamID()).To(Equal(protocol.StreamID(0))) - Expect(versionCryptoStream1.CryptoStreamID()).To(Equal(protocol.StreamID(1))) + Expect(versionGQUICFrames.CryptoStreamID()).To(Equal(protocol.StreamID(1))) + Expect(versionGQUICFrames.UsesIETFFrameFormat()).To(BeFalse()) + Expect(versionIETFFrames.CryptoStreamID()).To(Equal(protocol.StreamID(0))) + Expect(versionIETFFrames.UsesIETFFrameFormat()).To(BeTrue()) }) var _ = BeforeEach(func() { diff --git a/streams_map_test.go b/streams_map_test.go index 2cde482e..99936c99 100644 --- a/streams_map_test.go +++ b/streams_map_test.go @@ -50,7 +50,7 @@ var _ = Describe("Streams Map", func() { Context("getting and creating streams", func() { Context("as a server", func() { BeforeEach(func() { - setNewStreamsMap(protocol.PerspectiveServer, versionCryptoStream1) + setNewStreamsMap(protocol.PerspectiveServer, versionGQUICFrames) }) Context("client-side streams", func() { @@ -281,7 +281,7 @@ var _ = Describe("Streams Map", func() { }) It("starts with stream 1, if the crypto stream is stream 0", func() { - setNewStreamsMap(protocol.PerspectiveServer, versionCryptoStream0) + setNewStreamsMap(protocol.PerspectiveServer, versionIETFFrames) var str streamI go func() { defer GinkgoRecover() @@ -414,7 +414,7 @@ var _ = Describe("Streams Map", func() { Context("as a client", func() { BeforeEach(func() { - setNewStreamsMap(protocol.PerspectiveClient, versionCryptoStream1) + setNewStreamsMap(protocol.PerspectiveClient, versionGQUICFrames) m.UpdateMaxStreamLimit(100) }) @@ -463,7 +463,7 @@ var _ = Describe("Streams Map", func() { Context("client-side streams", func() { It("starts with stream 1, if the crypto stream is stream 0", func() { - setNewStreamsMap(protocol.PerspectiveClient, versionCryptoStream0) + setNewStreamsMap(protocol.PerspectiveClient, versionIETFFrames) m.UpdateMaxStreamLimit(100) s, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) @@ -521,7 +521,7 @@ var _ = Describe("Streams Map", func() { Context("DoS mitigation, iterating and deleting", func() { BeforeEach(func() { - setNewStreamsMap(protocol.PerspectiveServer, versionCryptoStream1) + setNewStreamsMap(protocol.PerspectiveServer, versionGQUICFrames) }) closeStream := func(id protocol.StreamID) { @@ -615,7 +615,7 @@ var _ = Describe("Streams Map", func() { Context("as a client", func() { BeforeEach(func() { - setNewStreamsMap(protocol.PerspectiveClient, versionCryptoStream1) + setNewStreamsMap(protocol.PerspectiveClient, versionGQUICFrames) m.UpdateMaxStreamLimit(100) for i := 1; i <= 5; i++ { if i%2 == 0 {