uquic/framer_test.go
Marten Seemann c0286b0c2e fix calculation of the length of appended STREAM frames
For the last STREAM frame we omit the Length field. When packing STREAM
frames, we need to account for this byte saving when calculating the
length of the payload.
2019-08-10 14:55:27 +07:00

307 lines
12 KiB
Go

package quic
import (
"bytes"
"github.com/golang/mock/gomock"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/wire"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Framer", func() {
const (
id1 = protocol.StreamID(10)
id2 = protocol.StreamID(11)
)
var (
framer framer
stream1, stream2 *MockSendStreamI
streamGetter *MockStreamGetter
version protocol.VersionNumber
)
BeforeEach(func() {
streamGetter = NewMockStreamGetter(mockCtrl)
stream1 = NewMockSendStreamI(mockCtrl)
stream1.EXPECT().StreamID().Return(protocol.StreamID(5)).AnyTimes()
stream2 = NewMockSendStreamI(mockCtrl)
stream2.EXPECT().StreamID().Return(protocol.StreamID(6)).AnyTimes()
framer = newFramer(streamGetter, version)
})
Context("handling control frames", func() {
It("adds control frames", func() {
mdf := &wire.MaxDataFrame{ByteOffset: 0x42}
msdf := &wire.MaxStreamDataFrame{ByteOffset: 0x1337}
framer.QueueControlFrame(mdf)
framer.QueueControlFrame(msdf)
frames, length := framer.AppendControlFrames(nil, 1000)
Expect(frames).To(ContainElement(mdf))
Expect(frames).To(ContainElement(msdf))
Expect(length).To(Equal(mdf.Length(version) + msdf.Length(version)))
})
It("appends to the slice given", func() {
ack := &wire.AckFrame{}
mdf := &wire.MaxDataFrame{ByteOffset: 0x42}
framer.QueueControlFrame(mdf)
frames, length := framer.AppendControlFrames([]wire.Frame{ack}, 1000)
Expect(frames).To(Equal([]wire.Frame{ack, mdf}))
Expect(length).To(Equal(mdf.Length(version)))
})
It("adds the right number of frames", func() {
maxSize := protocol.ByteCount(1000)
bf := &wire.DataBlockedFrame{DataLimit: 0x1337}
bfLen := bf.Length(version)
numFrames := int(maxSize / bfLen) // max number of frames that fit into maxSize
for i := 0; i < numFrames+1; i++ {
framer.QueueControlFrame(bf)
}
frames, length := framer.AppendControlFrames(nil, maxSize)
Expect(frames).To(HaveLen(numFrames))
Expect(length).To(BeNumerically(">", maxSize-bfLen))
frames, length = framer.AppendControlFrames(nil, maxSize)
Expect(frames).To(HaveLen(1))
Expect(length).To(Equal(bfLen))
})
})
Context("popping STREAM frames", func() {
It("returns nil when popping an empty framer", func() {
Expect(framer.AppendStreamFrames(nil, 1000)).To(BeEmpty())
})
It("returns STREAM frames", func() {
streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
f := &wire.StreamFrame{
StreamID: id1,
Data: []byte("foobar"),
Offset: 42,
DataLenPresent: true,
}
stream1.EXPECT().popStreamFrame(gomock.Any()).Return(f, false)
framer.AddActiveStream(id1)
fs, length := framer.AppendStreamFrames(nil, 1000)
Expect(fs).To(Equal([]wire.Frame{f}))
Expect(fs[0].(*wire.StreamFrame).DataLenPresent).To(BeFalse())
Expect(length).To(Equal(f.Length(version)))
})
It("appends to a frame slice", func() {
streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
f := &wire.StreamFrame{
StreamID: id1,
Data: []byte("foobar"),
DataLenPresent: true,
}
stream1.EXPECT().popStreamFrame(gomock.Any()).Return(f, false)
framer.AddActiveStream(id1)
mdf := &wire.MaxDataFrame{ByteOffset: 1337}
frames := []wire.Frame{mdf}
fs, length := framer.AppendStreamFrames(frames, 1000)
Expect(fs).To(Equal([]wire.Frame{mdf, f}))
Expect(fs[1].(*wire.StreamFrame).DataLenPresent).To(BeFalse())
Expect(length).To(Equal(f.Length(version)))
})
It("skips a stream that was reported active, but was completed shortly after", func() {
streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(nil, nil)
streamGetter.EXPECT().GetOrOpenSendStream(id2).Return(stream2, nil)
f := &wire.StreamFrame{
StreamID: id2,
Data: []byte("foobar"),
DataLenPresent: true,
}
stream2.EXPECT().popStreamFrame(gomock.Any()).Return(f, false)
framer.AddActiveStream(id1)
framer.AddActiveStream(id2)
frames, _ := framer.AppendStreamFrames(nil, 1000)
Expect(frames).To(Equal([]wire.Frame{f}))
})
It("skips a stream that was reported active, but doesn't have any data", func() {
streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
streamGetter.EXPECT().GetOrOpenSendStream(id2).Return(stream2, nil)
f := &wire.StreamFrame{
StreamID: id2,
Data: []byte("foobar"),
DataLenPresent: true,
}
stream1.EXPECT().popStreamFrame(gomock.Any()).Return(nil, false)
stream2.EXPECT().popStreamFrame(gomock.Any()).Return(f, false)
framer.AddActiveStream(id1)
framer.AddActiveStream(id2)
frames, _ := framer.AppendStreamFrames(nil, 1000)
Expect(frames).To(Equal([]wire.Frame{f}))
})
It("pops from a stream multiple times, if it has enough data", func() {
streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil).Times(2)
f1 := &wire.StreamFrame{StreamID: id1, Data: []byte("foobar")}
f2 := &wire.StreamFrame{StreamID: id1, Data: []byte("foobaz")}
stream1.EXPECT().popStreamFrame(gomock.Any()).Return(f1, true)
stream1.EXPECT().popStreamFrame(gomock.Any()).Return(f2, false)
framer.AddActiveStream(id1) // only add it once
frames, _ := framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize)
Expect(frames).To(Equal([]wire.Frame{f1}))
frames, _ = framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize)
Expect(frames).To(Equal([]wire.Frame{f2}))
// no further calls to popStreamFrame, after popStreamFrame said there's no more data
frames, _ = framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize)
Expect(frames).To(BeNil())
})
It("re-queues a stream at the end, if it has enough data", func() {
streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil).Times(2)
streamGetter.EXPECT().GetOrOpenSendStream(id2).Return(stream2, nil)
f11 := &wire.StreamFrame{StreamID: id1, Data: []byte("foobar")}
f12 := &wire.StreamFrame{StreamID: id1, Data: []byte("foobaz")}
f2 := &wire.StreamFrame{StreamID: id2, Data: []byte("raboof")}
stream1.EXPECT().popStreamFrame(gomock.Any()).Return(f11, true)
stream1.EXPECT().popStreamFrame(gomock.Any()).Return(f12, false)
stream2.EXPECT().popStreamFrame(gomock.Any()).Return(f2, false)
framer.AddActiveStream(id1) // only add it once
framer.AddActiveStream(id2)
// first a frame from stream 1
frames, _ := framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize)
Expect(frames).To(Equal([]wire.Frame{f11}))
// then a frame from stream 2
frames, _ = framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize)
Expect(frames).To(Equal([]wire.Frame{f2}))
// then another frame from stream 1
frames, _ = framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize)
Expect(frames).To(Equal([]wire.Frame{f12}))
})
It("only dequeues data from each stream once per packet", func() {
streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
streamGetter.EXPECT().GetOrOpenSendStream(id2).Return(stream2, nil)
f1 := &wire.StreamFrame{StreamID: id1, Data: []byte("foobar")}
f2 := &wire.StreamFrame{StreamID: id2, Data: []byte("raboof")}
// both streams have more data, and will be re-queued
stream1.EXPECT().popStreamFrame(gomock.Any()).Return(f1, true)
stream2.EXPECT().popStreamFrame(gomock.Any()).Return(f2, true)
framer.AddActiveStream(id1)
framer.AddActiveStream(id2)
frames, length := framer.AppendStreamFrames(nil, 1000)
Expect(frames).To(Equal([]wire.Frame{f1, f2}))
Expect(length).To(Equal(f1.Length(version) + f2.Length(version)))
})
It("returns multiple normal frames in the order they were reported active", func() {
streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
streamGetter.EXPECT().GetOrOpenSendStream(id2).Return(stream2, nil)
f1 := &wire.StreamFrame{Data: []byte("foobar")}
f2 := &wire.StreamFrame{Data: []byte("foobaz")}
stream1.EXPECT().popStreamFrame(gomock.Any()).Return(f1, false)
stream2.EXPECT().popStreamFrame(gomock.Any()).Return(f2, false)
framer.AddActiveStream(id2)
framer.AddActiveStream(id1)
frames, _ := framer.AppendStreamFrames(nil, 1000)
Expect(frames).To(Equal([]wire.Frame{f2, f1}))
})
It("only asks a stream for data once, even if it was reported active multiple times", func() {
streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
f := &wire.StreamFrame{Data: []byte("foobar")}
stream1.EXPECT().popStreamFrame(gomock.Any()).Return(f, false) // only one call to this function
framer.AddActiveStream(id1)
framer.AddActiveStream(id1)
frames, _ := framer.AppendStreamFrames(nil, 1000)
Expect(frames).To(HaveLen(1))
})
It("does not pop empty frames", func() {
fs, length := framer.AppendStreamFrames(nil, 500)
Expect(fs).To(BeEmpty())
Expect(length).To(BeZero())
})
It("pops maximum size STREAM frames", func() {
for i := protocol.MinStreamFrameSize; i < 2000; i++ {
streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
stream1.EXPECT().popStreamFrame(gomock.Any()).DoAndReturn(func(size protocol.ByteCount) (*wire.StreamFrame, bool) {
f := &wire.StreamFrame{
StreamID: id1,
DataLenPresent: true,
}
f.Data = make([]byte, f.MaxDataLen(size, version))
Expect(f.Length(version)).To(Equal(size))
return f, false
})
framer.AddActiveStream(id1)
frames, _ := framer.AppendStreamFrames(nil, i)
Expect(frames).To(HaveLen(1))
f := frames[0].(*wire.StreamFrame)
Expect(f.DataLenPresent).To(BeFalse())
Expect(f.Length(version)).To(Equal(i))
}
})
It("pops multiple STREAM frames", func() {
for i := 2 * protocol.MinStreamFrameSize; i < 2000; i++ {
streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
streamGetter.EXPECT().GetOrOpenSendStream(id2).Return(stream2, nil)
stream1.EXPECT().popStreamFrame(gomock.Any()).DoAndReturn(func(size protocol.ByteCount) (*wire.StreamFrame, bool) {
f := &wire.StreamFrame{
StreamID: id2,
DataLenPresent: true,
}
f.Data = make([]byte, f.MaxDataLen(protocol.MinStreamFrameSize, version))
return f, false
})
stream2.EXPECT().popStreamFrame(gomock.Any()).DoAndReturn(func(size protocol.ByteCount) (*wire.StreamFrame, bool) {
f := &wire.StreamFrame{
StreamID: id2,
DataLenPresent: true,
}
f.Data = make([]byte, f.MaxDataLen(size, version))
Expect(f.Length(version)).To(Equal(size))
return f, false
})
framer.AddActiveStream(id1)
framer.AddActiveStream(id2)
frames, _ := framer.AppendStreamFrames(nil, i)
Expect(frames).To(HaveLen(2))
f1 := frames[0].(*wire.StreamFrame)
f2 := frames[1].(*wire.StreamFrame)
Expect(f1.DataLenPresent).To(BeTrue())
Expect(f2.DataLenPresent).To(BeFalse())
Expect(f1.Length(version) + f2.Length(version)).To(Equal(i))
}
})
It("pops frames that when asked for the the minimum STREAM frame size", func() {
streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
stream1.EXPECT().popStreamFrame(gomock.Any()).Return(&wire.StreamFrame{Data: []byte("foobar")}, false)
framer.AddActiveStream(id1)
framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize)
})
It("does not pop frames smaller than the minimum size", func() {
// don't expect a call to PopStreamFrame()
framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize-1)
})
It("stops iterating when the remaining size is smaller than the minimum STREAM frame size", func() {
streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
// pop a frame such that the remaining size is one byte less than the minimum STREAM frame size
f := &wire.StreamFrame{
StreamID: id1,
Data: bytes.Repeat([]byte("f"), int(500-protocol.MinStreamFrameSize)),
DataLenPresent: true,
}
stream1.EXPECT().popStreamFrame(gomock.Any()).Return(f, false)
framer.AddActiveStream(id1)
fs, length := framer.AppendStreamFrames(nil, 500)
Expect(fs).To(Equal([]wire.Frame{f}))
Expect(length).To(Equal(f.Length(version)))
})
})
})