forked from quic-go/quic-go
internalize ackhandler and congestion
This commit is contained in:
7
internal/ackhandler/_gen.go
Normal file
7
internal/ackhandler/_gen.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "github.com/clipperhouse/linkedlist"
|
||||
_ "github.com/clipperhouse/slice"
|
||||
_ "github.com/clipperhouse/stringer"
|
||||
)
|
||||
24
internal/ackhandler/ackhandler_suite_test.go
Normal file
24
internal/ackhandler/ackhandler_suite_test.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package ackhandler
|
||||
|
||||
import (
|
||||
"github.com/golang/mock/gomock"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCrypto(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "AckHandler Suite")
|
||||
}
|
||||
|
||||
var mockCtrl *gomock.Controller
|
||||
|
||||
var _ = BeforeEach(func() {
|
||||
mockCtrl = gomock.NewController(GinkgoT())
|
||||
})
|
||||
|
||||
var _ = AfterEach(func() {
|
||||
mockCtrl.Finish()
|
||||
})
|
||||
48
internal/ackhandler/interfaces.go
Normal file
48
internal/ackhandler/interfaces.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package ackhandler
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/wire"
|
||||
)
|
||||
|
||||
// SentPacketHandler handles ACKs received for outgoing packets
|
||||
type SentPacketHandler interface {
|
||||
// SentPacket may modify the packet
|
||||
SentPacket(packet *Packet) error
|
||||
ReceivedAck(ackFrame *wire.AckFrame, withPacketNumber protocol.PacketNumber, encLevel protocol.EncryptionLevel, recvTime time.Time) error
|
||||
SetHandshakeComplete()
|
||||
|
||||
// SendingAllowed says if a packet can be sent.
|
||||
// Sending packets might not be possible because:
|
||||
// * we're congestion limited
|
||||
// * we're tracking the maximum number of sent packets
|
||||
SendingAllowed() bool
|
||||
// TimeUntilSend is the time when the next packet should be sent.
|
||||
// It is used for pacing packets.
|
||||
TimeUntilSend() time.Time
|
||||
// ShouldSendNumPackets returns the number of packets that should be sent immediately.
|
||||
// It always returns a number greater or equal than 1.
|
||||
// A number greater than 1 is returned when the pacing delay is smaller than the minimum pacing delay.
|
||||
// Note that the number of packets is only calculated based on the pacing algorithm.
|
||||
// Before sending any packet, SendingAllowed() must be called to learn if we can actually send it.
|
||||
ShouldSendNumPackets() int
|
||||
|
||||
GetStopWaitingFrame(force bool) *wire.StopWaitingFrame
|
||||
GetLowestPacketNotConfirmedAcked() protocol.PacketNumber
|
||||
DequeuePacketForRetransmission() (packet *Packet)
|
||||
GetLeastUnacked() protocol.PacketNumber
|
||||
|
||||
GetAlarmTimeout() time.Time
|
||||
OnAlarm()
|
||||
}
|
||||
|
||||
// ReceivedPacketHandler handles ACKs needed to send for incoming packets
|
||||
type ReceivedPacketHandler interface {
|
||||
ReceivedPacket(packetNumber protocol.PacketNumber, rcvTime time.Time, shouldInstigateAck bool) error
|
||||
IgnoreBelow(protocol.PacketNumber)
|
||||
|
||||
GetAlarmTimeout() time.Time
|
||||
GetAckFrame() *wire.AckFrame
|
||||
}
|
||||
35
internal/ackhandler/packet.go
Normal file
35
internal/ackhandler/packet.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package ackhandler
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/wire"
|
||||
)
|
||||
|
||||
// A Packet is a packet
|
||||
// +gen linkedlist
|
||||
type Packet struct {
|
||||
PacketNumber protocol.PacketNumber
|
||||
Frames []wire.Frame
|
||||
Length protocol.ByteCount
|
||||
EncryptionLevel protocol.EncryptionLevel
|
||||
|
||||
largestAcked protocol.PacketNumber // if the packet contains an ACK, the LargestAcked value of that ACK
|
||||
sendTime time.Time
|
||||
}
|
||||
|
||||
// GetFramesForRetransmission gets all the frames for retransmission
|
||||
func (p *Packet) GetFramesForRetransmission() []wire.Frame {
|
||||
var fs []wire.Frame
|
||||
for _, frame := range p.Frames {
|
||||
switch frame.(type) {
|
||||
case *wire.AckFrame:
|
||||
continue
|
||||
case *wire.StopWaitingFrame:
|
||||
continue
|
||||
}
|
||||
fs = append(fs, frame)
|
||||
}
|
||||
return fs
|
||||
}
|
||||
214
internal/ackhandler/packet_linkedlist.go
Normal file
214
internal/ackhandler/packet_linkedlist.go
Normal file
@@ -0,0 +1,214 @@
|
||||
// Generated by: main
|
||||
// TypeWriter: linkedlist
|
||||
// Directive: +gen on Packet
|
||||
|
||||
package ackhandler
|
||||
|
||||
// List is a modification of http://golang.org/pkg/container/list/
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// PacketElement is an element of a linked list.
|
||||
type PacketElement struct {
|
||||
// Next and previous pointers in the doubly-linked list of elements.
|
||||
// To simplify the implementation, internally a list l is implemented
|
||||
// as a ring, such that &l.root is both the next element of the last
|
||||
// list element (l.Back()) and the previous element of the first list
|
||||
// element (l.Front()).
|
||||
next, prev *PacketElement
|
||||
|
||||
// The list to which this element belongs.
|
||||
list *PacketList
|
||||
|
||||
// The value stored with this element.
|
||||
Value Packet
|
||||
}
|
||||
|
||||
// Next returns the next list element or nil.
|
||||
func (e *PacketElement) Next() *PacketElement {
|
||||
if p := e.next; e.list != nil && p != &e.list.root {
|
||||
return p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Prev returns the previous list element or nil.
|
||||
func (e *PacketElement) Prev() *PacketElement {
|
||||
if p := e.prev; e.list != nil && p != &e.list.root {
|
||||
return p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PacketList represents a doubly linked list.
|
||||
// The zero value for PacketList is an empty list ready to use.
|
||||
type PacketList struct {
|
||||
root PacketElement // sentinel list element, only &root, root.prev, and root.next are used
|
||||
len int // current list length excluding (this) sentinel element
|
||||
}
|
||||
|
||||
// Init initializes or clears list l.
|
||||
func (l *PacketList) Init() *PacketList {
|
||||
l.root.next = &l.root
|
||||
l.root.prev = &l.root
|
||||
l.len = 0
|
||||
return l
|
||||
}
|
||||
|
||||
// NewPacketList returns an initialized list.
|
||||
func NewPacketList() *PacketList { return new(PacketList).Init() }
|
||||
|
||||
// Len returns the number of elements of list l.
|
||||
// The complexity is O(1).
|
||||
func (l *PacketList) Len() int { return l.len }
|
||||
|
||||
// Front returns the first element of list l or nil.
|
||||
func (l *PacketList) Front() *PacketElement {
|
||||
if l.len == 0 {
|
||||
return nil
|
||||
}
|
||||
return l.root.next
|
||||
}
|
||||
|
||||
// Back returns the last element of list l or nil.
|
||||
func (l *PacketList) Back() *PacketElement {
|
||||
if l.len == 0 {
|
||||
return nil
|
||||
}
|
||||
return l.root.prev
|
||||
}
|
||||
|
||||
// lazyInit lazily initializes a zero PacketList value.
|
||||
func (l *PacketList) lazyInit() {
|
||||
if l.root.next == nil {
|
||||
l.Init()
|
||||
}
|
||||
}
|
||||
|
||||
// insert inserts e after at, increments l.len, and returns e.
|
||||
func (l *PacketList) insert(e, at *PacketElement) *PacketElement {
|
||||
n := at.next
|
||||
at.next = e
|
||||
e.prev = at
|
||||
e.next = n
|
||||
n.prev = e
|
||||
e.list = l
|
||||
l.len++
|
||||
return e
|
||||
}
|
||||
|
||||
// insertValue is a convenience wrapper for insert(&PacketElement{Value: v}, at).
|
||||
func (l *PacketList) insertValue(v Packet, at *PacketElement) *PacketElement {
|
||||
return l.insert(&PacketElement{Value: v}, at)
|
||||
}
|
||||
|
||||
// remove removes e from its list, decrements l.len, and returns e.
|
||||
func (l *PacketList) remove(e *PacketElement) *PacketElement {
|
||||
e.prev.next = e.next
|
||||
e.next.prev = e.prev
|
||||
e.next = nil // avoid memory leaks
|
||||
e.prev = nil // avoid memory leaks
|
||||
e.list = nil
|
||||
l.len--
|
||||
return e
|
||||
}
|
||||
|
||||
// Remove removes e from l if e is an element of list l.
|
||||
// It returns the element value e.Value.
|
||||
func (l *PacketList) Remove(e *PacketElement) Packet {
|
||||
if e.list == l {
|
||||
// if e.list == l, l must have been initialized when e was inserted
|
||||
// in l or l == nil (e is a zero PacketElement) and l.remove will crash
|
||||
l.remove(e)
|
||||
}
|
||||
return e.Value
|
||||
}
|
||||
|
||||
// PushFront inserts a new element e with value v at the front of list l and returns e.
|
||||
func (l *PacketList) PushFront(v Packet) *PacketElement {
|
||||
l.lazyInit()
|
||||
return l.insertValue(v, &l.root)
|
||||
}
|
||||
|
||||
// PushBack inserts a new element e with value v at the back of list l and returns e.
|
||||
func (l *PacketList) PushBack(v Packet) *PacketElement {
|
||||
l.lazyInit()
|
||||
return l.insertValue(v, l.root.prev)
|
||||
}
|
||||
|
||||
// InsertBefore inserts a new element e with value v immediately before mark and returns e.
|
||||
// If mark is not an element of l, the list is not modified.
|
||||
func (l *PacketList) InsertBefore(v Packet, mark *PacketElement) *PacketElement {
|
||||
if mark.list != l {
|
||||
return nil
|
||||
}
|
||||
// see comment in PacketList.Remove about initialization of l
|
||||
return l.insertValue(v, mark.prev)
|
||||
}
|
||||
|
||||
// InsertAfter inserts a new element e with value v immediately after mark and returns e.
|
||||
// If mark is not an element of l, the list is not modified.
|
||||
func (l *PacketList) InsertAfter(v Packet, mark *PacketElement) *PacketElement {
|
||||
if mark.list != l {
|
||||
return nil
|
||||
}
|
||||
// see comment in PacketList.Remove about initialization of l
|
||||
return l.insertValue(v, mark)
|
||||
}
|
||||
|
||||
// MoveToFront moves element e to the front of list l.
|
||||
// If e is not an element of l, the list is not modified.
|
||||
func (l *PacketList) MoveToFront(e *PacketElement) {
|
||||
if e.list != l || l.root.next == e {
|
||||
return
|
||||
}
|
||||
// see comment in PacketList.Remove about initialization of l
|
||||
l.insert(l.remove(e), &l.root)
|
||||
}
|
||||
|
||||
// MoveToBack moves element e to the back of list l.
|
||||
// If e is not an element of l, the list is not modified.
|
||||
func (l *PacketList) MoveToBack(e *PacketElement) {
|
||||
if e.list != l || l.root.prev == e {
|
||||
return
|
||||
}
|
||||
// see comment in PacketList.Remove about initialization of l
|
||||
l.insert(l.remove(e), l.root.prev)
|
||||
}
|
||||
|
||||
// MoveBefore moves element e to its new position before mark.
|
||||
// If e or mark is not an element of l, or e == mark, the list is not modified.
|
||||
func (l *PacketList) MoveBefore(e, mark *PacketElement) {
|
||||
if e.list != l || e == mark || mark.list != l {
|
||||
return
|
||||
}
|
||||
l.insert(l.remove(e), mark.prev)
|
||||
}
|
||||
|
||||
// MoveAfter moves element e to its new position after mark.
|
||||
// If e is not an element of l, or e == mark, the list is not modified.
|
||||
func (l *PacketList) MoveAfter(e, mark *PacketElement) {
|
||||
if e.list != l || e == mark || mark.list != l {
|
||||
return
|
||||
}
|
||||
l.insert(l.remove(e), mark)
|
||||
}
|
||||
|
||||
// PushBackList inserts a copy of an other list at the back of list l.
|
||||
// The lists l and other may be the same.
|
||||
func (l *PacketList) PushBackList(other *PacketList) {
|
||||
l.lazyInit()
|
||||
for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
|
||||
l.insertValue(e.Value, l.root.prev)
|
||||
}
|
||||
}
|
||||
|
||||
// PushFrontList inserts a copy of an other list at the front of list l.
|
||||
// The lists l and other may be the same.
|
||||
func (l *PacketList) PushFrontList(other *PacketList) {
|
||||
l.lazyInit()
|
||||
for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
|
||||
l.insertValue(e.Value, &l.root)
|
||||
}
|
||||
}
|
||||
51
internal/ackhandler/packet_test.go
Normal file
51
internal/ackhandler/packet_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package ackhandler
|
||||
|
||||
import (
|
||||
"github.com/lucas-clemente/quic-go/internal/wire"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Packet", func() {
|
||||
Context("getting frames for retransmission", func() {
|
||||
ackFrame := &wire.AckFrame{LargestAcked: 13}
|
||||
stopWaitingFrame := &wire.StopWaitingFrame{LeastUnacked: 7331}
|
||||
maxStreamDataFrame := &wire.MaxStreamDataFrame{StreamID: 999}
|
||||
|
||||
streamFrame := &wire.StreamFrame{
|
||||
StreamID: 5,
|
||||
Data: []byte{0x13, 0x37},
|
||||
}
|
||||
|
||||
rstStreamFrame := &wire.RstStreamFrame{
|
||||
StreamID: 555,
|
||||
ErrorCode: 1337,
|
||||
}
|
||||
|
||||
It("returns nil if there are no retransmittable frames", func() {
|
||||
packet := &Packet{
|
||||
Frames: []wire.Frame{ackFrame, stopWaitingFrame},
|
||||
}
|
||||
Expect(packet.GetFramesForRetransmission()).To(BeNil())
|
||||
})
|
||||
|
||||
It("returns all retransmittable frames", func() {
|
||||
packet := &Packet{
|
||||
Frames: []wire.Frame{
|
||||
maxStreamDataFrame,
|
||||
ackFrame,
|
||||
stopWaitingFrame,
|
||||
streamFrame,
|
||||
rstStreamFrame,
|
||||
},
|
||||
}
|
||||
fs := packet.GetFramesForRetransmission()
|
||||
Expect(fs).To(ContainElement(streamFrame))
|
||||
Expect(fs).To(ContainElement(rstStreamFrame))
|
||||
Expect(fs).To(ContainElement(maxStreamDataFrame))
|
||||
Expect(fs).ToNot(ContainElement(stopWaitingFrame))
|
||||
Expect(fs).ToNot(ContainElement(ackFrame))
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
125
internal/ackhandler/received_packet_handler.go
Normal file
125
internal/ackhandler/received_packet_handler.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package ackhandler
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/wire"
|
||||
)
|
||||
|
||||
type receivedPacketHandler struct {
|
||||
largestObserved protocol.PacketNumber
|
||||
ignoreBelow protocol.PacketNumber
|
||||
largestObservedReceivedTime time.Time
|
||||
|
||||
packetHistory *receivedPacketHistory
|
||||
|
||||
ackSendDelay time.Duration
|
||||
|
||||
packetsReceivedSinceLastAck int
|
||||
retransmittablePacketsReceivedSinceLastAck int
|
||||
ackQueued bool
|
||||
ackAlarm time.Time
|
||||
lastAck *wire.AckFrame
|
||||
|
||||
version protocol.VersionNumber
|
||||
}
|
||||
|
||||
// NewReceivedPacketHandler creates a new receivedPacketHandler
|
||||
func NewReceivedPacketHandler(version protocol.VersionNumber) ReceivedPacketHandler {
|
||||
return &receivedPacketHandler{
|
||||
packetHistory: newReceivedPacketHistory(),
|
||||
ackSendDelay: protocol.AckSendDelay,
|
||||
version: version,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *receivedPacketHandler) ReceivedPacket(packetNumber protocol.PacketNumber, rcvTime time.Time, shouldInstigateAck bool) error {
|
||||
if packetNumber > h.largestObserved {
|
||||
h.largestObserved = packetNumber
|
||||
h.largestObservedReceivedTime = rcvTime
|
||||
}
|
||||
|
||||
if packetNumber < h.ignoreBelow {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := h.packetHistory.ReceivedPacket(packetNumber); err != nil {
|
||||
return err
|
||||
}
|
||||
h.maybeQueueAck(packetNumber, rcvTime, shouldInstigateAck)
|
||||
return nil
|
||||
}
|
||||
|
||||
// IgnoreBelow sets a lower limit for acking packets.
|
||||
// Packets with packet numbers smaller than p will not be acked.
|
||||
func (h *receivedPacketHandler) IgnoreBelow(p protocol.PacketNumber) {
|
||||
h.ignoreBelow = p
|
||||
h.packetHistory.DeleteBelow(p)
|
||||
}
|
||||
|
||||
func (h *receivedPacketHandler) maybeQueueAck(packetNumber protocol.PacketNumber, rcvTime time.Time, shouldInstigateAck bool) {
|
||||
h.packetsReceivedSinceLastAck++
|
||||
|
||||
if shouldInstigateAck {
|
||||
h.retransmittablePacketsReceivedSinceLastAck++
|
||||
}
|
||||
|
||||
// always ack the first packet
|
||||
if h.lastAck == nil {
|
||||
h.ackQueued = true
|
||||
}
|
||||
|
||||
// if the packet number is smaller than the largest acked packet, it must have been reported missing with the last ACK
|
||||
// note that it cannot be a duplicate because they're already filtered out by ReceivedPacket()
|
||||
if h.lastAck != nil && packetNumber < h.lastAck.LargestAcked {
|
||||
h.ackQueued = true
|
||||
}
|
||||
|
||||
// check if a new missing range above the previously was created
|
||||
if h.lastAck != nil && h.packetHistory.GetHighestAckRange().First > h.lastAck.LargestAcked {
|
||||
h.ackQueued = true
|
||||
}
|
||||
|
||||
if !h.ackQueued && shouldInstigateAck {
|
||||
if h.retransmittablePacketsReceivedSinceLastAck >= protocol.RetransmittablePacketsBeforeAck {
|
||||
h.ackQueued = true
|
||||
} else {
|
||||
if h.ackAlarm.IsZero() {
|
||||
h.ackAlarm = rcvTime.Add(h.ackSendDelay)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if h.ackQueued {
|
||||
// cancel the ack alarm
|
||||
h.ackAlarm = time.Time{}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *receivedPacketHandler) GetAckFrame() *wire.AckFrame {
|
||||
if !h.ackQueued && (h.ackAlarm.IsZero() || h.ackAlarm.After(time.Now())) {
|
||||
return nil
|
||||
}
|
||||
|
||||
ackRanges := h.packetHistory.GetAckRanges()
|
||||
ack := &wire.AckFrame{
|
||||
LargestAcked: h.largestObserved,
|
||||
LowestAcked: ackRanges[len(ackRanges)-1].First,
|
||||
PacketReceivedTime: h.largestObservedReceivedTime,
|
||||
}
|
||||
|
||||
if len(ackRanges) > 1 {
|
||||
ack.AckRanges = ackRanges
|
||||
}
|
||||
|
||||
h.lastAck = ack
|
||||
h.ackAlarm = time.Time{}
|
||||
h.ackQueued = false
|
||||
h.packetsReceivedSinceLastAck = 0
|
||||
h.retransmittablePacketsReceivedSinceLastAck = 0
|
||||
|
||||
return ack
|
||||
}
|
||||
|
||||
func (h *receivedPacketHandler) GetAlarmTimeout() time.Time { return h.ackAlarm }
|
||||
308
internal/ackhandler/received_packet_handler_test.go
Normal file
308
internal/ackhandler/received_packet_handler_test.go
Normal file
@@ -0,0 +1,308 @@
|
||||
package ackhandler
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"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("receivedPacketHandler", func() {
|
||||
var (
|
||||
handler *receivedPacketHandler
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
handler = NewReceivedPacketHandler(protocol.VersionWhatever).(*receivedPacketHandler)
|
||||
})
|
||||
|
||||
Context("accepting packets", func() {
|
||||
It("handles a packet that arrives late", func() {
|
||||
err := handler.ReceivedPacket(protocol.PacketNumber(1), time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = handler.ReceivedPacket(protocol.PacketNumber(3), time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = handler.ReceivedPacket(protocol.PacketNumber(2), time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("saves the time when each packet arrived", func() {
|
||||
err := handler.ReceivedPacket(protocol.PacketNumber(3), time.Now(), true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.largestObservedReceivedTime).To(BeTemporally("~", time.Now(), 10*time.Millisecond))
|
||||
})
|
||||
|
||||
It("updates the largestObserved and the largestObservedReceivedTime", func() {
|
||||
now := time.Now()
|
||||
handler.largestObserved = 3
|
||||
handler.largestObservedReceivedTime = now.Add(-1 * time.Second)
|
||||
err := handler.ReceivedPacket(5, now, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.largestObserved).To(Equal(protocol.PacketNumber(5)))
|
||||
Expect(handler.largestObservedReceivedTime).To(Equal(now))
|
||||
})
|
||||
|
||||
It("doesn't update the largestObserved and the largestObservedReceivedTime for a belated packet", func() {
|
||||
now := time.Now()
|
||||
timestamp := now.Add(-1 * time.Second)
|
||||
handler.largestObserved = 5
|
||||
handler.largestObservedReceivedTime = timestamp
|
||||
err := handler.ReceivedPacket(4, now, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.largestObserved).To(Equal(protocol.PacketNumber(5)))
|
||||
Expect(handler.largestObservedReceivedTime).To(Equal(timestamp))
|
||||
})
|
||||
|
||||
It("passes on errors from receivedPacketHistory", func() {
|
||||
var err error
|
||||
for i := protocol.PacketNumber(0); i < 5*protocol.MaxTrackedReceivedAckRanges; i++ {
|
||||
err = handler.ReceivedPacket(2*i+1, time.Time{}, true)
|
||||
// this will eventually return an error
|
||||
// details about when exactly the receivedPacketHistory errors are tested there
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
Expect(err).To(MatchError(errTooManyOutstandingReceivedAckRanges))
|
||||
})
|
||||
})
|
||||
|
||||
Context("ACKs", func() {
|
||||
Context("queueing ACKs", func() {
|
||||
receiveAndAck10Packets := func() {
|
||||
for i := 1; i <= 10; i++ {
|
||||
err := handler.ReceivedPacket(protocol.PacketNumber(i), time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
Expect(handler.GetAckFrame()).ToNot(BeNil())
|
||||
Expect(handler.ackQueued).To(BeFalse())
|
||||
}
|
||||
|
||||
It("always queues an ACK for the first packet", func() {
|
||||
err := handler.ReceivedPacket(1, time.Time{}, false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.ackQueued).To(BeTrue())
|
||||
Expect(handler.GetAlarmTimeout()).To(BeZero())
|
||||
})
|
||||
|
||||
It("works with packet number 0", func() {
|
||||
err := handler.ReceivedPacket(0, time.Time{}, false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.ackQueued).To(BeTrue())
|
||||
Expect(handler.GetAlarmTimeout()).To(BeZero())
|
||||
})
|
||||
|
||||
It("queues an ACK for every RetransmittablePacketsBeforeAck retransmittable packet, if they are arriving fast", func() {
|
||||
receiveAndAck10Packets()
|
||||
p := protocol.PacketNumber(11)
|
||||
for i := 0; i < protocol.RetransmittablePacketsBeforeAck-1; i++ {
|
||||
err := handler.ReceivedPacket(p, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.ackQueued).To(BeFalse())
|
||||
p++
|
||||
}
|
||||
Expect(handler.GetAlarmTimeout()).NotTo(BeZero())
|
||||
err := handler.ReceivedPacket(p, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.ackQueued).To(BeTrue())
|
||||
Expect(handler.GetAlarmTimeout()).To(BeZero())
|
||||
})
|
||||
|
||||
It("only sets the timer when receiving a retransmittable packets", func() {
|
||||
receiveAndAck10Packets()
|
||||
err := handler.ReceivedPacket(11, time.Time{}, false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.ackQueued).To(BeFalse())
|
||||
Expect(handler.ackAlarm).To(BeZero())
|
||||
err = handler.ReceivedPacket(12, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.ackQueued).To(BeFalse())
|
||||
Expect(handler.ackAlarm).ToNot(BeZero())
|
||||
Expect(handler.GetAlarmTimeout()).NotTo(BeZero())
|
||||
})
|
||||
|
||||
It("queues an ACK if it was reported missing before", func() {
|
||||
receiveAndAck10Packets()
|
||||
err := handler.ReceivedPacket(11, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = handler.ReceivedPacket(13, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
ack := handler.GetAckFrame() // ACK: 1 and 3, missing: 2
|
||||
Expect(ack).ToNot(BeNil())
|
||||
Expect(ack.HasMissingRanges()).To(BeTrue())
|
||||
Expect(handler.ackQueued).To(BeFalse())
|
||||
err = handler.ReceivedPacket(12, time.Time{}, false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.ackQueued).To(BeTrue())
|
||||
})
|
||||
|
||||
It("queues an ACK if it creates a new missing range", func() {
|
||||
receiveAndAck10Packets()
|
||||
for i := 11; i < 16; i++ {
|
||||
err := handler.ReceivedPacket(protocol.PacketNumber(i), time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
err := handler.ReceivedPacket(20, time.Time{}, true) // we now know that packets 16 to 19 are missing
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.ackQueued).To(BeTrue())
|
||||
ack := handler.GetAckFrame()
|
||||
Expect(ack.HasMissingRanges()).To(BeTrue())
|
||||
Expect(ack).ToNot(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
Context("ACK generation", func() {
|
||||
BeforeEach(func() {
|
||||
handler.ackQueued = true
|
||||
})
|
||||
|
||||
It("generates a simple ACK frame", func() {
|
||||
err := handler.ReceivedPacket(1, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = handler.ReceivedPacket(2, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
ack := handler.GetAckFrame()
|
||||
Expect(ack).ToNot(BeNil())
|
||||
Expect(ack.LargestAcked).To(Equal(protocol.PacketNumber(2)))
|
||||
Expect(ack.LowestAcked).To(Equal(protocol.PacketNumber(1)))
|
||||
Expect(ack.AckRanges).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("generates an ACK for packet number 0", func() {
|
||||
err := handler.ReceivedPacket(0, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
ack := handler.GetAckFrame()
|
||||
Expect(ack).ToNot(BeNil())
|
||||
Expect(ack.LargestAcked).To(Equal(protocol.PacketNumber(0)))
|
||||
Expect(ack.LowestAcked).To(Equal(protocol.PacketNumber(0)))
|
||||
Expect(ack.AckRanges).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("saves the last sent ACK", func() {
|
||||
err := handler.ReceivedPacket(1, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
ack := handler.GetAckFrame()
|
||||
Expect(ack).ToNot(BeNil())
|
||||
Expect(handler.lastAck).To(Equal(ack))
|
||||
err = handler.ReceivedPacket(2, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
handler.ackQueued = true
|
||||
ack = handler.GetAckFrame()
|
||||
Expect(ack).ToNot(BeNil())
|
||||
Expect(handler.lastAck).To(Equal(ack))
|
||||
})
|
||||
|
||||
It("generates an ACK frame with missing packets", func() {
|
||||
err := handler.ReceivedPacket(1, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = handler.ReceivedPacket(4, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
ack := handler.GetAckFrame()
|
||||
Expect(ack).ToNot(BeNil())
|
||||
Expect(ack.LargestAcked).To(Equal(protocol.PacketNumber(4)))
|
||||
Expect(ack.LowestAcked).To(Equal(protocol.PacketNumber(1)))
|
||||
Expect(ack.AckRanges).To(Equal([]wire.AckRange{
|
||||
wire.AckRange{First: 4, Last: 4},
|
||||
wire.AckRange{First: 1, Last: 1},
|
||||
}))
|
||||
})
|
||||
|
||||
It("generates an ACK for packet number 0 and other packets", func() {
|
||||
err := handler.ReceivedPacket(0, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = handler.ReceivedPacket(1, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = handler.ReceivedPacket(3, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
ack := handler.GetAckFrame()
|
||||
Expect(ack).ToNot(BeNil())
|
||||
Expect(ack.LargestAcked).To(Equal(protocol.PacketNumber(3)))
|
||||
Expect(ack.LowestAcked).To(Equal(protocol.PacketNumber(0)))
|
||||
Expect(ack.AckRanges).To(Equal([]wire.AckRange{
|
||||
wire.AckRange{First: 3, Last: 3},
|
||||
wire.AckRange{First: 0, Last: 1},
|
||||
}))
|
||||
})
|
||||
|
||||
It("accepts packets below the lower limit", func() {
|
||||
handler.IgnoreBelow(6)
|
||||
err := handler.ReceivedPacket(2, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("doesn't add delayed packets to the packetHistory", func() {
|
||||
handler.IgnoreBelow(7)
|
||||
err := handler.ReceivedPacket(4, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = handler.ReceivedPacket(10, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
ack := handler.GetAckFrame()
|
||||
Expect(ack).ToNot(BeNil())
|
||||
Expect(ack.LargestAcked).To(Equal(protocol.PacketNumber(10)))
|
||||
Expect(ack.LowestAcked).To(Equal(protocol.PacketNumber(10)))
|
||||
})
|
||||
|
||||
It("deletes packets from the packetHistory when a lower limit is set", func() {
|
||||
for i := 1; i <= 12; i++ {
|
||||
err := handler.ReceivedPacket(protocol.PacketNumber(i), time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
handler.IgnoreBelow(7)
|
||||
// check that the packets were deleted from the receivedPacketHistory by checking the values in an ACK frame
|
||||
ack := handler.GetAckFrame()
|
||||
Expect(ack).ToNot(BeNil())
|
||||
Expect(ack.LargestAcked).To(Equal(protocol.PacketNumber(12)))
|
||||
Expect(ack.LowestAcked).To(Equal(protocol.PacketNumber(7)))
|
||||
Expect(ack.HasMissingRanges()).To(BeFalse())
|
||||
})
|
||||
|
||||
// TODO: remove this test when dropping support for STOP_WAITINGs
|
||||
It("handles a lower limit of 0", func() {
|
||||
handler.IgnoreBelow(0)
|
||||
err := handler.ReceivedPacket(1337, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
ack := handler.GetAckFrame()
|
||||
Expect(ack).ToNot(BeNil())
|
||||
Expect(ack.LargestAcked).To(Equal(protocol.PacketNumber(1337)))
|
||||
})
|
||||
|
||||
It("resets all counters needed for the ACK queueing decision when sending an ACK", func() {
|
||||
err := handler.ReceivedPacket(1, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
handler.ackAlarm = time.Now().Add(-time.Minute)
|
||||
Expect(handler.GetAckFrame()).ToNot(BeNil())
|
||||
Expect(handler.packetsReceivedSinceLastAck).To(BeZero())
|
||||
Expect(handler.ackAlarm).To(BeZero())
|
||||
Expect(handler.retransmittablePacketsReceivedSinceLastAck).To(BeZero())
|
||||
Expect(handler.ackQueued).To(BeFalse())
|
||||
})
|
||||
|
||||
It("doesn't generate an ACK when none is queued and the timer is not set", func() {
|
||||
err := handler.ReceivedPacket(1, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
handler.ackQueued = false
|
||||
handler.ackAlarm = time.Time{}
|
||||
Expect(handler.GetAckFrame()).To(BeNil())
|
||||
})
|
||||
|
||||
It("doesn't generate an ACK when none is queued and the timer has not yet expired", func() {
|
||||
err := handler.ReceivedPacket(1, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
handler.ackQueued = false
|
||||
handler.ackAlarm = time.Now().Add(time.Minute)
|
||||
Expect(handler.GetAckFrame()).To(BeNil())
|
||||
})
|
||||
|
||||
It("generates an ACK when the timer has expired", func() {
|
||||
err := handler.ReceivedPacket(1, time.Time{}, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
handler.ackQueued = false
|
||||
handler.ackAlarm = time.Now().Add(-time.Minute)
|
||||
Expect(handler.GetAckFrame()).ToNot(BeNil())
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
121
internal/ackhandler/received_packet_history.go
Normal file
121
internal/ackhandler/received_packet_history.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package ackhandler
|
||||
|
||||
import (
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||
"github.com/lucas-clemente/quic-go/internal/wire"
|
||||
"github.com/lucas-clemente/quic-go/qerr"
|
||||
)
|
||||
|
||||
// The receivedPacketHistory stores if a packet number has already been received.
|
||||
// It does not store packet contents.
|
||||
type receivedPacketHistory struct {
|
||||
ranges *utils.PacketIntervalList
|
||||
|
||||
lowestInReceivedPacketNumbers protocol.PacketNumber
|
||||
}
|
||||
|
||||
var errTooManyOutstandingReceivedAckRanges = qerr.Error(qerr.TooManyOutstandingReceivedPackets, "Too many outstanding received ACK ranges")
|
||||
|
||||
// newReceivedPacketHistory creates a new received packet history
|
||||
func newReceivedPacketHistory() *receivedPacketHistory {
|
||||
return &receivedPacketHistory{
|
||||
ranges: utils.NewPacketIntervalList(),
|
||||
}
|
||||
}
|
||||
|
||||
// ReceivedPacket registers a packet with PacketNumber p and updates the ranges
|
||||
func (h *receivedPacketHistory) ReceivedPacket(p protocol.PacketNumber) error {
|
||||
if h.ranges.Len() >= protocol.MaxTrackedReceivedAckRanges {
|
||||
return errTooManyOutstandingReceivedAckRanges
|
||||
}
|
||||
|
||||
if h.ranges.Len() == 0 {
|
||||
h.ranges.PushBack(utils.PacketInterval{Start: p, End: p})
|
||||
return nil
|
||||
}
|
||||
|
||||
for el := h.ranges.Back(); el != nil; el = el.Prev() {
|
||||
// p already included in an existing range. Nothing to do here
|
||||
if p >= el.Value.Start && p <= el.Value.End {
|
||||
return nil
|
||||
}
|
||||
|
||||
var rangeExtended bool
|
||||
if el.Value.End == p-1 { // extend a range at the end
|
||||
rangeExtended = true
|
||||
el.Value.End = p
|
||||
} else if el.Value.Start == p+1 { // extend a range at the beginning
|
||||
rangeExtended = true
|
||||
el.Value.Start = p
|
||||
}
|
||||
|
||||
// if a range was extended (either at the beginning or at the end, maybe it is possible to merge two ranges into one)
|
||||
if rangeExtended {
|
||||
prev := el.Prev()
|
||||
if prev != nil && prev.Value.End+1 == el.Value.Start { // merge two ranges
|
||||
prev.Value.End = el.Value.End
|
||||
h.ranges.Remove(el)
|
||||
return nil
|
||||
}
|
||||
return nil // if the two ranges were not merge, we're done here
|
||||
}
|
||||
|
||||
// create a new range at the end
|
||||
if p > el.Value.End {
|
||||
h.ranges.InsertAfter(utils.PacketInterval{Start: p, End: p}, el)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// create a new range at the beginning
|
||||
h.ranges.InsertBefore(utils.PacketInterval{Start: p, End: p}, h.ranges.Front())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteBelow deletes all entries below (but not including) p
|
||||
func (h *receivedPacketHistory) DeleteBelow(p protocol.PacketNumber) {
|
||||
if p <= h.lowestInReceivedPacketNumbers {
|
||||
return
|
||||
}
|
||||
h.lowestInReceivedPacketNumbers = p
|
||||
|
||||
nextEl := h.ranges.Front()
|
||||
for el := h.ranges.Front(); nextEl != nil; el = nextEl {
|
||||
nextEl = el.Next()
|
||||
|
||||
if p > el.Value.Start && p <= el.Value.End {
|
||||
el.Value.Start = p
|
||||
} else if el.Value.End < p { // delete a whole range
|
||||
h.ranges.Remove(el)
|
||||
} else { // no ranges affected. Nothing to do
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetAckRanges gets a slice of all AckRanges that can be used in an AckFrame
|
||||
func (h *receivedPacketHistory) GetAckRanges() []wire.AckRange {
|
||||
if h.ranges.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ackRanges := make([]wire.AckRange, h.ranges.Len())
|
||||
i := 0
|
||||
for el := h.ranges.Back(); el != nil; el = el.Prev() {
|
||||
ackRanges[i] = wire.AckRange{First: el.Value.Start, Last: el.Value.End}
|
||||
i++
|
||||
}
|
||||
return ackRanges
|
||||
}
|
||||
|
||||
func (h *receivedPacketHistory) GetHighestAckRange() wire.AckRange {
|
||||
ackRange := wire.AckRange{}
|
||||
if h.ranges.Len() > 0 {
|
||||
r := h.ranges.Back().Value
|
||||
ackRange.First = r.Start
|
||||
ackRange.Last = r.End
|
||||
}
|
||||
return ackRange
|
||||
}
|
||||
248
internal/ackhandler/received_packet_history_test.go
Normal file
248
internal/ackhandler/received_packet_history_test.go
Normal file
@@ -0,0 +1,248 @@
|
||||
package ackhandler
|
||||
|
||||
import (
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||
"github.com/lucas-clemente/quic-go/internal/wire"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("receivedPacketHistory", func() {
|
||||
var (
|
||||
hist *receivedPacketHistory
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
hist = newReceivedPacketHistory()
|
||||
})
|
||||
|
||||
Context("ranges", func() {
|
||||
It("adds the first packet", func() {
|
||||
hist.ReceivedPacket(4)
|
||||
Expect(hist.ranges.Len()).To(Equal(1))
|
||||
Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 4, End: 4}))
|
||||
})
|
||||
|
||||
It("doesn't care about duplicate packets", func() {
|
||||
hist.ReceivedPacket(4)
|
||||
Expect(hist.ranges.Len()).To(Equal(1))
|
||||
Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 4, End: 4}))
|
||||
})
|
||||
|
||||
It("adds a few consecutive packets", func() {
|
||||
hist.ReceivedPacket(4)
|
||||
hist.ReceivedPacket(5)
|
||||
hist.ReceivedPacket(6)
|
||||
Expect(hist.ranges.Len()).To(Equal(1))
|
||||
Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 4, End: 6}))
|
||||
})
|
||||
|
||||
It("doesn't care about a duplicate packet contained in an existing range", func() {
|
||||
hist.ReceivedPacket(4)
|
||||
hist.ReceivedPacket(5)
|
||||
hist.ReceivedPacket(6)
|
||||
hist.ReceivedPacket(5)
|
||||
Expect(hist.ranges.Len()).To(Equal(1))
|
||||
Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 4, End: 6}))
|
||||
})
|
||||
|
||||
It("extends a range at the front", func() {
|
||||
hist.ReceivedPacket(4)
|
||||
hist.ReceivedPacket(3)
|
||||
Expect(hist.ranges.Len()).To(Equal(1))
|
||||
Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 3, End: 4}))
|
||||
})
|
||||
|
||||
It("creates a new range when a packet is lost", func() {
|
||||
hist.ReceivedPacket(4)
|
||||
hist.ReceivedPacket(6)
|
||||
Expect(hist.ranges.Len()).To(Equal(2))
|
||||
Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 4, End: 4}))
|
||||
Expect(hist.ranges.Back().Value).To(Equal(utils.PacketInterval{Start: 6, End: 6}))
|
||||
})
|
||||
|
||||
It("creates a new range in between two ranges", func() {
|
||||
hist.ReceivedPacket(4)
|
||||
hist.ReceivedPacket(10)
|
||||
Expect(hist.ranges.Len()).To(Equal(2))
|
||||
hist.ReceivedPacket(7)
|
||||
Expect(hist.ranges.Len()).To(Equal(3))
|
||||
Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 4, End: 4}))
|
||||
Expect(hist.ranges.Front().Next().Value).To(Equal(utils.PacketInterval{Start: 7, End: 7}))
|
||||
Expect(hist.ranges.Back().Value).To(Equal(utils.PacketInterval{Start: 10, End: 10}))
|
||||
})
|
||||
|
||||
It("creates a new range before an existing range for a belated packet", func() {
|
||||
hist.ReceivedPacket(6)
|
||||
hist.ReceivedPacket(4)
|
||||
Expect(hist.ranges.Len()).To(Equal(2))
|
||||
Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 4, End: 4}))
|
||||
Expect(hist.ranges.Back().Value).To(Equal(utils.PacketInterval{Start: 6, End: 6}))
|
||||
})
|
||||
|
||||
It("extends a previous range at the end", func() {
|
||||
hist.ReceivedPacket(4)
|
||||
hist.ReceivedPacket(7)
|
||||
hist.ReceivedPacket(5)
|
||||
Expect(hist.ranges.Len()).To(Equal(2))
|
||||
Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 4, End: 5}))
|
||||
Expect(hist.ranges.Back().Value).To(Equal(utils.PacketInterval{Start: 7, End: 7}))
|
||||
})
|
||||
|
||||
It("extends a range at the front", func() {
|
||||
hist.ReceivedPacket(4)
|
||||
hist.ReceivedPacket(7)
|
||||
hist.ReceivedPacket(6)
|
||||
Expect(hist.ranges.Len()).To(Equal(2))
|
||||
Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 4, End: 4}))
|
||||
Expect(hist.ranges.Back().Value).To(Equal(utils.PacketInterval{Start: 6, End: 7}))
|
||||
})
|
||||
|
||||
It("closes a range", func() {
|
||||
hist.ReceivedPacket(6)
|
||||
hist.ReceivedPacket(4)
|
||||
Expect(hist.ranges.Len()).To(Equal(2))
|
||||
hist.ReceivedPacket(5)
|
||||
Expect(hist.ranges.Len()).To(Equal(1))
|
||||
Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 4, End: 6}))
|
||||
})
|
||||
|
||||
It("closes a range in the middle", func() {
|
||||
hist.ReceivedPacket(1)
|
||||
hist.ReceivedPacket(10)
|
||||
hist.ReceivedPacket(4)
|
||||
hist.ReceivedPacket(6)
|
||||
Expect(hist.ranges.Len()).To(Equal(4))
|
||||
hist.ReceivedPacket(5)
|
||||
Expect(hist.ranges.Len()).To(Equal(3))
|
||||
Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 1, End: 1}))
|
||||
Expect(hist.ranges.Front().Next().Value).To(Equal(utils.PacketInterval{Start: 4, End: 6}))
|
||||
Expect(hist.ranges.Back().Value).To(Equal(utils.PacketInterval{Start: 10, End: 10}))
|
||||
})
|
||||
})
|
||||
|
||||
Context("deleting", func() {
|
||||
It("does nothing when the history is empty", func() {
|
||||
hist.DeleteBelow(5)
|
||||
Expect(hist.ranges.Len()).To(BeZero())
|
||||
})
|
||||
|
||||
It("deletes a range", func() {
|
||||
hist.ReceivedPacket(4)
|
||||
hist.ReceivedPacket(5)
|
||||
hist.ReceivedPacket(10)
|
||||
hist.DeleteBelow(6)
|
||||
Expect(hist.ranges.Len()).To(Equal(1))
|
||||
Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 10, End: 10}))
|
||||
})
|
||||
|
||||
It("deletes multiple ranges", func() {
|
||||
hist.ReceivedPacket(1)
|
||||
hist.ReceivedPacket(5)
|
||||
hist.ReceivedPacket(10)
|
||||
hist.DeleteBelow(8)
|
||||
Expect(hist.ranges.Len()).To(Equal(1))
|
||||
Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 10, End: 10}))
|
||||
})
|
||||
|
||||
It("adjusts a range, if packets are delete from an existing range", func() {
|
||||
hist.ReceivedPacket(3)
|
||||
hist.ReceivedPacket(4)
|
||||
hist.ReceivedPacket(5)
|
||||
hist.ReceivedPacket(6)
|
||||
hist.ReceivedPacket(7)
|
||||
hist.DeleteBelow(5)
|
||||
Expect(hist.ranges.Len()).To(Equal(1))
|
||||
Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 5, End: 7}))
|
||||
})
|
||||
|
||||
It("adjusts a range, if only one packet remains in the range", func() {
|
||||
hist.ReceivedPacket(4)
|
||||
hist.ReceivedPacket(5)
|
||||
hist.ReceivedPacket(10)
|
||||
hist.DeleteBelow(5)
|
||||
Expect(hist.ranges.Len()).To(Equal(2))
|
||||
Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 5, End: 5}))
|
||||
Expect(hist.ranges.Back().Value).To(Equal(utils.PacketInterval{Start: 10, End: 10}))
|
||||
})
|
||||
|
||||
It("keeps a one-packet range, if deleting up to the packet directly below", func() {
|
||||
hist.ReceivedPacket(4)
|
||||
hist.DeleteBelow(4)
|
||||
Expect(hist.ranges.Len()).To(Equal(1))
|
||||
Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 4, End: 4}))
|
||||
})
|
||||
|
||||
Context("DoS protection", func() {
|
||||
It("doesn't create more than MaxTrackedReceivedAckRanges ranges", func() {
|
||||
for i := protocol.PacketNumber(1); i <= protocol.MaxTrackedReceivedAckRanges; i++ {
|
||||
err := hist.ReceivedPacket(2 * i)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
err := hist.ReceivedPacket(2*protocol.MaxTrackedReceivedAckRanges + 2)
|
||||
Expect(err).To(MatchError(errTooManyOutstandingReceivedAckRanges))
|
||||
})
|
||||
|
||||
It("doesn't consider already deleted ranges for MaxTrackedReceivedAckRanges", func() {
|
||||
for i := protocol.PacketNumber(1); i <= protocol.MaxTrackedReceivedAckRanges; i++ {
|
||||
err := hist.ReceivedPacket(2 * i)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
err := hist.ReceivedPacket(2*protocol.MaxTrackedReceivedAckRanges + 2)
|
||||
Expect(err).To(MatchError(errTooManyOutstandingReceivedAckRanges))
|
||||
hist.DeleteBelow(protocol.MaxTrackedReceivedAckRanges) // deletes about half of the ranges
|
||||
err = hist.ReceivedPacket(2*protocol.MaxTrackedReceivedAckRanges + 4)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Context("ACK range export", func() {
|
||||
It("returns nil if there are no ranges", func() {
|
||||
Expect(hist.GetAckRanges()).To(BeNil())
|
||||
})
|
||||
|
||||
It("gets a single ACK range", func() {
|
||||
hist.ReceivedPacket(4)
|
||||
hist.ReceivedPacket(5)
|
||||
ackRanges := hist.GetAckRanges()
|
||||
Expect(ackRanges).To(HaveLen(1))
|
||||
Expect(ackRanges[0]).To(Equal(wire.AckRange{First: 4, Last: 5}))
|
||||
})
|
||||
|
||||
It("gets multiple ACK ranges", func() {
|
||||
hist.ReceivedPacket(4)
|
||||
hist.ReceivedPacket(5)
|
||||
hist.ReceivedPacket(6)
|
||||
hist.ReceivedPacket(1)
|
||||
hist.ReceivedPacket(11)
|
||||
hist.ReceivedPacket(10)
|
||||
hist.ReceivedPacket(2)
|
||||
ackRanges := hist.GetAckRanges()
|
||||
Expect(ackRanges).To(HaveLen(3))
|
||||
Expect(ackRanges[0]).To(Equal(wire.AckRange{First: 10, Last: 11}))
|
||||
Expect(ackRanges[1]).To(Equal(wire.AckRange{First: 4, Last: 6}))
|
||||
Expect(ackRanges[2]).To(Equal(wire.AckRange{First: 1, Last: 2}))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Getting the highest ACK range", func() {
|
||||
It("returns the zero value if there are no ranges", func() {
|
||||
Expect(hist.GetHighestAckRange()).To(BeZero())
|
||||
})
|
||||
|
||||
It("gets a single ACK range", func() {
|
||||
hist.ReceivedPacket(4)
|
||||
hist.ReceivedPacket(5)
|
||||
Expect(hist.GetHighestAckRange()).To(Equal(wire.AckRange{First: 4, Last: 5}))
|
||||
})
|
||||
|
||||
It("gets the highest of multiple ACK ranges", func() {
|
||||
hist.ReceivedPacket(3)
|
||||
hist.ReceivedPacket(6)
|
||||
hist.ReceivedPacket(7)
|
||||
Expect(hist.GetHighestAckRange()).To(Equal(wire.AckRange{First: 6, Last: 7}))
|
||||
})
|
||||
})
|
||||
})
|
||||
36
internal/ackhandler/retransmittable.go
Normal file
36
internal/ackhandler/retransmittable.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package ackhandler
|
||||
|
||||
import "github.com/lucas-clemente/quic-go/internal/wire"
|
||||
|
||||
// Returns a new slice with all non-retransmittable frames deleted.
|
||||
func stripNonRetransmittableFrames(fs []wire.Frame) []wire.Frame {
|
||||
res := make([]wire.Frame, 0, len(fs))
|
||||
for _, f := range fs {
|
||||
if IsFrameRetransmittable(f) {
|
||||
res = append(res, f)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// IsFrameRetransmittable returns true if the frame should be retransmitted.
|
||||
func IsFrameRetransmittable(f wire.Frame) bool {
|
||||
switch f.(type) {
|
||||
case *wire.StopWaitingFrame:
|
||||
return false
|
||||
case *wire.AckFrame:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// HasRetransmittableFrames returns true if at least one frame is retransmittable.
|
||||
func HasRetransmittableFrames(fs []wire.Frame) bool {
|
||||
for _, f := range fs {
|
||||
if IsFrameRetransmittable(f) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
45
internal/ackhandler/retransmittable_test.go
Normal file
45
internal/ackhandler/retransmittable_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package ackhandler
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/wire"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("retransmittable frames", func() {
|
||||
for fl, el := range map[wire.Frame]bool{
|
||||
&wire.AckFrame{}: false,
|
||||
&wire.StopWaitingFrame{}: false,
|
||||
&wire.BlockedFrame{}: true,
|
||||
&wire.ConnectionCloseFrame{}: true,
|
||||
&wire.GoawayFrame{}: true,
|
||||
&wire.PingFrame{}: true,
|
||||
&wire.RstStreamFrame{}: true,
|
||||
&wire.StreamFrame{}: true,
|
||||
&wire.MaxDataFrame{}: true,
|
||||
&wire.MaxStreamDataFrame{}: true,
|
||||
} {
|
||||
f := fl
|
||||
e := el
|
||||
fName := reflect.ValueOf(f).Elem().Type().Name()
|
||||
|
||||
It("works for "+fName, func() {
|
||||
Expect(IsFrameRetransmittable(f)).To(Equal(e))
|
||||
})
|
||||
|
||||
It("stripping non-retransmittable frames works for "+fName, func() {
|
||||
s := []wire.Frame{f}
|
||||
if e {
|
||||
Expect(stripNonRetransmittableFrames(s)).To(Equal([]wire.Frame{f}))
|
||||
} else {
|
||||
Expect(stripNonRetransmittableFrames(s)).To(BeEmpty())
|
||||
}
|
||||
})
|
||||
|
||||
It("HasRetransmittableFrames works for "+fName, func() {
|
||||
Expect(HasRetransmittableFrames([]wire.Frame{f})).To(Equal(e))
|
||||
})
|
||||
}
|
||||
})
|
||||
477
internal/ackhandler/sent_packet_handler.go
Normal file
477
internal/ackhandler/sent_packet_handler.go
Normal file
@@ -0,0 +1,477 @@
|
||||
package ackhandler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/congestion"
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||
"github.com/lucas-clemente/quic-go/internal/wire"
|
||||
"github.com/lucas-clemente/quic-go/qerr"
|
||||
)
|
||||
|
||||
const (
|
||||
// Maximum reordering in time space before time based loss detection considers a packet lost.
|
||||
// In fraction of an RTT.
|
||||
timeReorderingFraction = 1.0 / 8
|
||||
// The default RTT used before an RTT sample is taken.
|
||||
// Note: This constant is also defined in the congestion package.
|
||||
defaultInitialRTT = 100 * time.Millisecond
|
||||
// defaultRTOTimeout is the RTO time on new connections
|
||||
defaultRTOTimeout = 500 * time.Millisecond
|
||||
// Minimum time in the future a tail loss probe alarm may be set for.
|
||||
minTPLTimeout = 10 * time.Millisecond
|
||||
// Minimum time in the future an RTO alarm may be set for.
|
||||
minRTOTimeout = 200 * time.Millisecond
|
||||
// maxRTOTimeout is the maximum RTO time
|
||||
maxRTOTimeout = 60 * time.Second
|
||||
)
|
||||
|
||||
// ErrDuplicateOrOutOfOrderAck occurs when a duplicate or an out-of-order ACK is received
|
||||
var ErrDuplicateOrOutOfOrderAck = errors.New("SentPacketHandler: Duplicate or out-of-order ACK")
|
||||
|
||||
type sentPacketHandler struct {
|
||||
lastSentPacketNumber protocol.PacketNumber
|
||||
nextPacketSendTime time.Time
|
||||
skippedPackets []protocol.PacketNumber
|
||||
|
||||
largestAcked protocol.PacketNumber
|
||||
largestReceivedPacketWithAck protocol.PacketNumber
|
||||
// lowestPacketNotConfirmedAcked is the lowest packet number that we sent an ACK for, but haven't received confirmation, that this ACK actually arrived
|
||||
// example: we send an ACK for packets 90-100 with packet number 20
|
||||
// once we receive an ACK from the peer for packet 20, the lowestPacketNotConfirmedAcked is 101
|
||||
lowestPacketNotConfirmedAcked protocol.PacketNumber
|
||||
|
||||
packetHistory *PacketList
|
||||
stopWaitingManager stopWaitingManager
|
||||
|
||||
retransmissionQueue []*Packet
|
||||
|
||||
bytesInFlight protocol.ByteCount
|
||||
|
||||
congestion congestion.SendAlgorithm
|
||||
rttStats *congestion.RTTStats
|
||||
|
||||
handshakeComplete bool
|
||||
// The number of times the handshake packets have been retransmitted without receiving an ack.
|
||||
handshakeCount uint32
|
||||
|
||||
// The number of times an RTO has been sent without receiving an ack.
|
||||
rtoCount uint32
|
||||
|
||||
// The time at which the next packet will be considered lost based on early transmit or exceeding the reordering window in time.
|
||||
lossTime time.Time
|
||||
|
||||
// The alarm timeout
|
||||
alarm time.Time
|
||||
}
|
||||
|
||||
// NewSentPacketHandler creates a new sentPacketHandler
|
||||
func NewSentPacketHandler(rttStats *congestion.RTTStats) SentPacketHandler {
|
||||
congestion := congestion.NewCubicSender(
|
||||
congestion.DefaultClock{},
|
||||
rttStats,
|
||||
false, /* don't use reno since chromium doesn't (why?) */
|
||||
protocol.InitialCongestionWindow,
|
||||
protocol.DefaultMaxCongestionWindow,
|
||||
)
|
||||
|
||||
return &sentPacketHandler{
|
||||
packetHistory: NewPacketList(),
|
||||
stopWaitingManager: stopWaitingManager{},
|
||||
rttStats: rttStats,
|
||||
congestion: congestion,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) lowestUnacked() protocol.PacketNumber {
|
||||
if f := h.packetHistory.Front(); f != nil {
|
||||
return f.Value.PacketNumber
|
||||
}
|
||||
return h.largestAcked + 1
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) SetHandshakeComplete() {
|
||||
var queue []*Packet
|
||||
for _, packet := range h.retransmissionQueue {
|
||||
if packet.EncryptionLevel == protocol.EncryptionForwardSecure {
|
||||
queue = append(queue, packet)
|
||||
}
|
||||
}
|
||||
h.retransmissionQueue = queue
|
||||
h.handshakeComplete = true
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) SentPacket(packet *Packet) error {
|
||||
if protocol.PacketNumber(len(h.retransmissionQueue)+h.packetHistory.Len()+1) > protocol.MaxTrackedSentPackets {
|
||||
return errors.New("Too many outstanding non-acked and non-retransmitted packets")
|
||||
}
|
||||
|
||||
for p := h.lastSentPacketNumber + 1; p < packet.PacketNumber; p++ {
|
||||
h.skippedPackets = append(h.skippedPackets, p)
|
||||
|
||||
if len(h.skippedPackets) > protocol.MaxTrackedSkippedPackets {
|
||||
h.skippedPackets = h.skippedPackets[1:]
|
||||
}
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
h.lastSentPacketNumber = packet.PacketNumber
|
||||
|
||||
var largestAcked protocol.PacketNumber
|
||||
if len(packet.Frames) > 0 {
|
||||
if ackFrame, ok := packet.Frames[0].(*wire.AckFrame); ok {
|
||||
largestAcked = ackFrame.LargestAcked
|
||||
}
|
||||
}
|
||||
|
||||
packet.Frames = stripNonRetransmittableFrames(packet.Frames)
|
||||
isRetransmittable := len(packet.Frames) != 0
|
||||
|
||||
if isRetransmittable {
|
||||
packet.sendTime = now
|
||||
packet.largestAcked = largestAcked
|
||||
h.bytesInFlight += packet.Length
|
||||
h.packetHistory.PushBack(*packet)
|
||||
}
|
||||
|
||||
h.congestion.OnPacketSent(
|
||||
now,
|
||||
h.bytesInFlight,
|
||||
packet.PacketNumber,
|
||||
packet.Length,
|
||||
isRetransmittable,
|
||||
)
|
||||
|
||||
h.nextPacketSendTime = utils.MaxTime(h.nextPacketSendTime, now).Add(h.congestion.TimeUntilSend(h.bytesInFlight))
|
||||
|
||||
h.updateLossDetectionAlarm(now)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) ReceivedAck(ackFrame *wire.AckFrame, withPacketNumber protocol.PacketNumber, encLevel protocol.EncryptionLevel, rcvTime time.Time) error {
|
||||
if ackFrame.LargestAcked > h.lastSentPacketNumber {
|
||||
return qerr.Error(qerr.InvalidAckData, "Received ACK for an unsent package")
|
||||
}
|
||||
|
||||
// duplicate or out-of-order ACK
|
||||
// if withPacketNumber <= h.largestReceivedPacketWithAck && withPacketNumber != 0 {
|
||||
if withPacketNumber <= h.largestReceivedPacketWithAck {
|
||||
return ErrDuplicateOrOutOfOrderAck
|
||||
}
|
||||
h.largestReceivedPacketWithAck = withPacketNumber
|
||||
|
||||
// ignore repeated ACK (ACKs that don't have a higher LargestAcked than the last ACK)
|
||||
if ackFrame.LargestAcked < h.lowestUnacked() {
|
||||
return nil
|
||||
}
|
||||
h.largestAcked = ackFrame.LargestAcked
|
||||
|
||||
if h.skippedPacketsAcked(ackFrame) {
|
||||
return qerr.Error(qerr.InvalidAckData, "Received an ACK for a skipped packet number")
|
||||
}
|
||||
|
||||
rttUpdated := h.maybeUpdateRTT(ackFrame.LargestAcked, ackFrame.DelayTime, rcvTime)
|
||||
|
||||
if rttUpdated {
|
||||
h.congestion.MaybeExitSlowStart()
|
||||
}
|
||||
|
||||
ackedPackets, err := h.determineNewlyAckedPackets(ackFrame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(ackedPackets) > 0 {
|
||||
for _, p := range ackedPackets {
|
||||
if encLevel < p.Value.EncryptionLevel {
|
||||
return fmt.Errorf("Received ACK with encryption level %s that acks a packet %d (encryption level %s)", encLevel, p.Value.PacketNumber, p.Value.EncryptionLevel)
|
||||
}
|
||||
// largestAcked == 0 either means that the packet didn't contain an ACK, or it just acked packet 0
|
||||
// It is safe to ignore the corner case of packets that just acked packet 0, because
|
||||
// the lowestPacketNotConfirmedAcked is only used to limit the number of ACK ranges we will send.
|
||||
if p.Value.largestAcked != 0 {
|
||||
h.lowestPacketNotConfirmedAcked = utils.MaxPacketNumber(h.lowestPacketNotConfirmedAcked, p.Value.largestAcked+1)
|
||||
}
|
||||
h.onPacketAcked(p)
|
||||
h.congestion.OnPacketAcked(p.Value.PacketNumber, p.Value.Length, h.bytesInFlight)
|
||||
}
|
||||
}
|
||||
|
||||
h.detectLostPackets(rcvTime)
|
||||
h.updateLossDetectionAlarm(rcvTime)
|
||||
|
||||
h.garbageCollectSkippedPackets()
|
||||
h.stopWaitingManager.ReceivedAck(ackFrame)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) GetLowestPacketNotConfirmedAcked() protocol.PacketNumber {
|
||||
return h.lowestPacketNotConfirmedAcked
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) determineNewlyAckedPackets(ackFrame *wire.AckFrame) ([]*PacketElement, error) {
|
||||
var ackedPackets []*PacketElement
|
||||
ackRangeIndex := 0
|
||||
for el := h.packetHistory.Front(); el != nil; el = el.Next() {
|
||||
packet := el.Value
|
||||
packetNumber := packet.PacketNumber
|
||||
|
||||
// Ignore packets below the LowestAcked
|
||||
if packetNumber < ackFrame.LowestAcked {
|
||||
continue
|
||||
}
|
||||
// Break after LargestAcked is reached
|
||||
if packetNumber > ackFrame.LargestAcked {
|
||||
break
|
||||
}
|
||||
|
||||
if ackFrame.HasMissingRanges() {
|
||||
ackRange := ackFrame.AckRanges[len(ackFrame.AckRanges)-1-ackRangeIndex]
|
||||
|
||||
for packetNumber > ackRange.Last && ackRangeIndex < len(ackFrame.AckRanges)-1 {
|
||||
ackRangeIndex++
|
||||
ackRange = ackFrame.AckRanges[len(ackFrame.AckRanges)-1-ackRangeIndex]
|
||||
}
|
||||
|
||||
if packetNumber >= ackRange.First { // packet i contained in ACK range
|
||||
if packetNumber > ackRange.Last {
|
||||
return nil, fmt.Errorf("BUG: ackhandler would have acked wrong packet 0x%x, while evaluating range 0x%x -> 0x%x", packetNumber, ackRange.First, ackRange.Last)
|
||||
}
|
||||
ackedPackets = append(ackedPackets, el)
|
||||
}
|
||||
} else {
|
||||
ackedPackets = append(ackedPackets, el)
|
||||
}
|
||||
}
|
||||
return ackedPackets, nil
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) maybeUpdateRTT(largestAcked protocol.PacketNumber, ackDelay time.Duration, rcvTime time.Time) bool {
|
||||
for el := h.packetHistory.Front(); el != nil; el = el.Next() {
|
||||
packet := el.Value
|
||||
if packet.PacketNumber == largestAcked {
|
||||
h.rttStats.UpdateRTT(rcvTime.Sub(packet.sendTime), ackDelay, rcvTime)
|
||||
return true
|
||||
}
|
||||
// Packets are sorted by number, so we can stop searching
|
||||
if packet.PacketNumber > largestAcked {
|
||||
break
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) updateLossDetectionAlarm(now time.Time) {
|
||||
// Cancel the alarm if no packets are outstanding
|
||||
if h.packetHistory.Len() == 0 {
|
||||
h.alarm = time.Time{}
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(#497): TLP
|
||||
if !h.handshakeComplete {
|
||||
h.alarm = now.Add(h.computeHandshakeTimeout())
|
||||
} else if !h.lossTime.IsZero() {
|
||||
// Early retransmit timer or time loss detection.
|
||||
h.alarm = h.lossTime
|
||||
} else {
|
||||
// RTO
|
||||
h.alarm = now.Add(h.computeRTOTimeout())
|
||||
}
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) detectLostPackets(now time.Time) {
|
||||
h.lossTime = time.Time{}
|
||||
|
||||
maxRTT := float64(utils.MaxDuration(h.rttStats.LatestRTT(), h.rttStats.SmoothedRTT()))
|
||||
delayUntilLost := time.Duration((1.0 + timeReorderingFraction) * maxRTT)
|
||||
|
||||
var lostPackets []*PacketElement
|
||||
for el := h.packetHistory.Front(); el != nil; el = el.Next() {
|
||||
packet := el.Value
|
||||
|
||||
if packet.PacketNumber > h.largestAcked {
|
||||
break
|
||||
}
|
||||
|
||||
timeSinceSent := now.Sub(packet.sendTime)
|
||||
if timeSinceSent > delayUntilLost {
|
||||
lostPackets = append(lostPackets, el)
|
||||
} else if h.lossTime.IsZero() {
|
||||
// Note: This conditional is only entered once per call
|
||||
h.lossTime = now.Add(delayUntilLost - timeSinceSent)
|
||||
}
|
||||
}
|
||||
|
||||
if len(lostPackets) > 0 {
|
||||
for _, p := range lostPackets {
|
||||
h.queuePacketForRetransmission(p)
|
||||
h.congestion.OnPacketLost(p.Value.PacketNumber, p.Value.Length, h.bytesInFlight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) OnAlarm() {
|
||||
now := time.Now()
|
||||
|
||||
// TODO(#497): TLP
|
||||
if !h.handshakeComplete {
|
||||
h.queueHandshakePacketsForRetransmission()
|
||||
h.handshakeCount++
|
||||
} else if !h.lossTime.IsZero() {
|
||||
// Early retransmit or time loss detection
|
||||
h.detectLostPackets(now)
|
||||
} else {
|
||||
// RTO
|
||||
h.retransmitOldestTwoPackets()
|
||||
h.rtoCount++
|
||||
}
|
||||
|
||||
h.updateLossDetectionAlarm(now)
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) GetAlarmTimeout() time.Time {
|
||||
return h.alarm
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) onPacketAcked(packetElement *PacketElement) {
|
||||
h.bytesInFlight -= packetElement.Value.Length
|
||||
h.rtoCount = 0
|
||||
h.handshakeCount = 0
|
||||
// TODO(#497): h.tlpCount = 0
|
||||
h.packetHistory.Remove(packetElement)
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) DequeuePacketForRetransmission() *Packet {
|
||||
if len(h.retransmissionQueue) == 0 {
|
||||
return nil
|
||||
}
|
||||
packet := h.retransmissionQueue[0]
|
||||
// Shift the slice and don't retain anything that isn't needed.
|
||||
copy(h.retransmissionQueue, h.retransmissionQueue[1:])
|
||||
h.retransmissionQueue[len(h.retransmissionQueue)-1] = nil
|
||||
h.retransmissionQueue = h.retransmissionQueue[:len(h.retransmissionQueue)-1]
|
||||
return packet
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) GetLeastUnacked() protocol.PacketNumber {
|
||||
return h.lowestUnacked()
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) GetStopWaitingFrame(force bool) *wire.StopWaitingFrame {
|
||||
return h.stopWaitingManager.GetStopWaitingFrame(force)
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) SendingAllowed() bool {
|
||||
cwnd := h.congestion.GetCongestionWindow()
|
||||
congestionLimited := h.bytesInFlight > cwnd
|
||||
maxTrackedLimited := protocol.PacketNumber(len(h.retransmissionQueue)+h.packetHistory.Len()) >= protocol.MaxTrackedSentPackets
|
||||
if congestionLimited {
|
||||
utils.Debugf("Congestion limited: bytes in flight %d, window %d", h.bytesInFlight, cwnd)
|
||||
}
|
||||
// Workaround for #555:
|
||||
// Always allow sending of retransmissions. This should probably be limited
|
||||
// to RTOs, but we currently don't have a nice way of distinguishing them.
|
||||
haveRetransmissions := len(h.retransmissionQueue) > 0
|
||||
return !maxTrackedLimited && (!congestionLimited || haveRetransmissions)
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) TimeUntilSend() time.Time {
|
||||
return h.nextPacketSendTime
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) ShouldSendNumPackets() int {
|
||||
delay := h.congestion.TimeUntilSend(h.bytesInFlight)
|
||||
if delay == 0 || delay > protocol.MinPacingDelay {
|
||||
return 1
|
||||
}
|
||||
return int(math.Ceil(float64(protocol.MinPacingDelay) / float64(delay)))
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) retransmitOldestTwoPackets() {
|
||||
if p := h.packetHistory.Front(); p != nil {
|
||||
h.queueRTO(p)
|
||||
}
|
||||
if p := h.packetHistory.Front(); p != nil {
|
||||
h.queueRTO(p)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) queueRTO(el *PacketElement) {
|
||||
packet := &el.Value
|
||||
utils.Debugf(
|
||||
"\tQueueing packet 0x%x for retransmission (RTO), %d outstanding",
|
||||
packet.PacketNumber,
|
||||
h.packetHistory.Len(),
|
||||
)
|
||||
h.queuePacketForRetransmission(el)
|
||||
h.congestion.OnPacketLost(packet.PacketNumber, packet.Length, h.bytesInFlight)
|
||||
h.congestion.OnRetransmissionTimeout(true)
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) queueHandshakePacketsForRetransmission() {
|
||||
var handshakePackets []*PacketElement
|
||||
for el := h.packetHistory.Front(); el != nil; el = el.Next() {
|
||||
if el.Value.EncryptionLevel < protocol.EncryptionForwardSecure {
|
||||
handshakePackets = append(handshakePackets, el)
|
||||
}
|
||||
}
|
||||
for _, el := range handshakePackets {
|
||||
h.queuePacketForRetransmission(el)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) queuePacketForRetransmission(packetElement *PacketElement) {
|
||||
packet := &packetElement.Value
|
||||
h.bytesInFlight -= packet.Length
|
||||
h.retransmissionQueue = append(h.retransmissionQueue, packet)
|
||||
h.packetHistory.Remove(packetElement)
|
||||
h.stopWaitingManager.QueuedRetransmissionForPacketNumber(packet.PacketNumber)
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) computeHandshakeTimeout() time.Duration {
|
||||
duration := 2 * h.rttStats.SmoothedRTT()
|
||||
if duration == 0 {
|
||||
duration = 2 * defaultInitialRTT
|
||||
}
|
||||
duration = utils.MaxDuration(duration, minTPLTimeout)
|
||||
// exponential backoff
|
||||
// There's an implicit limit to this set by the handshake timeout.
|
||||
return duration << h.handshakeCount
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) computeRTOTimeout() time.Duration {
|
||||
rto := h.congestion.RetransmissionDelay()
|
||||
if rto == 0 {
|
||||
rto = defaultRTOTimeout
|
||||
}
|
||||
rto = utils.MaxDuration(rto, minRTOTimeout)
|
||||
// Exponential backoff
|
||||
rto = rto << h.rtoCount
|
||||
return utils.MinDuration(rto, maxRTOTimeout)
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) skippedPacketsAcked(ackFrame *wire.AckFrame) bool {
|
||||
for _, p := range h.skippedPackets {
|
||||
if ackFrame.AcksPacket(p) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) garbageCollectSkippedPackets() {
|
||||
lowestUnacked := h.lowestUnacked()
|
||||
deleteIndex := 0
|
||||
for i, p := range h.skippedPackets {
|
||||
if p < lowestUnacked {
|
||||
deleteIndex = i + 1
|
||||
}
|
||||
}
|
||||
h.skippedPackets = h.skippedPackets[deleteIndex:]
|
||||
}
|
||||
910
internal/ackhandler/sent_packet_handler_test.go
Normal file
910
internal/ackhandler/sent_packet_handler_test.go
Normal file
@@ -0,0 +1,910 @@
|
||||
package ackhandler
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/lucas-clemente/quic-go/internal/congestion"
|
||||
"github.com/lucas-clemente/quic-go/internal/mocks"
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/wire"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func retransmittablePacket(num protocol.PacketNumber) *Packet {
|
||||
return &Packet{
|
||||
PacketNumber: num,
|
||||
Length: 1,
|
||||
Frames: []wire.Frame{&wire.PingFrame{}},
|
||||
EncryptionLevel: protocol.EncryptionForwardSecure,
|
||||
}
|
||||
}
|
||||
|
||||
func nonRetransmittablePacket(num protocol.PacketNumber) *Packet {
|
||||
return &Packet{PacketNumber: num, Length: 1, Frames: []wire.Frame{&wire.AckFrame{}}}
|
||||
}
|
||||
|
||||
func handshakePacket(num protocol.PacketNumber) *Packet {
|
||||
return &Packet{
|
||||
PacketNumber: num,
|
||||
Length: 1,
|
||||
Frames: []wire.Frame{&wire.PingFrame{}},
|
||||
EncryptionLevel: protocol.EncryptionUnencrypted,
|
||||
}
|
||||
}
|
||||
|
||||
var _ = Describe("SentPacketHandler", func() {
|
||||
var (
|
||||
handler *sentPacketHandler
|
||||
streamFrame wire.StreamFrame
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
rttStats := &congestion.RTTStats{}
|
||||
handler = NewSentPacketHandler(rttStats).(*sentPacketHandler)
|
||||
handler.SetHandshakeComplete()
|
||||
streamFrame = wire.StreamFrame{
|
||||
StreamID: 5,
|
||||
Data: []byte{0x13, 0x37},
|
||||
}
|
||||
})
|
||||
|
||||
getPacketElement := func(p protocol.PacketNumber) *PacketElement {
|
||||
for el := handler.packetHistory.Front(); el != nil; el = el.Next() {
|
||||
if el.Value.PacketNumber == p {
|
||||
return el
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
It("gets the LeastUnacked packet number", func() {
|
||||
handler.largestAcked = 0x1337
|
||||
Expect(handler.GetLeastUnacked()).To(Equal(protocol.PacketNumber(0x1337 + 1)))
|
||||
})
|
||||
|
||||
Context("registering sent packets", func() {
|
||||
It("accepts two consecutive packets", func() {
|
||||
packet1 := Packet{PacketNumber: 1, Frames: []wire.Frame{&streamFrame}, Length: 1}
|
||||
packet2 := Packet{PacketNumber: 2, Frames: []wire.Frame{&streamFrame}, Length: 2}
|
||||
err := handler.SentPacket(&packet1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = handler.SentPacket(&packet2)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.lastSentPacketNumber).To(Equal(protocol.PacketNumber(2)))
|
||||
Expect(handler.packetHistory.Front().Value.PacketNumber).To(Equal(protocol.PacketNumber(1)))
|
||||
Expect(handler.packetHistory.Back().Value.PacketNumber).To(Equal(protocol.PacketNumber(2)))
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(3)))
|
||||
Expect(handler.skippedPackets).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("accepts packet number 0", func() {
|
||||
packet1 := Packet{PacketNumber: 0, Frames: []wire.Frame{&streamFrame}, Length: 1}
|
||||
packet2 := Packet{PacketNumber: 1, Frames: []wire.Frame{&streamFrame}, Length: 2}
|
||||
err := handler.SentPacket(&packet1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.lastSentPacketNumber).To(BeZero())
|
||||
err = handler.SentPacket(&packet2)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.lastSentPacketNumber).To(Equal(protocol.PacketNumber(1)))
|
||||
Expect(handler.packetHistory.Front().Value.PacketNumber).To(Equal(protocol.PacketNumber(0)))
|
||||
Expect(handler.packetHistory.Back().Value.PacketNumber).To(Equal(protocol.PacketNumber(1)))
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(3)))
|
||||
Expect(handler.skippedPackets).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("stores the sent time", func() {
|
||||
packet := Packet{PacketNumber: 1, Frames: []wire.Frame{&streamFrame}, Length: 1}
|
||||
err := handler.SentPacket(&packet)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.packetHistory.Front().Value.sendTime.Unix()).To(BeNumerically("~", time.Now().Unix(), 1))
|
||||
})
|
||||
|
||||
It("does not store non-retransmittable packets", func() {
|
||||
err := handler.SentPacket(&Packet{PacketNumber: 1, Length: 1})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.packetHistory.Len()).To(BeZero())
|
||||
})
|
||||
|
||||
Context("skipped packet numbers", func() {
|
||||
It("works with non-consecutive packet numbers", func() {
|
||||
packet1 := Packet{PacketNumber: 1, Frames: []wire.Frame{&streamFrame}, Length: 1}
|
||||
packet2 := Packet{PacketNumber: 3, Frames: []wire.Frame{&streamFrame}, Length: 2}
|
||||
err := handler.SentPacket(&packet1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = handler.SentPacket(&packet2)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.lastSentPacketNumber).To(Equal(protocol.PacketNumber(3)))
|
||||
el := handler.packetHistory.Front()
|
||||
Expect(el.Value.PacketNumber).To(Equal(protocol.PacketNumber(1)))
|
||||
el = el.Next()
|
||||
Expect(el.Value.PacketNumber).To(Equal(protocol.PacketNumber(3)))
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(3)))
|
||||
Expect(handler.skippedPackets).To(HaveLen(1))
|
||||
Expect(handler.skippedPackets[0]).To(Equal(protocol.PacketNumber(2)))
|
||||
})
|
||||
|
||||
It("recognizes multiple skipped packets", func() {
|
||||
packet1 := Packet{PacketNumber: 1, Frames: []wire.Frame{&streamFrame}, Length: 1}
|
||||
packet2 := Packet{PacketNumber: 3, Frames: []wire.Frame{&streamFrame}, Length: 2}
|
||||
packet3 := Packet{PacketNumber: 5, Frames: []wire.Frame{&streamFrame}, Length: 2}
|
||||
err := handler.SentPacket(&packet1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = handler.SentPacket(&packet2)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = handler.SentPacket(&packet3)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.skippedPackets).To(HaveLen(2))
|
||||
Expect(handler.skippedPackets).To(Equal([]protocol.PacketNumber{2, 4}))
|
||||
})
|
||||
|
||||
It("recognizes multiple consecutive skipped packets", func() {
|
||||
packet1 := Packet{PacketNumber: 1, Frames: []wire.Frame{&streamFrame}, Length: 1}
|
||||
packet2 := Packet{PacketNumber: 4, Frames: []wire.Frame{&streamFrame}, Length: 2}
|
||||
err := handler.SentPacket(&packet1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = handler.SentPacket(&packet2)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.skippedPackets).To(HaveLen(2))
|
||||
Expect(handler.skippedPackets).To(Equal([]protocol.PacketNumber{2, 3}))
|
||||
})
|
||||
|
||||
It("limits the lengths of the skipped packet slice", func() {
|
||||
for i := 0; i < protocol.MaxTrackedSkippedPackets+5; i++ {
|
||||
packet := Packet{PacketNumber: protocol.PacketNumber(2*i + 1), Frames: []wire.Frame{&streamFrame}, Length: 1}
|
||||
err := handler.SentPacket(&packet)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
Expect(handler.skippedPackets).To(HaveLen(protocol.MaxUndecryptablePackets))
|
||||
Expect(handler.skippedPackets[0]).To(Equal(protocol.PacketNumber(10)))
|
||||
Expect(handler.skippedPackets[protocol.MaxTrackedSkippedPackets-1]).To(Equal(protocol.PacketNumber(10 + 2*(protocol.MaxTrackedSkippedPackets-1))))
|
||||
})
|
||||
|
||||
Context("garbage collection", func() {
|
||||
It("keeps all packet numbers above the LargestAcked", func() {
|
||||
handler.skippedPackets = []protocol.PacketNumber{2, 5, 8, 10}
|
||||
handler.largestAcked = 1
|
||||
handler.garbageCollectSkippedPackets()
|
||||
Expect(handler.skippedPackets).To(Equal([]protocol.PacketNumber{2, 5, 8, 10}))
|
||||
})
|
||||
|
||||
It("doesn't keep packet numbers below the LargestAcked", func() {
|
||||
handler.skippedPackets = []protocol.PacketNumber{1, 5, 8, 10}
|
||||
handler.largestAcked = 5
|
||||
handler.garbageCollectSkippedPackets()
|
||||
Expect(handler.skippedPackets).To(Equal([]protocol.PacketNumber{8, 10}))
|
||||
})
|
||||
|
||||
It("deletes all packet numbers if LargestAcked is sufficiently high", func() {
|
||||
handler.skippedPackets = []protocol.PacketNumber{1, 5, 10}
|
||||
handler.largestAcked = 15
|
||||
handler.garbageCollectSkippedPackets()
|
||||
Expect(handler.skippedPackets).To(BeEmpty())
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Context("DoS mitigation", func() {
|
||||
It("checks the size of the packet history, for unacked packets", func() {
|
||||
i := protocol.PacketNumber(1)
|
||||
for ; i <= protocol.MaxTrackedSentPackets; i++ {
|
||||
err := handler.SentPacket(retransmittablePacket(i))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
err := handler.SentPacket(retransmittablePacket(i))
|
||||
Expect(err).To(MatchError("Too many outstanding non-acked and non-retransmitted packets"))
|
||||
})
|
||||
|
||||
// TODO: add a test that the length of the retransmission queue is considered, even if packets have already been ACKed. Relevant once we drop support for QUIC 33 and earlier
|
||||
})
|
||||
|
||||
Context("ACK processing", func() {
|
||||
var packets []*Packet
|
||||
|
||||
BeforeEach(func() {
|
||||
packets = []*Packet{
|
||||
{PacketNumber: 0, Frames: []wire.Frame{&streamFrame}, Length: 1},
|
||||
{PacketNumber: 1, Frames: []wire.Frame{&streamFrame}, Length: 1},
|
||||
{PacketNumber: 2, Frames: []wire.Frame{&streamFrame}, Length: 1},
|
||||
{PacketNumber: 3, Frames: []wire.Frame{&streamFrame}, Length: 1},
|
||||
{PacketNumber: 4, Frames: []wire.Frame{&streamFrame}, Length: 1},
|
||||
{PacketNumber: 5, Frames: []wire.Frame{&streamFrame}, Length: 1},
|
||||
{PacketNumber: 6, Frames: []wire.Frame{&streamFrame}, Length: 1},
|
||||
{PacketNumber: 7, Frames: []wire.Frame{&streamFrame}, Length: 1},
|
||||
{PacketNumber: 8, Frames: []wire.Frame{&streamFrame}, Length: 1},
|
||||
{PacketNumber: 9, Frames: []wire.Frame{&streamFrame}, Length: 1},
|
||||
{PacketNumber: 10, Frames: []wire.Frame{&streamFrame}, Length: 1},
|
||||
{PacketNumber: 12, Frames: []wire.Frame{&streamFrame}, Length: 1},
|
||||
}
|
||||
for _, packet := range packets {
|
||||
err := handler.SentPacket(packet)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
// Increase RTT, because the tests would be flaky otherwise
|
||||
handler.rttStats.UpdateRTT(time.Hour, 0, time.Now())
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(len(packets))))
|
||||
})
|
||||
|
||||
expectInPacketHistory := func(expected []protocol.PacketNumber) {
|
||||
var packets []protocol.PacketNumber
|
||||
for el := handler.packetHistory.Front(); el != nil; el = el.Next() {
|
||||
packets = append(packets, el.Value.PacketNumber)
|
||||
}
|
||||
ExpectWithOffset(1, packets).To(Equal(expected))
|
||||
}
|
||||
|
||||
Context("ACK validation", func() {
|
||||
It("rejects duplicate ACKs", func() {
|
||||
largestAcked := 3
|
||||
ack := wire.AckFrame{
|
||||
LargestAcked: protocol.PacketNumber(largestAcked),
|
||||
LowestAcked: 1,
|
||||
}
|
||||
err := handler.ReceivedAck(&ack, 1337, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(len(packets) - 3)))
|
||||
err = handler.ReceivedAck(&ack, 1337, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).To(MatchError(ErrDuplicateOrOutOfOrderAck))
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(len(packets) - 3)))
|
||||
})
|
||||
|
||||
It("rejects out of order ACKs", func() {
|
||||
// acks packets 0, 1, 2, 3
|
||||
ack := wire.AckFrame{LargestAcked: 3}
|
||||
err := handler.ReceivedAck(&ack, 1337, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(len(packets) - 4)))
|
||||
err = handler.ReceivedAck(&ack, 1337-1, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).To(MatchError(ErrDuplicateOrOutOfOrderAck))
|
||||
Expect(handler.largestAcked).To(Equal(protocol.PacketNumber(3)))
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(len(packets) - 4)))
|
||||
})
|
||||
|
||||
It("rejects ACKs with a too high LargestAcked packet number", func() {
|
||||
ack := wire.AckFrame{
|
||||
LargestAcked: packets[len(packets)-1].PacketNumber + 1337,
|
||||
}
|
||||
err := handler.ReceivedAck(&ack, 1, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).To(MatchError("InvalidAckData: Received ACK for an unsent package"))
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(len(packets))))
|
||||
})
|
||||
|
||||
It("ignores repeated ACKs", func() {
|
||||
ack := wire.AckFrame{
|
||||
LargestAcked: 3,
|
||||
LowestAcked: 1,
|
||||
}
|
||||
err := handler.ReceivedAck(&ack, 1337, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(len(packets) - 3)))
|
||||
err = handler.ReceivedAck(&ack, 1337+1, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.largestAcked).To(Equal(protocol.PacketNumber(3)))
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(len(packets) - 3)))
|
||||
})
|
||||
|
||||
It("rejects ACKs for skipped packets", func() {
|
||||
ack := wire.AckFrame{
|
||||
LargestAcked: 12,
|
||||
LowestAcked: 5,
|
||||
}
|
||||
err := handler.ReceivedAck(&ack, 1337, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).To(MatchError("InvalidAckData: Received an ACK for a skipped packet number"))
|
||||
})
|
||||
|
||||
It("accepts an ACK that correctly nacks a skipped packet", func() {
|
||||
ack := wire.AckFrame{
|
||||
LargestAcked: 12,
|
||||
LowestAcked: 5,
|
||||
AckRanges: []wire.AckRange{
|
||||
{First: 12, Last: 12},
|
||||
{First: 5, Last: 10},
|
||||
},
|
||||
}
|
||||
err := handler.ReceivedAck(&ack, 1337, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.largestAcked).ToNot(BeZero())
|
||||
})
|
||||
})
|
||||
|
||||
Context("acks and nacks the right packets", func() {
|
||||
It("adjusts the LargestAcked", func() {
|
||||
ack := wire.AckFrame{
|
||||
LargestAcked: 5,
|
||||
LowestAcked: 0,
|
||||
}
|
||||
err := handler.ReceivedAck(&ack, 1, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.largestAcked).To(Equal(protocol.PacketNumber(5)))
|
||||
el := handler.packetHistory.Front()
|
||||
for i := 6; i <= 10; i++ {
|
||||
Expect(el.Value.PacketNumber).To(Equal(protocol.PacketNumber(i)))
|
||||
el = el.Next()
|
||||
}
|
||||
Expect(el.Value.PacketNumber).To(Equal(protocol.PacketNumber(12)))
|
||||
})
|
||||
|
||||
It("rejects an ACK that acks packets with a higher encryption level", func() {
|
||||
err := handler.SentPacket(&Packet{
|
||||
PacketNumber: 13,
|
||||
EncryptionLevel: protocol.EncryptionForwardSecure,
|
||||
Frames: []wire.Frame{&streamFrame},
|
||||
Length: 1,
|
||||
})
|
||||
ack := wire.AckFrame{
|
||||
LargestAcked: 13,
|
||||
LowestAcked: 13,
|
||||
}
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = handler.ReceivedAck(&ack, 1, protocol.EncryptionSecure, time.Now())
|
||||
Expect(err).To(MatchError("Received ACK with encryption level encrypted (not forward-secure) that acks a packet 13 (encryption level forward-secure)"))
|
||||
})
|
||||
|
||||
It("acks all packets for an ACK frame with no missing packets", func() {
|
||||
ack := wire.AckFrame{
|
||||
LargestAcked: 8,
|
||||
LowestAcked: 1,
|
||||
}
|
||||
err := handler.ReceivedAck(&ack, 1, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
el := handler.packetHistory.Front()
|
||||
Expect(el.Value.PacketNumber).To(Equal(protocol.PacketNumber(0)))
|
||||
el = el.Next()
|
||||
Expect(el.Value.PacketNumber).To(Equal(protocol.PacketNumber(9)))
|
||||
el = el.Next()
|
||||
Expect(el.Value.PacketNumber).To(Equal(protocol.PacketNumber(10)))
|
||||
Expect(el.Next().Value.PacketNumber).To(Equal(protocol.PacketNumber(12)))
|
||||
})
|
||||
|
||||
It("acks packet 0", func() {
|
||||
ack := wire.AckFrame{
|
||||
LargestAcked: 0,
|
||||
LowestAcked: 0,
|
||||
}
|
||||
err := handler.ReceivedAck(&ack, 1, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
expectInPacketHistory([]protocol.PacketNumber{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12})
|
||||
})
|
||||
|
||||
It("handles an ACK frame with one missing packet range", func() {
|
||||
ack := wire.AckFrame{
|
||||
LargestAcked: 9,
|
||||
LowestAcked: 1,
|
||||
AckRanges: []wire.AckRange{ // packets 4 and 5 were lost
|
||||
{First: 6, Last: 9},
|
||||
{First: 1, Last: 3},
|
||||
},
|
||||
}
|
||||
err := handler.ReceivedAck(&ack, 1, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
expectInPacketHistory([]protocol.PacketNumber{0, 4, 5, 10, 12})
|
||||
})
|
||||
|
||||
It("does not ack packets below the LowestAcked", func() {
|
||||
ack := wire.AckFrame{
|
||||
LargestAcked: 8,
|
||||
LowestAcked: 3,
|
||||
}
|
||||
err := handler.ReceivedAck(&ack, 1, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
expectInPacketHistory([]protocol.PacketNumber{0, 1, 2, 9, 10, 12})
|
||||
})
|
||||
|
||||
It("handles an ACK with multiple missing packet ranges", func() {
|
||||
ack := wire.AckFrame{
|
||||
LargestAcked: 9,
|
||||
LowestAcked: 1,
|
||||
AckRanges: []wire.AckRange{ // packets 2, 4 and 5, and 8 were lost
|
||||
{First: 9, Last: 9},
|
||||
{First: 6, Last: 7},
|
||||
{First: 3, Last: 3},
|
||||
{First: 1, Last: 1},
|
||||
},
|
||||
}
|
||||
err := handler.ReceivedAck(&ack, 1, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
expectInPacketHistory([]protocol.PacketNumber{0, 2, 4, 5, 8, 10, 12})
|
||||
})
|
||||
|
||||
It("processes an ACK frame that would be sent after a late arrival of a packet", func() {
|
||||
largestObserved := 6
|
||||
ack1 := wire.AckFrame{
|
||||
LargestAcked: protocol.PacketNumber(largestObserved),
|
||||
LowestAcked: 1,
|
||||
AckRanges: []wire.AckRange{
|
||||
{First: 4, Last: protocol.PacketNumber(largestObserved)},
|
||||
{First: 1, Last: 2},
|
||||
},
|
||||
}
|
||||
err := handler.ReceivedAck(&ack1, 1, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(len(packets) - 5)))
|
||||
expectInPacketHistory([]protocol.PacketNumber{0, 3, 7, 8, 9, 10, 12})
|
||||
ack2 := wire.AckFrame{
|
||||
LargestAcked: protocol.PacketNumber(largestObserved),
|
||||
LowestAcked: 1,
|
||||
}
|
||||
err = handler.ReceivedAck(&ack2, 2, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(len(packets) - 6)))
|
||||
expectInPacketHistory([]protocol.PacketNumber{0, 7, 8, 9, 10, 12})
|
||||
})
|
||||
|
||||
It("processes an ACK frame that would be sent after a late arrival of a packet and another packet", func() {
|
||||
ack1 := wire.AckFrame{
|
||||
LargestAcked: 6,
|
||||
LowestAcked: 0,
|
||||
AckRanges: []wire.AckRange{
|
||||
{First: 4, Last: 6},
|
||||
{First: 0, Last: 2},
|
||||
},
|
||||
}
|
||||
err := handler.ReceivedAck(&ack1, 1, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(len(packets) - 6)))
|
||||
expectInPacketHistory([]protocol.PacketNumber{3, 7, 8, 9, 10, 12})
|
||||
ack2 := wire.AckFrame{
|
||||
LargestAcked: 7,
|
||||
LowestAcked: 1,
|
||||
}
|
||||
err = handler.ReceivedAck(&ack2, 2, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(len(packets) - 8)))
|
||||
expectInPacketHistory([]protocol.PacketNumber{8, 9, 10, 12})
|
||||
})
|
||||
|
||||
It("processes an ACK that contains old ACK ranges", func() {
|
||||
ack1 := wire.AckFrame{
|
||||
LargestAcked: 6,
|
||||
LowestAcked: 1,
|
||||
}
|
||||
err := handler.ReceivedAck(&ack1, 1, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
expectInPacketHistory([]protocol.PacketNumber{0, 7, 8, 9, 10, 12})
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(len(packets) - 6)))
|
||||
ack2 := wire.AckFrame{
|
||||
LargestAcked: 10,
|
||||
LowestAcked: 1,
|
||||
AckRanges: []wire.AckRange{
|
||||
{First: 8, Last: 10},
|
||||
{First: 3, Last: 3},
|
||||
{First: 1, Last: 1},
|
||||
},
|
||||
}
|
||||
err = handler.ReceivedAck(&ack2, 2, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(len(packets) - 6 - 3)))
|
||||
expectInPacketHistory([]protocol.PacketNumber{0, 7, 12})
|
||||
})
|
||||
})
|
||||
|
||||
Context("calculating RTT", func() {
|
||||
It("computes the RTT", func() {
|
||||
now := time.Now()
|
||||
// First, fake the sent times of the first, second and last packet
|
||||
getPacketElement(1).Value.sendTime = now.Add(-10 * time.Minute)
|
||||
getPacketElement(2).Value.sendTime = now.Add(-5 * time.Minute)
|
||||
getPacketElement(6).Value.sendTime = now.Add(-1 * time.Minute)
|
||||
// Now, check that the proper times are used when calculating the deltas
|
||||
err := handler.ReceivedAck(&wire.AckFrame{LargestAcked: 1}, 1, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 10*time.Minute, 1*time.Second))
|
||||
err = handler.ReceivedAck(&wire.AckFrame{LargestAcked: 2}, 2, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 5*time.Minute, 1*time.Second))
|
||||
err = handler.ReceivedAck(&wire.AckFrame{LargestAcked: 6}, 3, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 1*time.Minute, 1*time.Second))
|
||||
})
|
||||
|
||||
It("uses the DelayTime in the ACK frame", func() {
|
||||
now := time.Now()
|
||||
// make sure the rttStats have a min RTT, so that the delay is used
|
||||
handler.rttStats.UpdateRTT(5*time.Minute, 0, time.Now())
|
||||
getPacketElement(1).Value.sendTime = now.Add(-10 * time.Minute)
|
||||
err := handler.ReceivedAck(&wire.AckFrame{LargestAcked: 1, DelayTime: 5 * time.Minute}, 1, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 5*time.Minute, 1*time.Second))
|
||||
})
|
||||
})
|
||||
|
||||
Context("determinining, which ACKs we have received an ACK for", func() {
|
||||
BeforeEach(func() {
|
||||
morePackets := []*Packet{
|
||||
&Packet{PacketNumber: 13, Frames: []wire.Frame{&wire.AckFrame{LowestAcked: 80, LargestAcked: 100}, &streamFrame}, Length: 1},
|
||||
&Packet{PacketNumber: 14, Frames: []wire.Frame{&wire.AckFrame{LowestAcked: 50, LargestAcked: 200}, &streamFrame}, Length: 1},
|
||||
&Packet{PacketNumber: 15, Frames: []wire.Frame{&streamFrame}, Length: 1},
|
||||
}
|
||||
for _, packet := range morePackets {
|
||||
err := handler.SentPacket(packet)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
})
|
||||
|
||||
It("determines which ACK we have received an ACK for", func() {
|
||||
err := handler.ReceivedAck(&wire.AckFrame{LargestAcked: 15, LowestAcked: 12}, 1, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.GetLowestPacketNotConfirmedAcked()).To(Equal(protocol.PacketNumber(201)))
|
||||
})
|
||||
|
||||
It("doesn't do anything when the acked packet didn't contain an ACK", func() {
|
||||
err := handler.ReceivedAck(&wire.AckFrame{LargestAcked: 13, LowestAcked: 13}, 1, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.GetLowestPacketNotConfirmedAcked()).To(Equal(protocol.PacketNumber(101)))
|
||||
err = handler.ReceivedAck(&wire.AckFrame{LargestAcked: 15, LowestAcked: 15}, 2, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.GetLowestPacketNotConfirmedAcked()).To(Equal(protocol.PacketNumber(101)))
|
||||
})
|
||||
|
||||
It("doesn't decrease the value", func() {
|
||||
err := handler.ReceivedAck(&wire.AckFrame{LargestAcked: 14, LowestAcked: 14}, 1, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.GetLowestPacketNotConfirmedAcked()).To(Equal(protocol.PacketNumber(201)))
|
||||
err = handler.ReceivedAck(&wire.AckFrame{LargestAcked: 13, LowestAcked: 13}, 2, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.GetLowestPacketNotConfirmedAcked()).To(Equal(protocol.PacketNumber(201)))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Context("Retransmission handling", func() {
|
||||
var packets []*Packet
|
||||
|
||||
BeforeEach(func() {
|
||||
packets = []*Packet{
|
||||
{PacketNumber: 1, Frames: []wire.Frame{&streamFrame}, Length: 1, EncryptionLevel: protocol.EncryptionUnencrypted},
|
||||
{PacketNumber: 2, Frames: []wire.Frame{&streamFrame}, Length: 1, EncryptionLevel: protocol.EncryptionUnencrypted},
|
||||
{PacketNumber: 3, Frames: []wire.Frame{&streamFrame}, Length: 1, EncryptionLevel: protocol.EncryptionUnencrypted},
|
||||
{PacketNumber: 4, Frames: []wire.Frame{&streamFrame}, Length: 1, EncryptionLevel: protocol.EncryptionSecure},
|
||||
{PacketNumber: 5, Frames: []wire.Frame{&streamFrame}, Length: 1, EncryptionLevel: protocol.EncryptionSecure},
|
||||
{PacketNumber: 6, Frames: []wire.Frame{&streamFrame}, Length: 1, EncryptionLevel: protocol.EncryptionForwardSecure},
|
||||
{PacketNumber: 7, Frames: []wire.Frame{&streamFrame}, Length: 1, EncryptionLevel: protocol.EncryptionForwardSecure},
|
||||
}
|
||||
for _, packet := range packets {
|
||||
handler.SentPacket(packet)
|
||||
}
|
||||
// Increase RTT, because the tests would be flaky otherwise
|
||||
handler.rttStats.UpdateRTT(time.Minute, 0, time.Now())
|
||||
// Ack a single packet so that we have non-RTO timings
|
||||
handler.ReceivedAck(&wire.AckFrame{LargestAcked: 2, LowestAcked: 2}, 1, protocol.EncryptionForwardSecure, time.Now())
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(6)))
|
||||
})
|
||||
|
||||
It("does not dequeue a packet if no ack has been received", func() {
|
||||
Expect(handler.DequeuePacketForRetransmission()).To(BeNil())
|
||||
})
|
||||
|
||||
It("dequeues a packet for retransmission", func() {
|
||||
getPacketElement(1).Value.sendTime = time.Now().Add(-time.Hour)
|
||||
handler.OnAlarm()
|
||||
Expect(getPacketElement(1)).To(BeNil())
|
||||
Expect(handler.retransmissionQueue).To(HaveLen(1))
|
||||
Expect(handler.retransmissionQueue[0].PacketNumber).To(Equal(protocol.PacketNumber(1)))
|
||||
packet := handler.DequeuePacketForRetransmission()
|
||||
Expect(packet).ToNot(BeNil())
|
||||
Expect(packet.PacketNumber).To(Equal(protocol.PacketNumber(1)))
|
||||
Expect(handler.DequeuePacketForRetransmission()).To(BeNil())
|
||||
})
|
||||
|
||||
It("deletes non forward-secure packets when the handshake completes", func() {
|
||||
for i := protocol.PacketNumber(1); i <= 7; i++ {
|
||||
if i == 2 { // packet 2 was already acked in BeforeEach
|
||||
continue
|
||||
}
|
||||
handler.queuePacketForRetransmission(getPacketElement(i))
|
||||
}
|
||||
Expect(handler.retransmissionQueue).To(HaveLen(6))
|
||||
handler.SetHandshakeComplete()
|
||||
packet := handler.DequeuePacketForRetransmission()
|
||||
Expect(packet).ToNot(BeNil())
|
||||
Expect(packet.PacketNumber).To(Equal(protocol.PacketNumber(6)))
|
||||
packet = handler.DequeuePacketForRetransmission()
|
||||
Expect(packet).ToNot(BeNil())
|
||||
Expect(packet.PacketNumber).To(Equal(protocol.PacketNumber(7)))
|
||||
Expect(handler.DequeuePacketForRetransmission()).To(BeNil())
|
||||
})
|
||||
|
||||
Context("STOP_WAITINGs", func() {
|
||||
It("gets a STOP_WAITING frame", func() {
|
||||
ack := wire.AckFrame{LargestAcked: 5, LowestAcked: 5}
|
||||
err := handler.ReceivedAck(&ack, 2, protocol.EncryptionForwardSecure, time.Now())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(handler.GetStopWaitingFrame(false)).To(Equal(&wire.StopWaitingFrame{LeastUnacked: 6}))
|
||||
})
|
||||
|
||||
It("gets a STOP_WAITING frame after queueing a retransmission", func() {
|
||||
handler.queuePacketForRetransmission(getPacketElement(5))
|
||||
Expect(handler.GetStopWaitingFrame(false)).To(Equal(&wire.StopWaitingFrame{LeastUnacked: 6}))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
It("calculates bytes in flight", func() {
|
||||
packet1 := Packet{PacketNumber: 1, Frames: []wire.Frame{&streamFrame}, Length: 1}
|
||||
packet2 := Packet{PacketNumber: 2, Frames: []wire.Frame{&streamFrame}, Length: 2}
|
||||
packet3 := Packet{PacketNumber: 3, Frames: []wire.Frame{&streamFrame}, Length: 3}
|
||||
err := handler.SentPacket(&packet1)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(1)))
|
||||
err = handler.SentPacket(&packet2)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(1 + 2)))
|
||||
err = handler.SentPacket(&packet3)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(1 + 2 + 3)))
|
||||
|
||||
// Increase RTT, because the tests would be flaky otherwise
|
||||
handler.rttStats.UpdateRTT(time.Minute, 0, time.Now())
|
||||
|
||||
// ACK 1 and 3, NACK 2
|
||||
ack := wire.AckFrame{
|
||||
LargestAcked: 3,
|
||||
LowestAcked: 1,
|
||||
AckRanges: []wire.AckRange{
|
||||
{First: 3, Last: 3},
|
||||
{First: 1, Last: 1},
|
||||
},
|
||||
}
|
||||
err = handler.ReceivedAck(&ack, 1, protocol.EncryptionUnencrypted, time.Now())
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(2)))
|
||||
|
||||
handler.packetHistory.Front().Value.sendTime = time.Now().Add(-time.Hour)
|
||||
handler.OnAlarm()
|
||||
|
||||
Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(0)))
|
||||
})
|
||||
|
||||
Context("congestion", func() {
|
||||
var cong *mocks.MockSendAlgorithm
|
||||
|
||||
BeforeEach(func() {
|
||||
cong = mocks.NewMockSendAlgorithm(mockCtrl)
|
||||
cong.EXPECT().RetransmissionDelay().AnyTimes()
|
||||
handler.congestion = cong
|
||||
})
|
||||
|
||||
It("should call OnSent", func() {
|
||||
cong.EXPECT().OnPacketSent(
|
||||
gomock.Any(),
|
||||
protocol.ByteCount(42),
|
||||
protocol.PacketNumber(1),
|
||||
protocol.ByteCount(42),
|
||||
true,
|
||||
)
|
||||
cong.EXPECT().TimeUntilSend(gomock.Any())
|
||||
p := &Packet{
|
||||
PacketNumber: 1,
|
||||
Length: 42,
|
||||
Frames: []wire.Frame{&wire.PingFrame{}},
|
||||
}
|
||||
err := handler.SentPacket(p)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should call MaybeExitSlowStart and OnPacketAcked", func() {
|
||||
cong.EXPECT().OnPacketSent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(2)
|
||||
cong.EXPECT().TimeUntilSend(gomock.Any()).Times(2)
|
||||
cong.EXPECT().MaybeExitSlowStart()
|
||||
cong.EXPECT().OnPacketAcked(
|
||||
protocol.PacketNumber(1),
|
||||
protocol.ByteCount(1),
|
||||
protocol.ByteCount(1),
|
||||
)
|
||||
handler.SentPacket(retransmittablePacket(1))
|
||||
handler.SentPacket(retransmittablePacket(2))
|
||||
err := handler.ReceivedAck(&wire.AckFrame{LargestAcked: 1, LowestAcked: 1}, 1, protocol.EncryptionForwardSecure, time.Now())
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should call MaybeExitSlowStart and OnPacketLost", func() {
|
||||
cong.EXPECT().OnPacketSent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3)
|
||||
cong.EXPECT().TimeUntilSend(gomock.Any()).Times(3)
|
||||
cong.EXPECT().OnRetransmissionTimeout(true).Times(2)
|
||||
cong.EXPECT().OnPacketLost(
|
||||
protocol.PacketNumber(1),
|
||||
protocol.ByteCount(1),
|
||||
protocol.ByteCount(2),
|
||||
)
|
||||
cong.EXPECT().OnPacketLost(
|
||||
protocol.PacketNumber(2),
|
||||
protocol.ByteCount(1),
|
||||
protocol.ByteCount(1),
|
||||
)
|
||||
handler.SentPacket(retransmittablePacket(1))
|
||||
handler.SentPacket(retransmittablePacket(2))
|
||||
handler.SentPacket(retransmittablePacket(3))
|
||||
handler.OnAlarm() // RTO, meaning 2 lost packets
|
||||
})
|
||||
|
||||
It("allows or denies sending based on congestion", func() {
|
||||
handler.bytesInFlight = 100
|
||||
cong.EXPECT().GetCongestionWindow().Return(protocol.ByteCount(200))
|
||||
Expect(handler.SendingAllowed()).To(BeTrue())
|
||||
cong.EXPECT().GetCongestionWindow().Return(protocol.ByteCount(75))
|
||||
Expect(handler.SendingAllowed()).To(BeFalse())
|
||||
})
|
||||
|
||||
It("allows or denies sending based on the number of tracked packets", func() {
|
||||
cong.EXPECT().GetCongestionWindow().Times(2)
|
||||
Expect(handler.SendingAllowed()).To(BeTrue())
|
||||
handler.retransmissionQueue = make([]*Packet, protocol.MaxTrackedSentPackets)
|
||||
Expect(handler.SendingAllowed()).To(BeFalse())
|
||||
})
|
||||
|
||||
It("allows sending if there are retransmisisons outstanding", func() {
|
||||
cong.EXPECT().GetCongestionWindow().Times(2)
|
||||
handler.bytesInFlight = 100
|
||||
Expect(handler.retransmissionQueue).To(BeEmpty())
|
||||
Expect(handler.SendingAllowed()).To(BeFalse())
|
||||
handler.retransmissionQueue = []*Packet{{PacketNumber: 3}}
|
||||
Expect(handler.SendingAllowed()).To(BeTrue())
|
||||
})
|
||||
|
||||
It("gets the pacing delay", func() {
|
||||
handler.bytesInFlight = 100
|
||||
cong.EXPECT().OnPacketSent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any())
|
||||
cong.EXPECT().TimeUntilSend(protocol.ByteCount(100)).Return(time.Hour)
|
||||
handler.SentPacket(&Packet{PacketNumber: 1})
|
||||
Expect(handler.TimeUntilSend()).To(BeTemporally("~", time.Now().Add(time.Hour), time.Second))
|
||||
})
|
||||
|
||||
It("allows sending of one packet, if it should be sent immediately", func() {
|
||||
cong.EXPECT().TimeUntilSend(gomock.Any()).Return(time.Duration(0))
|
||||
Expect(handler.ShouldSendNumPackets()).To(Equal(1))
|
||||
})
|
||||
|
||||
It("allows sending of multiple packets, if the pacing delay is smaller than the minimum", func() {
|
||||
pacingDelay := protocol.MinPacingDelay / 10
|
||||
cong.EXPECT().TimeUntilSend(gomock.Any()).Return(pacingDelay)
|
||||
Expect(handler.ShouldSendNumPackets()).To(Equal(10))
|
||||
})
|
||||
|
||||
It("allows sending of multiple packets, if the pacing delay is smaller than the minimum, and not a fraction", func() {
|
||||
pacingDelay := protocol.MinPacingDelay * 2 / 5
|
||||
cong.EXPECT().TimeUntilSend(gomock.Any()).Return(pacingDelay)
|
||||
Expect(handler.ShouldSendNumPackets()).To(Equal(3))
|
||||
})
|
||||
})
|
||||
|
||||
Context("calculating RTO", func() {
|
||||
It("uses default RTO", func() {
|
||||
Expect(handler.computeRTOTimeout()).To(Equal(defaultRTOTimeout))
|
||||
})
|
||||
|
||||
It("uses RTO from rttStats", func() {
|
||||
rtt := time.Second
|
||||
expected := rtt + rtt/2*4
|
||||
handler.rttStats.UpdateRTT(rtt, 0, time.Now())
|
||||
Expect(handler.computeRTOTimeout()).To(Equal(expected))
|
||||
})
|
||||
|
||||
It("limits RTO min", func() {
|
||||
rtt := time.Millisecond
|
||||
handler.rttStats.UpdateRTT(rtt, 0, time.Now())
|
||||
Expect(handler.computeRTOTimeout()).To(Equal(minRTOTimeout))
|
||||
})
|
||||
|
||||
It("limits RTO max", func() {
|
||||
rtt := time.Hour
|
||||
handler.rttStats.UpdateRTT(rtt, 0, time.Now())
|
||||
Expect(handler.computeRTOTimeout()).To(Equal(maxRTOTimeout))
|
||||
})
|
||||
|
||||
It("implements exponential backoff", func() {
|
||||
handler.rtoCount = 0
|
||||
Expect(handler.computeRTOTimeout()).To(Equal(defaultRTOTimeout))
|
||||
handler.rtoCount = 1
|
||||
Expect(handler.computeRTOTimeout()).To(Equal(2 * defaultRTOTimeout))
|
||||
handler.rtoCount = 2
|
||||
Expect(handler.computeRTOTimeout()).To(Equal(4 * defaultRTOTimeout))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Delay-based loss detection", func() {
|
||||
It("detects a packet as lost", func() {
|
||||
err := handler.SentPacket(retransmittablePacket(1))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = handler.SentPacket(retransmittablePacket(2))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(handler.lossTime.IsZero()).To(BeTrue())
|
||||
|
||||
err = handler.ReceivedAck(&wire.AckFrame{LargestAcked: 2, LowestAcked: 2}, 1, protocol.EncryptionForwardSecure, time.Now().Add(time.Hour))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(handler.lossTime.IsZero()).To(BeFalse())
|
||||
|
||||
// RTT is around 1h now.
|
||||
// The formula is (1+1/8) * RTT, so this should be around that number
|
||||
Expect(handler.lossTime.Sub(time.Now())).To(BeNumerically("~", time.Hour*9/8, time.Minute))
|
||||
Expect(handler.GetAlarmTimeout().Sub(time.Now())).To(BeNumerically("~", time.Hour*9/8, time.Minute))
|
||||
|
||||
handler.packetHistory.Front().Value.sendTime = time.Now().Add(-2 * time.Hour)
|
||||
handler.OnAlarm()
|
||||
Expect(handler.DequeuePacketForRetransmission()).NotTo(BeNil())
|
||||
})
|
||||
|
||||
It("does not detect packets as lost without ACKs", func() {
|
||||
err := handler.SentPacket(&Packet{PacketNumber: 1, Length: 1})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = handler.SentPacket(retransmittablePacket(2))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = handler.SentPacket(retransmittablePacket(3))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(handler.lossTime.IsZero()).To(BeTrue())
|
||||
|
||||
err = handler.ReceivedAck(&wire.AckFrame{LargestAcked: 1, LowestAcked: 1}, 1, protocol.EncryptionUnencrypted, time.Now().Add(time.Hour))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(handler.lossTime.IsZero()).To(BeTrue())
|
||||
Expect(handler.GetAlarmTimeout().Sub(time.Now())).To(BeNumerically("~", handler.computeRTOTimeout(), time.Minute))
|
||||
|
||||
// This means RTO, so both packets should be lost
|
||||
handler.OnAlarm()
|
||||
Expect(handler.DequeuePacketForRetransmission()).ToNot(BeNil())
|
||||
Expect(handler.DequeuePacketForRetransmission()).ToNot(BeNil())
|
||||
Expect(handler.DequeuePacketForRetransmission()).To(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
Context("retransmission for handshake packets", func() {
|
||||
BeforeEach(func() {
|
||||
handler.handshakeComplete = false
|
||||
})
|
||||
|
||||
It("detects the handshake timeout", func() {
|
||||
// send handshake packets: 1, 2, 4
|
||||
// send a forward-secure packet: 3
|
||||
err := handler.SentPacket(handshakePacket(1))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = handler.SentPacket(handshakePacket(2))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = handler.SentPacket(retransmittablePacket(3))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = handler.SentPacket(handshakePacket(4))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = handler.ReceivedAck(&wire.AckFrame{LargestAcked: 1, LowestAcked: 1}, 1, protocol.EncryptionSecure, time.Now())
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(handler.lossTime.IsZero()).To(BeTrue())
|
||||
handshakeTimeout := handler.computeHandshakeTimeout()
|
||||
Expect(handler.GetAlarmTimeout().Sub(time.Now())).To(BeNumerically("~", handshakeTimeout, time.Minute))
|
||||
|
||||
handler.OnAlarm()
|
||||
p := handler.DequeuePacketForRetransmission()
|
||||
Expect(p).ToNot(BeNil())
|
||||
Expect(p.PacketNumber).To(Equal(protocol.PacketNumber(2)))
|
||||
p = handler.DequeuePacketForRetransmission()
|
||||
Expect(p).ToNot(BeNil())
|
||||
Expect(p.PacketNumber).To(Equal(protocol.PacketNumber(4)))
|
||||
Expect(handler.packetHistory.Len()).To(Equal(1))
|
||||
Expect(handler.packetHistory.Front().Value.PacketNumber).To(Equal(protocol.PacketNumber(3)))
|
||||
Expect(handler.handshakeCount).To(BeEquivalentTo(1))
|
||||
// make sure the exponential backoff is used
|
||||
Expect(handler.computeHandshakeTimeout()).To(BeNumerically("~", 2*handshakeTimeout, time.Minute))
|
||||
})
|
||||
})
|
||||
|
||||
Context("RTO retransmission", func() {
|
||||
It("queues two packets if RTO expires", func() {
|
||||
err := handler.SentPacket(retransmittablePacket(1))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = handler.SentPacket(retransmittablePacket(2))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
handler.rttStats.UpdateRTT(time.Hour, 0, time.Now())
|
||||
Expect(handler.lossTime.IsZero()).To(BeTrue())
|
||||
Expect(handler.GetAlarmTimeout().Sub(time.Now())).To(BeNumerically("~", handler.computeRTOTimeout(), time.Minute))
|
||||
|
||||
handler.OnAlarm()
|
||||
p := handler.DequeuePacketForRetransmission()
|
||||
Expect(p).ToNot(BeNil())
|
||||
Expect(p.PacketNumber).To(Equal(protocol.PacketNumber(1)))
|
||||
p = handler.DequeuePacketForRetransmission()
|
||||
Expect(p).ToNot(BeNil())
|
||||
Expect(p.PacketNumber).To(Equal(protocol.PacketNumber(2)))
|
||||
|
||||
Expect(handler.rtoCount).To(BeEquivalentTo(1))
|
||||
})
|
||||
})
|
||||
})
|
||||
42
internal/ackhandler/stop_waiting_manager.go
Normal file
42
internal/ackhandler/stop_waiting_manager.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package ackhandler
|
||||
|
||||
import (
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/wire"
|
||||
)
|
||||
|
||||
// This stopWaitingManager is not supposed to satisfy the StopWaitingManager interface, which is a remnant of the legacy AckHandler, and should be remove once we drop support for QUIC 33
|
||||
type stopWaitingManager struct {
|
||||
largestLeastUnackedSent protocol.PacketNumber
|
||||
nextLeastUnacked protocol.PacketNumber
|
||||
|
||||
lastStopWaitingFrame *wire.StopWaitingFrame
|
||||
}
|
||||
|
||||
func (s *stopWaitingManager) GetStopWaitingFrame(force bool) *wire.StopWaitingFrame {
|
||||
if s.nextLeastUnacked <= s.largestLeastUnackedSent {
|
||||
if force {
|
||||
return s.lastStopWaitingFrame
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
s.largestLeastUnackedSent = s.nextLeastUnacked
|
||||
swf := &wire.StopWaitingFrame{
|
||||
LeastUnacked: s.nextLeastUnacked,
|
||||
}
|
||||
s.lastStopWaitingFrame = swf
|
||||
return swf
|
||||
}
|
||||
|
||||
func (s *stopWaitingManager) ReceivedAck(ack *wire.AckFrame) {
|
||||
if ack.LargestAcked >= s.nextLeastUnacked {
|
||||
s.nextLeastUnacked = ack.LargestAcked + 1
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stopWaitingManager) QueuedRetransmissionForPacketNumber(p protocol.PacketNumber) {
|
||||
if p >= s.nextLeastUnacked {
|
||||
s.nextLeastUnacked = p + 1
|
||||
}
|
||||
}
|
||||
55
internal/ackhandler/stop_waiting_manager_test.go
Normal file
55
internal/ackhandler/stop_waiting_manager_test.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package ackhandler
|
||||
|
||||
import (
|
||||
"github.com/lucas-clemente/quic-go/internal/wire"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("StopWaitingManager", func() {
|
||||
var manager *stopWaitingManager
|
||||
BeforeEach(func() {
|
||||
manager = &stopWaitingManager{}
|
||||
})
|
||||
|
||||
It("returns nil in the beginning", func() {
|
||||
Expect(manager.GetStopWaitingFrame(false)).To(BeNil())
|
||||
Expect(manager.GetStopWaitingFrame(true)).To(BeNil())
|
||||
})
|
||||
|
||||
It("returns a StopWaitingFrame, when a new ACK arrives", func() {
|
||||
manager.ReceivedAck(&wire.AckFrame{LargestAcked: 10})
|
||||
Expect(manager.GetStopWaitingFrame(false)).To(Equal(&wire.StopWaitingFrame{LeastUnacked: 11}))
|
||||
})
|
||||
|
||||
It("does not decrease the LeastUnacked", func() {
|
||||
manager.ReceivedAck(&wire.AckFrame{LargestAcked: 10})
|
||||
manager.ReceivedAck(&wire.AckFrame{LargestAcked: 9})
|
||||
Expect(manager.GetStopWaitingFrame(false)).To(Equal(&wire.StopWaitingFrame{LeastUnacked: 11}))
|
||||
})
|
||||
|
||||
It("does not send the same StopWaitingFrame twice", func() {
|
||||
manager.ReceivedAck(&wire.AckFrame{LargestAcked: 10})
|
||||
Expect(manager.GetStopWaitingFrame(false)).ToNot(BeNil())
|
||||
Expect(manager.GetStopWaitingFrame(false)).To(BeNil())
|
||||
})
|
||||
|
||||
It("gets the same StopWaitingFrame twice, if forced", func() {
|
||||
manager.ReceivedAck(&wire.AckFrame{LargestAcked: 10})
|
||||
Expect(manager.GetStopWaitingFrame(false)).ToNot(BeNil())
|
||||
Expect(manager.GetStopWaitingFrame(true)).ToNot(BeNil())
|
||||
Expect(manager.GetStopWaitingFrame(true)).ToNot(BeNil())
|
||||
})
|
||||
|
||||
It("increases the LeastUnacked when a retransmission is queued", func() {
|
||||
manager.ReceivedAck(&wire.AckFrame{LargestAcked: 10})
|
||||
manager.QueuedRetransmissionForPacketNumber(20)
|
||||
Expect(manager.GetStopWaitingFrame(false)).To(Equal(&wire.StopWaitingFrame{LeastUnacked: 21}))
|
||||
})
|
||||
|
||||
It("does not decrease the LeastUnacked when a retransmission is queued", func() {
|
||||
manager.ReceivedAck(&wire.AckFrame{LargestAcked: 10})
|
||||
manager.QueuedRetransmissionForPacketNumber(9)
|
||||
Expect(manager.GetStopWaitingFrame(false)).To(Equal(&wire.StopWaitingFrame{LeastUnacked: 11}))
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user