forked from quic-go/quic-go
337 lines
10 KiB
Go
337 lines
10 KiB
Go
package quic
|
|
|
|
import (
|
|
"github.com/lucas-clemente/quic-go/frames"
|
|
"github.com/lucas-clemente/quic-go/protocol"
|
|
. "github.com/onsi/ginkgo"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
var _ = Describe("streamFrameQueue", func() {
|
|
var prioFrame1, prioFrame2 *frames.StreamFrame
|
|
var frame1, frame2, frame3 *frames.StreamFrame
|
|
var queue *streamFrameQueue
|
|
|
|
BeforeEach(func() {
|
|
queue = newStreamFrameQueue()
|
|
prioFrame1 = &frames.StreamFrame{
|
|
StreamID: 5,
|
|
Data: []byte{0x13, 0x37},
|
|
}
|
|
prioFrame2 = &frames.StreamFrame{
|
|
StreamID: 6,
|
|
Data: []byte{0xDE, 0xCA, 0xFB, 0xAD},
|
|
}
|
|
frame1 = &frames.StreamFrame{
|
|
StreamID: 10,
|
|
Data: []byte{0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0x13, 0x37},
|
|
}
|
|
frame2 = &frames.StreamFrame{
|
|
StreamID: 11,
|
|
Data: []byte{0xDE, 0xAD, 0xBE, 0xEF, 0x37},
|
|
}
|
|
frame3 = &frames.StreamFrame{
|
|
StreamID: 11,
|
|
Data: []byte{0xBE, 0xEF},
|
|
}
|
|
})
|
|
|
|
It("sets the DataLenPresent on all StreamFrames", func() {
|
|
queue.Push(frame1, false)
|
|
queue.Push(prioFrame1, true)
|
|
Expect(queue.prioFrames[0].DataLenPresent).To(BeTrue())
|
|
Expect(queue.frameMap[frame1.StreamID][0].DataLenPresent).To(BeTrue())
|
|
})
|
|
|
|
Context("Queue Length", func() {
|
|
It("returns the correct length for an empty queue", func() {
|
|
Expect(queue.Len()).To(BeZero())
|
|
})
|
|
|
|
It("returns the correct length for a queue", func() {
|
|
queue.Push(prioFrame1, true)
|
|
Expect(queue.Len()).To(Equal(1))
|
|
queue.Push(frame1, false)
|
|
queue.Push(frame2, false)
|
|
Expect(queue.Len()).To(Equal(3))
|
|
})
|
|
|
|
It("reduces the length when popping", func() {
|
|
queue.Push(frame1, false)
|
|
queue.Push(frame2, false)
|
|
Expect(queue.Len()).To(Equal(2))
|
|
queue.Pop(1000)
|
|
Expect(queue.Len()).To(Equal(1))
|
|
queue.Pop(1000)
|
|
Expect(queue.Len()).To(Equal(0))
|
|
})
|
|
})
|
|
|
|
Context("Queue Byte Length", func() {
|
|
It("returns the correct length for an empty queue", func() {
|
|
Expect(queue.ByteLen()).To(BeZero())
|
|
})
|
|
|
|
It("returns the correct byte length for a queue", func() {
|
|
queue.Push(prioFrame1, true)
|
|
Expect(queue.ByteLen()).To(Equal(protocol.ByteCount(2)))
|
|
queue.Push(frame2, false)
|
|
Expect(queue.ByteLen()).To(Equal(prioFrame1.DataLen() + frame2.DataLen()))
|
|
})
|
|
|
|
It("returns the correct byte length when popping", func() {
|
|
queue.Push(prioFrame1, true)
|
|
queue.Push(frame1, false)
|
|
Expect(queue.ByteLen()).To(Equal(prioFrame1.DataLen() + frame1.DataLen()))
|
|
queue.Pop(1000)
|
|
Expect(queue.ByteLen()).To(Equal(frame1.DataLen()))
|
|
queue.Pop(1000)
|
|
Expect(queue.ByteLen()).To(Equal(protocol.ByteCount(0)))
|
|
})
|
|
})
|
|
|
|
Context("Pushing", func() {
|
|
It("adds the streams to the map", func() {
|
|
queue.Push(frame1, false)
|
|
Expect(queue.frameMap).To(HaveKey(frame1.StreamID))
|
|
Expect(queue.frameMap[frame1.StreamID][0]).To(Equal(frame1))
|
|
})
|
|
|
|
It("only adds a StreamID once to the active stream list", func() {
|
|
queue.Push(frame1, false)
|
|
queue.Push(frame1, false)
|
|
Expect(queue.frameMap).To(HaveKey(frame1.StreamID))
|
|
Expect(queue.frameMap[frame1.StreamID]).To(HaveLen(2))
|
|
Expect(queue.activeStreams).To(HaveLen(1))
|
|
Expect(queue.activeStreams[0]).To(Equal(frame1.StreamID))
|
|
})
|
|
})
|
|
|
|
Context("getNextStream", func() {
|
|
It("returns 0 for an empty queue", func() {
|
|
streamID, err := queue.getNextStream()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(streamID).To(BeZero())
|
|
})
|
|
|
|
It("does not change the byte length when using getNextStream()", func() {
|
|
queue.Push(prioFrame1, true)
|
|
queue.Push(frame1, false)
|
|
length := prioFrame1.DataLen() + frame1.DataLen()
|
|
Expect(queue.ByteLen()).To(Equal(length))
|
|
_, err := queue.getNextStream()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(queue.ByteLen()).To(Equal(length))
|
|
})
|
|
|
|
It("does not change the length when using front()", func() {
|
|
queue.Push(prioFrame1, true)
|
|
queue.Push(frame1, false)
|
|
Expect(queue.Len()).To(Equal(2))
|
|
_, err := queue.getNextStream()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(queue.Len()).To(Equal(2))
|
|
})
|
|
|
|
It("returns normal frames if no prio frames are available", func() {
|
|
queue.Push(frame1, false)
|
|
queue.Push(frame2, false)
|
|
streamID, err := queue.getNextStream()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(streamID).To(Equal(frame1.StreamID))
|
|
})
|
|
|
|
It("gets the frame inserted at first at first", func() {
|
|
queue.Push(frame2, false)
|
|
queue.Push(frame1, false)
|
|
streamID, err := queue.getNextStream()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(streamID).To(Equal(frame2.StreamID))
|
|
})
|
|
})
|
|
|
|
Context("Popping", func() {
|
|
It("returns nil when popping an empty queue", func() {
|
|
Expect(queue.Pop(1000)).To(BeNil())
|
|
})
|
|
|
|
It("deletes elements once they are popped", func() {
|
|
queue.Push(frame1, false)
|
|
frame, err := queue.Pop(1000)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame).To(Equal(frame1))
|
|
frame, err = queue.Pop(1000)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame).To(BeNil())
|
|
})
|
|
|
|
It("returns normal frames if no prio frames are available", func() {
|
|
queue.Push(frame1, false)
|
|
queue.Push(frame2, false)
|
|
frame, err := queue.Pop(1000)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame).To(Equal(frame1))
|
|
frame, err = queue.Pop(1000)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame).To(Equal(frame2))
|
|
})
|
|
|
|
It("returns prio frames first", func() {
|
|
queue.Push(prioFrame1, true)
|
|
queue.Push(frame1, false)
|
|
queue.Push(frame2, false)
|
|
queue.Push(prioFrame2, true)
|
|
frame, err := queue.Pop(1000)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame).To(Equal(prioFrame1))
|
|
frame, err = queue.Pop(1000)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame).To(Equal(prioFrame2))
|
|
frame, err = queue.Pop(1000)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame).To(Equal(frame1))
|
|
})
|
|
|
|
Context("scheduling", func() {
|
|
It("goes around", func() {
|
|
queue.Push(frame2, false) // StreamID: 11
|
|
queue.Push(frame3, false) // StreamID: 11
|
|
queue.Push(frame1, false) // StreamID: 10
|
|
frame, err := queue.Pop(1000)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame).To(Equal(frame2))
|
|
frame, err = queue.Pop(1000)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame).To(Equal(frame1))
|
|
frame, err = queue.Pop(1000)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame).To(Equal(frame3))
|
|
})
|
|
|
|
It("starts with the frame inserted first", func() {
|
|
queue.Push(frame1, false) // StreamID: 10
|
|
queue.Push(frame2, false) // StreamID: 11
|
|
queue.Push(frame3, false) // StreamID: 11
|
|
frame, err := queue.Pop(1000)
|
|
Expect(frame).To(Equal(frame1))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
frame, err = queue.Pop(1000)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame).To(Equal(frame2))
|
|
frame, err = queue.Pop(1000)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame).To(Equal(frame3))
|
|
})
|
|
|
|
It("goes around, also when frame have to be split", func() {
|
|
queue.Push(frame2, false) // StreamID: 11
|
|
queue.Push(frame1, false) // StreamID: 10
|
|
frame, err := queue.Pop(5)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame.StreamID).To(Equal(frame2.StreamID))
|
|
frame, err = queue.Pop(1000)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame).To(Equal(frame1))
|
|
frame, err = queue.Pop(1000)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame.StreamID).To(Equal(frame2.StreamID))
|
|
})
|
|
})
|
|
|
|
Context("splitting of frames", func() {
|
|
It("splits off nothing", func() {
|
|
f := &frames.StreamFrame{
|
|
StreamID: 1,
|
|
Data: []byte("bar"),
|
|
Offset: 3,
|
|
}
|
|
Expect(queue.maybeSplitOffFrame(f, 1000)).To(BeNil())
|
|
Expect(f.Offset).To(Equal(protocol.ByteCount(3)))
|
|
Expect(f.Data).To(Equal([]byte("bar")))
|
|
})
|
|
|
|
It("splits off initial frame", func() {
|
|
f := &frames.StreamFrame{
|
|
StreamID: 1,
|
|
Data: []byte("foobar"),
|
|
DataLenPresent: true,
|
|
Offset: 3,
|
|
FinBit: true,
|
|
}
|
|
minLength, _ := f.MinLength()
|
|
previous := queue.maybeSplitOffFrame(f, minLength-1+3)
|
|
Expect(previous).ToNot(BeNil())
|
|
Expect(previous.StreamID).To(Equal(protocol.StreamID(1)))
|
|
Expect(previous.Data).To(Equal([]byte("foo")))
|
|
Expect(previous.DataLenPresent).To(BeTrue())
|
|
Expect(previous.Offset).To(Equal(protocol.ByteCount(3)))
|
|
Expect(previous.FinBit).To(BeFalse())
|
|
Expect(f.StreamID).To(Equal(protocol.StreamID(1)))
|
|
Expect(f.Data).To(Equal([]byte("bar")))
|
|
Expect(f.DataLenPresent).To(BeTrue())
|
|
Expect(f.Offset).To(Equal(protocol.ByteCount(6)))
|
|
Expect(f.FinBit).To(BeTrue())
|
|
})
|
|
|
|
It("splits a frame", func() {
|
|
queue.Push(frame1, false)
|
|
origlen := frame1.DataLen()
|
|
frame, err := queue.Pop(6)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
minLength, _ := frame.MinLength()
|
|
Expect(minLength - 1 + frame.DataLen()).To(Equal(protocol.ByteCount(6)))
|
|
Expect(queue.frameMap[frame1.StreamID][0].Data).To(HaveLen(int(origlen - frame.DataLen())))
|
|
Expect(queue.frameMap[frame1.StreamID][0].Offset).To(Equal(frame.DataLen()))
|
|
})
|
|
|
|
It("only removes a frame from the queue after return all split parts", func() {
|
|
queue.Push(frame1, false)
|
|
Expect(queue.Len()).To(Equal(1))
|
|
frame, err := queue.Pop(6)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame).ToNot(BeNil())
|
|
Expect(queue.Len()).To(Equal(1))
|
|
frame, err = queue.Pop(100)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame).ToNot(BeNil())
|
|
Expect(queue.Len()).To(BeZero())
|
|
})
|
|
|
|
It("gets the whole data of a frame, when it was split", func() {
|
|
length := frame1.DataLen()
|
|
origdata := make([]byte, length)
|
|
copy(origdata, frame1.Data)
|
|
queue.Push(frame1, false)
|
|
frame, err := queue.Pop(6)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
nextframe, err := queue.Pop(1000)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame.DataLen() + nextframe.DataLen()).To(Equal(length))
|
|
data := make([]byte, length)
|
|
copy(data, frame.Data)
|
|
copy(data[int(frame.DataLen()):], nextframe.Data)
|
|
Expect(data).To(Equal(origdata))
|
|
})
|
|
|
|
It("correctly calculates the byte length when returning a split frame", func() {
|
|
queue.Push(frame1, false)
|
|
queue.Push(frame2, false)
|
|
startByteLength := queue.ByteLen()
|
|
frame, err := queue.Pop(6)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame.StreamID).To(Equal(frame1.StreamID)) // make sure the right frame was popped
|
|
Expect(queue.ByteLen()).To(Equal(startByteLength - frame.DataLen()))
|
|
})
|
|
|
|
It("does not change the length of the queue when returning a split frame", func() {
|
|
queue.Push(frame1, false)
|
|
queue.Push(frame2, false)
|
|
frame, err := queue.Pop(6)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(frame.StreamID).To(Equal(frame1.StreamID)) // make sure the right frame was popped
|
|
Expect(queue.Len()).To(Equal(2))
|
|
})
|
|
})
|
|
})
|
|
})
|