forked from quic-go/quic-go
This did not cause an error, since the packet didn't exist in the packetHistory. With this fix, it is more consistent and it saves one loop iteration.
333 lines
9.9 KiB
Go
333 lines
9.9 KiB
Go
package ackhandler
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/lucas-clemente/quic-go/ackhandlerlegacy"
|
|
"github.com/lucas-clemente/quic-go/congestion"
|
|
"github.com/lucas-clemente/quic-go/frames"
|
|
"github.com/lucas-clemente/quic-go/protocol"
|
|
"github.com/lucas-clemente/quic-go/qerr"
|
|
"github.com/lucas-clemente/quic-go/utils"
|
|
)
|
|
|
|
var (
|
|
// ErrDuplicateOrOutOfOrderAck occurs when a duplicate or an out-of-order ACK is received
|
|
ErrDuplicateOrOutOfOrderAck = errors.New("SentPacketHandler: Duplicate or out-of-order ACK")
|
|
// ErrEntropy occurs when an ACK with incorrect entropy is received
|
|
ErrEntropy = qerr.Error(qerr.InvalidAckData, "wrong entropy")
|
|
// ErrMapAccess occurs when a NACK contains invalid NACK ranges
|
|
ErrMapAccess = qerr.Error(qerr.InvalidAckData, "Packet does not exist in PacketHistory")
|
|
// ErrTooManyTrackedSentPackets occurs when the sentPacketHandler has to keep track of too many packets
|
|
ErrTooManyTrackedSentPackets = errors.New("Too many outstanding non-acked and non-retransmitted packets")
|
|
errAckForUnsentPacket = qerr.Error(qerr.InvalidAckData, "Received ACK for an unsent package")
|
|
)
|
|
|
|
var errDuplicatePacketNumber = errors.New("Packet number already exists in Packet History")
|
|
|
|
type sentPacketHandler struct {
|
|
lastSentPacketNumber protocol.PacketNumber
|
|
lastSentPacketTime time.Time
|
|
LargestInOrderAcked protocol.PacketNumber
|
|
LargestAcked protocol.PacketNumber
|
|
|
|
largestReceivedPacketWithAck protocol.PacketNumber
|
|
|
|
// TODO: Move into separate class as in chromium
|
|
packetHistory map[protocol.PacketNumber]*ackhandlerlegacy.Packet
|
|
stopWaitingManager stopWaitingManager
|
|
|
|
retransmissionQueue []*ackhandlerlegacy.Packet
|
|
|
|
bytesInFlight protocol.ByteCount
|
|
|
|
rttStats *congestion.RTTStats
|
|
congestion congestion.SendAlgorithm
|
|
}
|
|
|
|
// NewSentPacketHandler creates a new sentPacketHandler
|
|
func NewSentPacketHandler() SentPacketHandler {
|
|
rttStats := &congestion.RTTStats{}
|
|
|
|
congestion := congestion.NewCubicSender(
|
|
congestion.DefaultClock{},
|
|
rttStats,
|
|
false, /* don't use reno since chromium doesn't (why?) */
|
|
protocol.InitialCongestionWindow,
|
|
protocol.DefaultMaxCongestionWindow,
|
|
)
|
|
|
|
return &sentPacketHandler{
|
|
packetHistory: make(map[protocol.PacketNumber]*ackhandlerlegacy.Packet),
|
|
stopWaitingManager: stopWaitingManager{},
|
|
rttStats: rttStats,
|
|
congestion: congestion,
|
|
}
|
|
}
|
|
|
|
func (h *sentPacketHandler) ackPacket(packetNumber protocol.PacketNumber) *ackhandlerlegacy.Packet {
|
|
packet, ok := h.packetHistory[packetNumber]
|
|
if ok && !packet.Retransmitted {
|
|
h.bytesInFlight -= packet.Length
|
|
}
|
|
|
|
if h.LargestInOrderAcked == packetNumber-1 {
|
|
h.LargestInOrderAcked++
|
|
}
|
|
|
|
delete(h.packetHistory, packetNumber)
|
|
|
|
return packet
|
|
}
|
|
|
|
func (h *sentPacketHandler) nackPacket(packetNumber protocol.PacketNumber) (*ackhandlerlegacy.Packet, error) {
|
|
packet, ok := h.packetHistory[packetNumber]
|
|
// This means that the packet has already been retransmitted, do nothing.
|
|
// We're probably only receiving another NACK for this packet because the
|
|
// retransmission has not yet arrived at the client.
|
|
if !ok {
|
|
return nil, nil
|
|
}
|
|
|
|
packet.MissingReports++
|
|
|
|
if packet.MissingReports > protocol.RetransmissionThreshold {
|
|
h.queuePacketForRetransmission(packet)
|
|
return packet, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (h *sentPacketHandler) queuePacketForRetransmission(packet *ackhandlerlegacy.Packet) {
|
|
h.bytesInFlight -= packet.Length
|
|
h.retransmissionQueue = append(h.retransmissionQueue, packet)
|
|
packet.Retransmitted = true
|
|
|
|
// increase the LargestInOrderAcked, if this is the lowest packet that hasn't been acked yet
|
|
if packet.PacketNumber == h.LargestInOrderAcked+1 {
|
|
for i := packet.PacketNumber + 1; i < h.LargestAcked; i++ {
|
|
_, ok := h.packetHistory[protocol.PacketNumber(i)]
|
|
if !ok {
|
|
h.LargestInOrderAcked = i
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// strictly speaking, this is only necessary for RTO retransmissions
|
|
// this is because FastRetransmissions are triggered by missing ranges in ACKs, and then the LargestAcked will already be higher than the packet number of the retransmitted packet
|
|
h.stopWaitingManager.QueuedRetransmissionForPacketNumber(packet.PacketNumber)
|
|
}
|
|
|
|
func (h *sentPacketHandler) SentPacket(packet *ackhandlerlegacy.Packet) error {
|
|
_, ok := h.packetHistory[packet.PacketNumber]
|
|
if ok {
|
|
return errDuplicatePacketNumber
|
|
}
|
|
|
|
now := time.Now()
|
|
h.lastSentPacketTime = now
|
|
packet.SendTime = now
|
|
if packet.Length == 0 {
|
|
return errors.New("SentPacketHandler: packet cannot be empty")
|
|
}
|
|
h.bytesInFlight += packet.Length
|
|
|
|
h.lastSentPacketNumber = packet.PacketNumber
|
|
h.packetHistory[packet.PacketNumber] = packet
|
|
|
|
h.congestion.OnPacketSent(
|
|
time.Now(),
|
|
h.BytesInFlight(),
|
|
packet.PacketNumber,
|
|
packet.Length,
|
|
true, /* TODO: is retransmittable */
|
|
)
|
|
|
|
return nil
|
|
}
|
|
|
|
// TODO: Simplify return types
|
|
func (h *sentPacketHandler) ReceivedAck(ackFrame *frames.AckFrame, withPacketNumber protocol.PacketNumber) error {
|
|
if ackFrame.LargestAcked > h.lastSentPacketNumber {
|
|
return errAckForUnsentPacket
|
|
}
|
|
|
|
// duplicate or out-of-order ACK
|
|
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.LargestInOrderAcked {
|
|
return nil
|
|
}
|
|
|
|
h.LargestAcked = ackFrame.LargestAcked
|
|
|
|
packet, ok := h.packetHistory[h.LargestAcked]
|
|
if ok {
|
|
// Update the RTT
|
|
timeDelta := time.Now().Sub(packet.SendTime)
|
|
// TODO: Don't always update RTT
|
|
h.rttStats.UpdateRTT(timeDelta, ackFrame.DelayTime, time.Now())
|
|
if utils.Debug() {
|
|
utils.Debugf("\tEstimated RTT: %dms", h.rttStats.SmoothedRTT()/time.Millisecond)
|
|
}
|
|
}
|
|
|
|
var ackedPackets congestion.PacketVector
|
|
var lostPackets congestion.PacketVector
|
|
|
|
// NACK packets below the LowestAcked
|
|
for i := h.LargestInOrderAcked + 1; i < ackFrame.LowestAcked; i++ {
|
|
p, err := h.nackPacket(i)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if p != nil {
|
|
lostPackets = append(lostPackets, congestion.PacketInfo{Number: p.PacketNumber, Length: p.Length})
|
|
}
|
|
}
|
|
|
|
ackRangeIndex := 0
|
|
for i := ackFrame.LowestAcked; i <= ackFrame.LargestAcked; i++ {
|
|
if ackFrame.HasMissingRanges() {
|
|
ackRange := ackFrame.AckRanges[len(ackFrame.AckRanges)-1-ackRangeIndex]
|
|
|
|
if i > ackRange.LastPacketNumber && ackRangeIndex < len(ackFrame.AckRanges)-1 {
|
|
ackRangeIndex++
|
|
ackRange = ackFrame.AckRanges[len(ackFrame.AckRanges)-1-ackRangeIndex]
|
|
}
|
|
|
|
if i >= ackRange.FirstPacketNumber { // packet i contained in ACK range
|
|
p := h.ackPacket(i)
|
|
if p != nil {
|
|
ackedPackets = append(ackedPackets, congestion.PacketInfo{Number: p.PacketNumber, Length: p.Length})
|
|
}
|
|
} else {
|
|
p, err := h.nackPacket(i)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if p != nil {
|
|
lostPackets = append(lostPackets, congestion.PacketInfo{Number: p.PacketNumber, Length: p.Length})
|
|
}
|
|
}
|
|
} else {
|
|
p := h.ackPacket(i)
|
|
if p != nil {
|
|
ackedPackets = append(ackedPackets, congestion.PacketInfo{Number: p.PacketNumber, Length: p.Length})
|
|
}
|
|
}
|
|
}
|
|
|
|
h.stopWaitingManager.ReceivedAck(ackFrame)
|
|
|
|
h.congestion.OnCongestionEvent(
|
|
true, /* TODO: rtt updated */
|
|
h.BytesInFlight(),
|
|
ackedPackets,
|
|
lostPackets,
|
|
)
|
|
|
|
return nil
|
|
}
|
|
|
|
// ProbablyHasPacketForRetransmission returns if there is a packet queued for retransmission
|
|
// There is one case where it gets the answer wrong:
|
|
// if a packet has already been queued for retransmission, but a belated ACK is received for this packet, this function will return true, although the packet will not be returend for retransmission by DequeuePacketForRetransmission()
|
|
func (h *sentPacketHandler) ProbablyHasPacketForRetransmission() bool {
|
|
h.maybeQueuePacketsRTO()
|
|
|
|
return len(h.retransmissionQueue) > 0
|
|
}
|
|
|
|
func (h *sentPacketHandler) DequeuePacketForRetransmission() (packet *ackhandlerlegacy.Packet) {
|
|
if !h.ProbablyHasPacketForRetransmission() {
|
|
return nil
|
|
}
|
|
|
|
for len(h.retransmissionQueue) > 0 {
|
|
queueLen := len(h.retransmissionQueue)
|
|
// packets are usually NACKed in descending order. So use the slice as a stack
|
|
packet = h.retransmissionQueue[queueLen-1]
|
|
h.retransmissionQueue = h.retransmissionQueue[:queueLen-1]
|
|
|
|
// this happens if a belated ACK arrives for this packet
|
|
// no need to retransmit it
|
|
_, ok := h.packetHistory[packet.PacketNumber]
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
delete(h.packetHistory, packet.PacketNumber)
|
|
return packet
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *sentPacketHandler) BytesInFlight() protocol.ByteCount {
|
|
return h.bytesInFlight
|
|
}
|
|
|
|
func (h *sentPacketHandler) GetLargestAcked() protocol.PacketNumber {
|
|
return h.LargestAcked
|
|
}
|
|
|
|
func (h *sentPacketHandler) GetStopWaitingFrame() *frames.StopWaitingFrame {
|
|
return h.stopWaitingManager.GetStopWaitingFrame()
|
|
}
|
|
|
|
func (h *sentPacketHandler) CongestionAllowsSending() bool {
|
|
return h.BytesInFlight() <= h.congestion.GetCongestionWindow()
|
|
}
|
|
|
|
func (h *sentPacketHandler) CheckForError() error {
|
|
length := len(h.retransmissionQueue) + len(h.packetHistory)
|
|
if uint32(length) > protocol.MaxTrackedSentPackets {
|
|
return ErrTooManyTrackedSentPackets
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *sentPacketHandler) maybeQueuePacketsRTO() {
|
|
if time.Now().Before(h.TimeOfFirstRTO()) {
|
|
return
|
|
}
|
|
|
|
for p := h.LargestInOrderAcked + 1; p <= h.lastSentPacketNumber; p++ {
|
|
packet := h.packetHistory[p]
|
|
if packet != nil && !packet.Retransmitted {
|
|
packetsLost := congestion.PacketVector{congestion.PacketInfo{
|
|
Number: packet.PacketNumber,
|
|
Length: packet.Length,
|
|
}}
|
|
h.congestion.OnCongestionEvent(false, h.BytesInFlight(), nil, packetsLost)
|
|
h.congestion.OnRetransmissionTimeout(true)
|
|
h.queuePacketForRetransmission(packet)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (h *sentPacketHandler) getRTO() time.Duration {
|
|
rto := h.congestion.RetransmissionDelay()
|
|
if rto == 0 {
|
|
rto = protocol.DefaultRetransmissionTime
|
|
}
|
|
return utils.MaxDuration(rto, protocol.MinRetransmissionTime)
|
|
}
|
|
|
|
func (h *sentPacketHandler) TimeOfFirstRTO() time.Time {
|
|
if h.lastSentPacketTime.IsZero() {
|
|
return time.Time{}
|
|
}
|
|
return h.lastSentPacketTime.Add(h.getRTO())
|
|
}
|