refactor retransmission queue to enable splitting of 1-RTT CRYPTO frames (#5058)

This commit is contained in:
Marten Seemann
2025-04-19 20:14:55 +08:00
committed by GitHub
parent 0c1db893f9
commit 2cfc9b8596

View File

@@ -9,124 +9,106 @@ import (
"github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/internal/wire"
) )
type framesToRetransmit struct {
crypto []*wire.CryptoFrame
other []wire.Frame
}
type retransmissionQueue struct { type retransmissionQueue struct {
initial []wire.Frame initial *framesToRetransmit
initialCryptoData []*wire.CryptoFrame handshake *framesToRetransmit
appData framesToRetransmit
handshake []wire.Frame
handshakeCryptoData []*wire.CryptoFrame
appData []wire.Frame
} }
func newRetransmissionQueue() *retransmissionQueue { func newRetransmissionQueue() *retransmissionQueue {
return &retransmissionQueue{} return &retransmissionQueue{
initial: &framesToRetransmit{},
handshake: &framesToRetransmit{},
}
} }
func (q *retransmissionQueue) addInitial(f wire.Frame) { func (q *retransmissionQueue) addInitial(f wire.Frame) {
if cf, ok := f.(*wire.CryptoFrame); ok { if q.initial == nil {
q.initialCryptoData = append(q.initialCryptoData, cf)
return return
} }
q.initial = append(q.initial, f) if cf, ok := f.(*wire.CryptoFrame); ok {
q.initial.crypto = append(q.initial.crypto, cf)
return
}
q.initial.other = append(q.initial.other, f)
} }
func (q *retransmissionQueue) addHandshake(f wire.Frame) { func (q *retransmissionQueue) addHandshake(f wire.Frame) {
if cf, ok := f.(*wire.CryptoFrame); ok { if q.handshake == nil {
q.handshakeCryptoData = append(q.handshakeCryptoData, cf)
return return
} }
q.handshake = append(q.handshake, f) if cf, ok := f.(*wire.CryptoFrame); ok {
q.handshake.crypto = append(q.handshake.crypto, cf)
return
}
q.handshake.other = append(q.handshake.other, f)
}
func (q *retransmissionQueue) addAppData(f wire.Frame) {
switch f := f.(type) {
case *wire.StreamFrame:
panic("STREAM frames are handled with their respective streams.")
case *wire.CryptoFrame:
q.appData.crypto = append(q.appData.crypto, f)
default:
q.appData.other = append(q.appData.other, f)
}
} }
func (q *retransmissionQueue) HasData(encLevel protocol.EncryptionLevel) bool { func (q *retransmissionQueue) HasData(encLevel protocol.EncryptionLevel) bool {
//nolint:exhaustive // 0-RTT data is retransmitted in 1-RTT packets. //nolint:exhaustive // 0-RTT data is retransmitted in 1-RTT packets.
switch encLevel { switch encLevel {
case protocol.EncryptionInitial: case protocol.EncryptionInitial:
return len(q.initialCryptoData) > 0 || len(q.initial) > 0 return q.initial != nil &&
(len(q.initial.crypto) > 0 || len(q.initial.other) > 0)
case protocol.EncryptionHandshake: case protocol.EncryptionHandshake:
return len(q.handshakeCryptoData) > 0 || len(q.handshake) > 0 return q.handshake != nil &&
(len(q.handshake.crypto) > 0 || len(q.handshake.other) > 0)
case protocol.Encryption1RTT: case protocol.Encryption1RTT:
return len(q.appData) > 0 return len(q.appData.crypto) > 0 || len(q.appData.other) > 0
} }
return false return false
} }
func (q *retransmissionQueue) addAppData(f wire.Frame) {
if _, ok := f.(*wire.StreamFrame); ok {
panic("STREAM frames are handled with their respective streams.")
}
q.appData = append(q.appData, f)
}
func (q *retransmissionQueue) GetFrame(encLevel protocol.EncryptionLevel, maxLen protocol.ByteCount, v protocol.Version) wire.Frame { func (q *retransmissionQueue) GetFrame(encLevel protocol.EncryptionLevel, maxLen protocol.ByteCount, v protocol.Version) wire.Frame {
//nolint:exhaustive // 0-RTT packets can't contain retransmissions var r *framesToRetransmit
//nolint:exhaustive // 0-RTT data is retransmitted in 1-RTT packets.
switch encLevel { switch encLevel {
case protocol.EncryptionInitial: case protocol.EncryptionInitial:
return q.getInitialFrame(maxLen, v) r = q.initial
case protocol.EncryptionHandshake: case protocol.EncryptionHandshake:
return q.getHandshakeFrame(maxLen, v) r = q.handshake
case protocol.Encryption1RTT: case protocol.Encryption1RTT:
return q.getAppDataFrame(maxLen, v) r = &q.appData
} }
if r == nil {
return nil return nil
} }
func (q *retransmissionQueue) getInitialFrame(maxLen protocol.ByteCount, v protocol.Version) wire.Frame { if len(r.crypto) > 0 {
if len(q.initialCryptoData) > 0 { f := r.crypto[0]
f := q.initialCryptoData[0]
newFrame, needsSplit := f.MaybeSplitOffFrame(maxLen, v) newFrame, needsSplit := f.MaybeSplitOffFrame(maxLen, v)
if newFrame == nil && !needsSplit { // the whole frame fits if newFrame == nil && !needsSplit { // the whole frame fits
q.initialCryptoData = q.initialCryptoData[1:] r.crypto = r.crypto[1:]
return f return f
} }
if newFrame != nil { // frame was split. Leave the original frame in the queue. if newFrame != nil { // frame was split. Leave the original frame in the queue.
return newFrame return newFrame
} }
} }
if len(q.initial) == 0 { if len(r.other) == 0 {
return nil return nil
} }
f := q.initial[0] f := r.other[0]
if f.Length(v) > maxLen { if f.Length(v) > maxLen {
return nil return nil
} }
q.initial = q.initial[1:] r.other = r.other[1:]
return f
}
func (q *retransmissionQueue) getHandshakeFrame(maxLen protocol.ByteCount, v protocol.Version) wire.Frame {
if len(q.handshakeCryptoData) > 0 {
f := q.handshakeCryptoData[0]
newFrame, needsSplit := f.MaybeSplitOffFrame(maxLen, v)
if newFrame == nil && !needsSplit { // the whole frame fits
q.handshakeCryptoData = q.handshakeCryptoData[1:]
return f
}
if newFrame != nil { // frame was split. Leave the original frame in the queue.
return newFrame
}
}
if len(q.handshake) == 0 {
return nil
}
f := q.handshake[0]
if f.Length(v) > maxLen {
return nil
}
q.handshake = q.handshake[1:]
return f
}
func (q *retransmissionQueue) getAppDataFrame(maxLen protocol.ByteCount, v protocol.Version) wire.Frame {
if len(q.appData) == 0 {
return nil
}
f := q.appData[0]
if f.Length(v) > maxLen {
return nil
}
q.appData = q.appData[1:]
return f return f
} }
@@ -135,10 +117,8 @@ func (q *retransmissionQueue) DropPackets(encLevel protocol.EncryptionLevel) {
switch encLevel { switch encLevel {
case protocol.EncryptionInitial: case protocol.EncryptionInitial:
q.initial = nil q.initial = nil
q.initialCryptoData = nil
case protocol.EncryptionHandshake: case protocol.EncryptionHandshake:
q.handshake = nil q.handshake = nil
q.handshakeCryptoData = nil
default: default:
panic(fmt.Sprintf("unexpected encryption level: %s", encLevel)) panic(fmt.Sprintf("unexpected encryption level: %s", encLevel))
} }