mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-03 20:27:35 +03:00
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.
307 lines
12 KiB
Go
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)))
|
|
})
|
|
})
|
|
})
|