refactor packet unpacking

This replaces version.UsesMaxDataFrame by versoin.UsesIETFFrameFormat.
That way, we can have two separate code paths in the unpacker to unpack
either gQUIC frames or IETF frames.
This commit is contained in:
Marten Seemann 2017-11-17 07:42:55 -08:00
parent 0ac728f96e
commit 759b0d87b1
15 changed files with 429 additions and 347 deletions

View file

@ -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

View file

@ -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() {

View file

@ -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

View file

@ -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)))
})
})
})

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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())
})

View file

@ -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
}

View file

@ -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,21 +72,9 @@ 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)))
Context("for gQUIC frames", func() {
BeforeEach(func() {
unpacker.version = versionGQUICFrames
})
It("handles PADDING frames", func() {
@ -101,11 +86,11 @@ var _ = Describe("Packet unpacker", func() {
It("handles PADDING between two other frames", func() {
f := &wire.PingFrame{}
err := f.Write(buf, protocol.VersionWhatever)
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, protocol.VersionWhatever)
err = f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
@ -119,7 +104,7 @@ var _ = Describe("Packet unpacker", func() {
ByteOffset: 0xdecafbad11223344,
ErrorCode: 0x13371234,
}
err := f.Write(buf, protocol.VersionWhatever)
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
@ -129,7 +114,7 @@ var _ = Describe("Packet unpacker", func() {
It("unpacks CONNECTION_CLOSE frames", func() {
f := &wire.ConnectionCloseFrame{ReasonPhrase: "foo"}
err := f.Write(buf, protocol.VersionWhatever)
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
@ -151,85 +136,13 @@ var _ = Describe("Packet unpacker", func() {
Expect(packet.frames).To(Equal([]wire.Frame{f}))
})
Context("flow control frames, when the crypto stream is stream 0", func() {
BeforeEach(func() {
unpacker.version = versionCryptoStream0
})
It("unpacks MAX_DATA frames", func() {
f := &wire.MaxDataFrame{
ByteOffset: 0xcafe000000001337,
}
buf := &bytes.Buffer{}
err := f.Write(buf, versionCryptoStream0)
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_STREAM_DATA frames", func() {
f := &wire.MaxStreamDataFrame{
StreamID: 0xdeadbeef,
ByteOffset: 0xcafe000000001337,
}
buf := &bytes.Buffer{}
err := f.Write(buf, versionCryptoStream0)
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, versionCryptoStream0)
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, versionCryptoStream0)
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.InvalidWindowUpdateData,
0x09: qerr.InvalidBlockedData,
} {
setData([]byte{b})
_, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err.(*qerr.QuicError).ErrorCode).To(Equal(e))
}
})
})
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)
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
@ -242,7 +155,7 @@ var _ = Describe("Packet unpacker", func() {
ByteOffset: 0xcafe000000001337,
}
buf := &bytes.Buffer{}
err := f.Write(buf, versionCryptoStream1)
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
@ -253,7 +166,7 @@ var _ = Describe("Packet unpacker", func() {
It("unpacks connection-level BLOCKED frames", func() {
f := &wire.BlockedFrame{}
buf := &bytes.Buffer{}
err := f.Write(buf, versionCryptoStream1)
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
@ -264,7 +177,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, versionCryptoStream1)
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
@ -272,18 +185,6 @@ var _ = Describe("Packet unpacker", func() {
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)
@ -315,6 +216,8 @@ var _ = Describe("Packet unpacker", func() {
0x01: qerr.InvalidRstStreamData,
0x02: qerr.InvalidConnectionCloseData,
0x03: qerr.InvalidGoawayData,
0x04: qerr.InvalidWindowUpdateData,
0x05: qerr.InvalidBlockedData,
0x06: qerr.InvalidStopWaitingData,
} {
setData([]byte{b})
@ -323,14 +226,30 @@ var _ = Describe("Packet unpacker", func() {
}
})
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: unpacker.version.CryptoStreamID(),
StreamID: versionGQUICFrames.CryptoStreamID(),
Data: []byte("foobar"),
}
err := f.Write(buf, 0)
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
@ -341,10 +260,10 @@ var _ = Describe("Packet unpacker", func() {
It("unpacks encrypted STREAM frames on the crypto stream", func() {
unpacker.aead.(*mockAEAD).encLevelOpen = protocol.EncryptionSecure
f := &wire.StreamFrame{
StreamID: unpacker.version.CryptoStreamID(),
StreamID: versionGQUICFrames.CryptoStreamID(),
Data: []byte("foobar"),
}
err := f.Write(buf, 0)
err := f.Write(buf, versionGQUICFrames)
Expect(err).ToNot(HaveOccurred())
setData(buf.Bytes())
packet, err := unpacker.Unpack(hdrBin, hdr, data)
@ -358,11 +277,111 @@ var _ = Describe("Packet unpacker", func() {
StreamID: 3,
Data: []byte("foobar"),
}
err := f.Write(buf, 0)
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() {
f := &wire.MaxDataFrame{
ByteOffset: 0xcafe000000001337,
}
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 MAX_STREAM_DATA frames", func() {
f := &wire.MaxStreamDataFrame{
StreamID: 0xdeadbeef,
ByteOffset: 0xcafe000000001337,
}
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 connection-level BLOCKED frames", func() {
f := &wire.BlockedFrame{}
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 stream-level BLOCKED frames", func() {
f := &wire.StreamBlockedFrame{StreamID: 0xdeadbeef}
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("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,
} {
setData([]byte{b})
_, err := unpacker.Unpack(hdrBin, hdr, data)
Expect(err.(*qerr.QuicError).ErrorCode).To(Equal(e))
}
})
})
})

View file

@ -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() {

View file

@ -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 {