mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-04 12:47:36 +03:00
uTLS is not yet bumped to the new version, so this commit breaks the dependencies relationship by getting rid of the local replace.
196 lines
5.6 KiB
Go
196 lines
5.6 KiB
Go
package ackhandler
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/refraction-networking/uquic/internal/protocol"
|
|
"github.com/refraction-networking/uquic/internal/utils"
|
|
"github.com/refraction-networking/uquic/internal/wire"
|
|
)
|
|
|
|
// number of ack-eliciting packets received before sending an ack.
|
|
const packetsBeforeAck = 2
|
|
|
|
type receivedPacketTracker struct {
|
|
largestObserved protocol.PacketNumber
|
|
ignoreBelow protocol.PacketNumber
|
|
largestObservedReceivedTime time.Time
|
|
ect0, ect1, ecnce uint64
|
|
|
|
packetHistory *receivedPacketHistory
|
|
|
|
maxAckDelay time.Duration
|
|
rttStats *utils.RTTStats
|
|
|
|
hasNewAck bool // true as soon as we received an ack-eliciting new packet
|
|
ackQueued bool // true once we received more than 2 (or later in the connection 10) ack-eliciting packets
|
|
|
|
ackElicitingPacketsReceivedSinceLastAck int
|
|
ackAlarm time.Time
|
|
lastAck *wire.AckFrame
|
|
|
|
logger utils.Logger
|
|
}
|
|
|
|
func newReceivedPacketTracker(
|
|
rttStats *utils.RTTStats,
|
|
logger utils.Logger,
|
|
) *receivedPacketTracker {
|
|
return &receivedPacketTracker{
|
|
packetHistory: newReceivedPacketHistory(),
|
|
maxAckDelay: protocol.MaxAckDelay,
|
|
rttStats: rttStats,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
func (h *receivedPacketTracker) ReceivedPacket(packetNumber protocol.PacketNumber, ecn protocol.ECN, rcvTime time.Time, shouldInstigateAck bool) error {
|
|
if isNew := h.packetHistory.ReceivedPacket(packetNumber); !isNew {
|
|
return fmt.Errorf("recevedPacketTracker BUG: ReceivedPacket called for old / duplicate packet %d", packetNumber)
|
|
}
|
|
|
|
isMissing := h.isMissing(packetNumber)
|
|
if packetNumber >= h.largestObserved {
|
|
h.largestObserved = packetNumber
|
|
h.largestObservedReceivedTime = rcvTime
|
|
}
|
|
|
|
if shouldInstigateAck {
|
|
h.hasNewAck = true
|
|
}
|
|
if shouldInstigateAck {
|
|
h.maybeQueueAck(packetNumber, rcvTime, isMissing)
|
|
}
|
|
switch ecn {
|
|
case protocol.ECNNon:
|
|
case protocol.ECT0:
|
|
h.ect0++
|
|
case protocol.ECT1:
|
|
h.ect1++
|
|
case protocol.ECNCE:
|
|
h.ecnce++
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// IgnoreBelow sets a lower limit for acknowledging packets.
|
|
// Packets with packet numbers smaller than p will not be acked.
|
|
func (h *receivedPacketTracker) IgnoreBelow(p protocol.PacketNumber) {
|
|
if p <= h.ignoreBelow {
|
|
return
|
|
}
|
|
h.ignoreBelow = p
|
|
h.packetHistory.DeleteBelow(p)
|
|
if h.logger.Debug() {
|
|
h.logger.Debugf("\tIgnoring all packets below %d.", p)
|
|
}
|
|
}
|
|
|
|
// isMissing says if a packet was reported missing in the last ACK.
|
|
func (h *receivedPacketTracker) isMissing(p protocol.PacketNumber) bool {
|
|
if h.lastAck == nil || p < h.ignoreBelow {
|
|
return false
|
|
}
|
|
return p < h.lastAck.LargestAcked() && !h.lastAck.AcksPacket(p)
|
|
}
|
|
|
|
func (h *receivedPacketTracker) hasNewMissingPackets() bool {
|
|
if h.lastAck == nil {
|
|
return false
|
|
}
|
|
highestRange := h.packetHistory.GetHighestAckRange()
|
|
return highestRange.Smallest > h.lastAck.LargestAcked()+1 && highestRange.Len() == 1
|
|
}
|
|
|
|
// maybeQueueAck queues an ACK, if necessary.
|
|
func (h *receivedPacketTracker) maybeQueueAck(pn protocol.PacketNumber, rcvTime time.Time, wasMissing bool) {
|
|
// always acknowledge the first packet
|
|
if h.lastAck == nil {
|
|
if !h.ackQueued {
|
|
h.logger.Debugf("\tQueueing ACK because the first packet should be acknowledged.")
|
|
}
|
|
h.ackQueued = true
|
|
return
|
|
}
|
|
|
|
if h.ackQueued {
|
|
return
|
|
}
|
|
|
|
h.ackElicitingPacketsReceivedSinceLastAck++
|
|
|
|
// Send an ACK if this packet was reported missing in an ACK sent before.
|
|
// Ack decimation with reordering relies on the timer to send an ACK, but if
|
|
// missing packets we reported in the previous ack, send an ACK immediately.
|
|
if wasMissing {
|
|
if h.logger.Debug() {
|
|
h.logger.Debugf("\tQueueing ACK because packet %d was missing before.", pn)
|
|
}
|
|
h.ackQueued = true
|
|
}
|
|
|
|
// send an ACK every 2 ack-eliciting packets
|
|
if h.ackElicitingPacketsReceivedSinceLastAck >= packetsBeforeAck {
|
|
if h.logger.Debug() {
|
|
h.logger.Debugf("\tQueueing ACK because packet %d packets were received after the last ACK (using initial threshold: %d).", h.ackElicitingPacketsReceivedSinceLastAck, packetsBeforeAck)
|
|
}
|
|
h.ackQueued = true
|
|
} else if h.ackAlarm.IsZero() {
|
|
if h.logger.Debug() {
|
|
h.logger.Debugf("\tSetting ACK timer to max ack delay: %s", h.maxAckDelay)
|
|
}
|
|
h.ackAlarm = rcvTime.Add(h.maxAckDelay)
|
|
}
|
|
|
|
// Queue an ACK if there are new missing packets to report.
|
|
if h.hasNewMissingPackets() {
|
|
h.logger.Debugf("\tQueuing ACK because there's a new missing packet to report.")
|
|
h.ackQueued = true
|
|
}
|
|
|
|
if h.ackQueued {
|
|
// cancel the ack alarm
|
|
h.ackAlarm = time.Time{}
|
|
}
|
|
}
|
|
|
|
func (h *receivedPacketTracker) GetAckFrame(onlyIfQueued bool) *wire.AckFrame {
|
|
if !h.hasNewAck {
|
|
return nil
|
|
}
|
|
now := time.Now()
|
|
if onlyIfQueued {
|
|
if !h.ackQueued && (h.ackAlarm.IsZero() || h.ackAlarm.After(now)) {
|
|
return nil
|
|
}
|
|
if h.logger.Debug() && !h.ackQueued && !h.ackAlarm.IsZero() {
|
|
h.logger.Debugf("Sending ACK because the ACK timer expired.")
|
|
}
|
|
}
|
|
|
|
// This function always returns the same ACK frame struct, filled with the most recent values.
|
|
ack := h.lastAck
|
|
if ack == nil {
|
|
ack = &wire.AckFrame{}
|
|
}
|
|
ack.Reset()
|
|
ack.DelayTime = utils.Max(0, now.Sub(h.largestObservedReceivedTime))
|
|
ack.ECT0 = h.ect0
|
|
ack.ECT1 = h.ect1
|
|
ack.ECNCE = h.ecnce
|
|
ack.AckRanges = h.packetHistory.AppendAckRanges(ack.AckRanges)
|
|
|
|
h.lastAck = ack
|
|
h.ackAlarm = time.Time{}
|
|
h.ackQueued = false
|
|
h.hasNewAck = false
|
|
h.ackElicitingPacketsReceivedSinceLastAck = 0
|
|
return ack
|
|
}
|
|
|
|
func (h *receivedPacketTracker) GetAlarmTimeout() time.Time { return h.ackAlarm }
|
|
|
|
func (h *receivedPacketTracker) IsPotentiallyDuplicate(pn protocol.PacketNumber) bool {
|
|
return h.packetHistory.IsPotentiallyDuplicate(pn)
|
|
}
|