// When 0-RTT is rejected, all application data sent so far becomes invalid.
// Delete the packets from the history and remove them from bytes_in_flight.
for pn, p := range h.appDataPackets.history.Packets() {
- if p.EncryptionLevel != protocol.Encryption0RTT && !p.skippedPacket {
+ if p.EncryptionLevel != protocol.Encryption0RTT {
break
}
h.removeFromBytesInFlight(p)
// Packets are returned in ascending packet number order.
func (h *sentPacketHandler) detectAndRemoveAckedPackets(ack *wire.AckFrame, encLevel protocol.EncryptionLevel) ([]packetWithPacketNumber, error) {
- pnSpace := h.getPacketNumberSpace(encLevel)
- ackRangeIndex := 0
if len(h.ackedPackets) > 0 {
return nil, errors.New("ackhandler BUG: ackedPackets slice not empty")
}
+
+ pnSpace := h.getPacketNumberSpace(encLevel)
+
+ if encLevel == protocol.Encryption1RTT {
+ for p := range pnSpace.history.SkippedPackets() {
+ if ack.AcksPacket(p) {
+ return nil, &qerr.TransportError{
+ ErrorCode: qerr.ProtocolViolation,
+ ErrorMessage: fmt.Sprintf("received an ACK for skipped packet number: %d (%s)", p, encLevel),
+ }
+ }
+ }
+ }
+
+ var ackRangeIndex int
lowestAcked := ack.LowestAcked()
largestAcked := ack.LargestAcked()
for pn, p := range pnSpace.history.Packets() {
return nil, fmt.Errorf("BUG: ackhandler would have acked wrong packet %d, while evaluating range %d -> %d", pn, ackRange.Smallest, ackRange.Largest)
}
}
- if p.skippedPacket {
- return nil, &qerr.TransportError{
- ErrorCode: qerr.ProtocolViolation,
- ErrorMessage: fmt.Sprintf("received an ACK for skipped packet number: %d (%s)", pn, encLevel),
- }
- }
if p.isPathProbePacket {
probePacket := pnSpace.history.RemovePathProbe(pn)
// the probe packet might already have been declared lost
break
}
- isRegularPacket := !p.skippedPacket && !p.isPathProbePacket
var packetLost bool
if !p.SendTime.After(lostSendTime) {
packetLost = true
- if isRegularPacket {
+ if !p.isPathProbePacket {
if h.logger.Debug() {
h.logger.Debugf("\tlost packet %d (time threshold)", pn)
}
}
} else if pnSpace.largestAcked >= pn+packetThreshold {
packetLost = true
- if isRegularPacket {
+ if !p.isPathProbePacket {
if h.logger.Debug() {
h.logger.Debugf("\tlost packet %d (reordering threshold)", pn)
}
}
if packetLost {
pnSpace.history.DeclareLost(pn)
- if isRegularPacket {
+ if !p.isPathProbePacket {
// the bytes in flight need to be reduced no matter if the frames in this packet will be retransmitted
h.removeFromBytesInFlight(p)
h.queueFramesForRetransmission(p)
if firstPacketSendTime.IsZero() {
firstPacketSendTime = p.SendTime
}
- if !p.declaredLost && !p.skippedPacket {
+ if !p.declaredLost {
h.queueFramesForRetransmission(p)
}
}
// All application data packets sent at this point are 0-RTT packets.
// In the case of a Retry, we can assume that the server dropped all of them.
for _, p := range h.appDataPackets.history.Packets() {
- if !p.declaredLost && !p.skippedPacket {
+ if !p.declaredLost {
h.queueFramesForRetransmission(p)
}
}
h.rttStats.ResetForPathMigration()
for pn, p := range h.appDataPackets.history.Packets() {
h.appDataPackets.history.DeclareLost(pn)
- if !p.skippedPacket && !p.isPathProbePacket {
+ if !p.isPathProbePacket {
h.removeFromBytesInFlight(p)
h.queueFramesForRetransmission(p)
}
type sentPacketHistory struct {
packets []*packet
pathProbePackets []packetWithPacketNumber
+ skippedPackets []protocol.PacketNumber
numOutstanding int
func (h *sentPacketHistory) SkippedPacket(pn protocol.PacketNumber) {
h.checkSequentialPacketNumberUse(pn)
- h.packets = append(h.packets, &packet{skippedPacket: true})
+ if len(h.packets) > 0 {
+ h.packets = append(h.packets, nil)
+ }
+ h.skippedPackets = append(h.skippedPackets, pn)
}
func (h *sentPacketHistory) SentNonAckElicitingPacket(pn protocol.PacketNumber) {
return h.pathProbePackets[0].PacketNumber, h.pathProbePackets[0].packet
}
+func (h *sentPacketHistory) SkippedPackets() iter.Seq[protocol.PacketNumber] {
+ return func(yield func(protocol.PacketNumber) bool) {
+ for _, p := range h.skippedPackets {
+ if !yield(p) {
+ return
+ }
+ }
+ }
+}
+
func (h *sentPacketHistory) Len() int {
return len(h.packets)
}
+// Remove removes a packet from the sent packet history.
+// It must not be used for skipped packet numbers.
func (h *sentPacketHistory) Remove(pn protocol.PacketNumber) error {
idx, ok := h.getIndex(pn)
if !ok {
}
h.packets[idx] = nil
// clean up all skipped packets directly before this packet number
+ var hasPacketBefore bool
for idx > 0 {
idx--
- p := h.packets[idx]
- if p == nil || !p.skippedPacket {
+ if h.packets[idx] != nil {
+ hasPacketBefore = true
break
}
- h.packets[idx] = nil
}
- if idx == 0 {
+ if !hasPacketBefore {
h.cleanupStart()
}
if len(h.packets) > 0 && h.packets[0] == nil {
- panic("remove failed")
+ panic("cleanup failed")
+ }
+ if len(h.packets) > 0 && len(h.skippedPackets) > 0 {
+ for _, p := range h.skippedPackets {
+ if p < h.firstPacketNumber {
+ h.skippedPackets = h.skippedPackets[1:]
+ }
+ }
}
return nil
}
package ackhandler
import (
+ "slices"
"testing"
"time"
func (h *sentPacketHistory) getPacketNumbers() []protocol.PacketNumber {
pns := make([]protocol.PacketNumber, 0, len(h.packets))
- for pn, p := range h.Packets() {
- if p != nil && !p.skippedPacket {
- pns = append(pns, pn)
- }
- }
- return pns
-}
-
-func (h *sentPacketHistory) getSkippedPacketNumbers() []protocol.PacketNumber {
- var pns []protocol.PacketNumber
- for pn, p := range h.Packets() {
- if p != nil && p.skippedPacket {
- pns = append(pns, pn)
- }
+ for pn := range h.Packets() {
+ pns = append(pns, pn)
}
return pns
}
hist.SentAckElicitingPacket(1, &packet{})
hist.SentAckElicitingPacket(2, &packet{})
require.Equal(t, append(firstPacketNumber, 1, 2), hist.getPacketNumbers())
- require.Empty(t, hist.getSkippedPacketNumbers())
+ require.Empty(t, slices.Collect(hist.SkippedPackets()))
if firstPacketAckEliciting {
require.Equal(t, 3, hist.Len())
} else {
hist.SkippedPacket(10)
hist.SentAckElicitingPacket(11, &packet{SendTime: now})
require.Equal(t, append(firstPacketNumber, 1, 2, 4, 6, 8, 11), hist.getPacketNumbers())
- require.Equal(t, []protocol.PacketNumber{7, 10}, hist.getSkippedPacketNumbers())
+ require.Equal(t, []protocol.PacketNumber{7, 10}, slices.Collect(hist.SkippedPackets()))
if firstPacketAckEliciting {
require.Equal(t, 12, hist.Len())
} else {
hist.SkippedPacket(5)
hist.SentAckElicitingPacket(6, &packet{})
require.Equal(t, []protocol.PacketNumber{0, 1, 4, 6}, hist.getPacketNumbers())
- require.Equal(t, []protocol.PacketNumber{2, 3, 5}, hist.getSkippedPacketNumbers())
+ require.Equal(t, []protocol.PacketNumber{2, 3, 5}, slices.Collect(hist.SkippedPackets()))
require.NoError(t, hist.Remove(0))
+ require.Equal(t, []protocol.PacketNumber{2, 3, 5}, slices.Collect(hist.SkippedPackets()))
require.NoError(t, hist.Remove(1))
require.Equal(t, []protocol.PacketNumber{4, 6}, hist.getPacketNumbers())
- require.Equal(t, []protocol.PacketNumber{2, 3, 5}, hist.getSkippedPacketNumbers())
+ require.Equal(t, []protocol.PacketNumber{5}, slices.Collect(hist.SkippedPackets()))
// add one more packet
hist.SentAckElicitingPacket(7, &packet{})
require.NoError(t, hist.Remove(6))
require.NoError(t, hist.Remove(8))
require.Empty(t, hist.getPacketNumbers())
- require.Empty(t, hist.getSkippedPacketNumbers())
+ require.Empty(t, slices.Collect(hist.SkippedPackets()))
require.False(t, hist.HasOutstandingPackets())
}
hist.SkippedPacket(4)
hist.SkippedPacket(5)
hist.SentAckElicitingPacket(6, &packet{})
+ require.Equal(t, []protocol.PacketNumber{0, 4, 5}, slices.Collect(hist.SkippedPackets()))
require.NoError(t, hist.Remove(3))
- require.NoError(t, hist.Remove(4))
- var packets, skippedPackets []protocol.PacketNumber
+ var packets []protocol.PacketNumber
for pn, p := range hist.Packets() {
- if p.skippedPacket {
- skippedPackets = append(skippedPackets, pn)
- } else {
- packets = append(packets, pn)
- }
+ require.NotNil(t, p)
+ packets = append(packets, pn)
}
require.Equal(t, []protocol.PacketNumber{1, 2, 6}, packets)
- require.Equal(t, []protocol.PacketNumber{0, 5}, skippedPackets)
+ require.Equal(t, []protocol.PacketNumber{4, 5}, slices.Collect(hist.SkippedPackets()))
}
func TestSentPacketHistoryDeleteWhileIterating(t *testing.T) {
switch pn {
case 0:
require.NoError(t, hist.Remove(0))
- case 4:
- require.NoError(t, hist.Remove(4))
+ case 3:
+ require.NoError(t, hist.Remove(3))
}
}
- require.Equal(t, []protocol.PacketNumber{0, 1, 2, 3, 4, 5}, iterations)
- require.Equal(t, []protocol.PacketNumber{1, 3, 5}, hist.getPacketNumbers())
- require.Equal(t, []protocol.PacketNumber{2}, hist.getSkippedPacketNumbers())
+ require.Equal(t, []protocol.PacketNumber{0, 1, 3, 5}, iterations)
+ require.Equal(t, []protocol.PacketNumber{1, 5}, hist.getPacketNumbers())
+ require.Equal(t, []protocol.PacketNumber{2, 4}, slices.Collect(hist.SkippedPackets()))
}
func TestSentPacketHistoryPathProbes(t *testing.T) {