use default RTT (100ms) for 0-RTT if no prior estimate (#5388)

* utils: initialize the {Smoothed, Latest, Min}RTT to 100ms

* utils: use time.Duration.Nanoseconds instead of uint64

No functional change expected.

* congestion: better check to avoid division by zero
This commit is contained in:
Marten Seemann
2025-10-16 15:32:46 +08:00
committed by GitHub
parent 5e5100b40c
commit 42b198b8d1
14 changed files with 157 additions and 143 deletions

View File

@@ -111,7 +111,7 @@ func testSentPacketHandlerSendAndAcknowledge(t *testing.T, encLevel protocol.Enc
sph := newSentPacketHandler(
0,
1200,
&utils.RTTStats{},
utils.NewRTTStats(),
&utils.ConnectionStats{},
false,
false,
@@ -165,7 +165,7 @@ func TestSentPacketHandlerAcknowledgeSkippedPacket(t *testing.T) {
sph := newSentPacketHandler(
0,
1200,
&utils.RTTStats{},
utils.NewRTTStats(),
&utils.ConnectionStats{},
false,
false,
@@ -213,14 +213,14 @@ func TestSentPacketHandlerRTTs(t *testing.T) {
}
func testSentPacketHandlerRTTs(t *testing.T, encLevel protocol.EncryptionLevel, usesAckDelay bool) {
var expectedRTTStats utils.RTTStats
expectedRTTStats := utils.NewRTTStats()
expectedRTTStats.SetMaxAckDelay(time.Second)
var rttStats utils.RTTStats
rttStats := utils.NewRTTStats()
rttStats.SetMaxAckDelay(time.Second)
sph := newSentPacketHandler(
0,
1200,
&rttStats,
rttStats,
&utils.ConnectionStats{},
false,
false,
@@ -310,7 +310,7 @@ func testSentPacketHandlerAmplificationLimitServer(t *testing.T, addressValidate
sph := newSentPacketHandler(
0,
1200,
&utils.RTTStats{},
utils.NewRTTStats(),
&utils.ConnectionStats{},
addressValidated,
false,
@@ -380,7 +380,7 @@ func testSentPacketHandlerAmplificationLimitClient(t *testing.T, dropHandshake b
sph := newSentPacketHandler(
0,
1200,
&utils.RTTStats{},
utils.NewRTTStats(),
&utils.ConnectionStats{},
true,
false,
@@ -431,11 +431,11 @@ func testSentPacketHandlerAmplificationLimitClient(t *testing.T, dropHandshake b
}
func TestSentPacketHandlerDelayBasedLossDetection(t *testing.T) {
var rttStats utils.RTTStats
rttStats := utils.NewRTTStats()
sph := newSentPacketHandler(
0,
1200,
&rttStats,
rttStats,
&utils.ConnectionStats{},
true,
false,
@@ -485,11 +485,11 @@ func TestSentPacketHandlerDelayBasedLossDetection(t *testing.T) {
}
func TestSentPacketHandlerPacketBasedLossDetection(t *testing.T) {
var rttStats utils.RTTStats
rttStats := utils.NewRTTStats()
sph := newSentPacketHandler(
0,
1200,
&rttStats,
rttStats,
&utils.ConnectionStats{},
true,
false,
@@ -542,7 +542,7 @@ func testSentPacketHandlerPTO(t *testing.T, encLevel protocol.EncryptionLevel, p
var packets packetTracker
var eventRecorder events.Recorder
var rttStats utils.RTTStats
rttStats := utils.NewRTTStats()
rttStats.SetMaxAckDelay(25 * time.Millisecond)
rttStats.UpdateRTT(500*time.Millisecond, 0)
rttStats.UpdateRTT(1000*time.Millisecond, 0)
@@ -550,7 +550,7 @@ func testSentPacketHandlerPTO(t *testing.T, encLevel protocol.EncryptionLevel, p
sph := newSentPacketHandler(
0,
1200,
&rttStats,
rttStats,
&utils.ConnectionStats{},
true,
false,
@@ -745,13 +745,13 @@ func testSentPacketHandlerPTO(t *testing.T, encLevel protocol.EncryptionLevel, p
}
func TestSentPacketHandlerPacketNumberSpacesPTO(t *testing.T) {
var rttStats utils.RTTStats
rttStats := utils.NewRTTStats()
const rtt = time.Second
rttStats.UpdateRTT(rtt, 0)
sph := newSentPacketHandler(
0,
1200,
&rttStats,
rttStats,
&utils.ConnectionStats{},
true,
false,
@@ -843,7 +843,7 @@ func TestSentPacketHandler0RTT(t *testing.T) {
sph := newSentPacketHandler(
0,
1200,
&utils.RTTStats{},
utils.NewRTTStats(),
&utils.ConnectionStats{},
true,
false,
@@ -890,11 +890,11 @@ func TestSentPacketHandler0RTT(t *testing.T) {
func TestSentPacketHandlerCongestion(t *testing.T) {
mockCtrl := gomock.NewController(t)
cong := mocks.NewMockSendAlgorithmWithDebugInfos(mockCtrl)
var rttStats utils.RTTStats
rttStats := utils.NewRTTStats()
sph := newSentPacketHandler(
0,
1200,
&rttStats,
rttStats,
&utils.ConnectionStats{},
true,
false,
@@ -990,11 +990,11 @@ func TestSentPacketHandlerRetry(t *testing.T) {
func testSentPacketHandlerRetry(t *testing.T, rtt, expectedRTT time.Duration) {
var initialPackets, appDataPackets packetTracker
var rttStats utils.RTTStats
rttStats := utils.NewRTTStats()
sph := newSentPacketHandler(
0,
1200,
&rttStats,
rttStats,
&utils.ConnectionStats{},
true,
false,
@@ -1041,11 +1041,11 @@ func testSentPacketHandlerRetry(t *testing.T, rtt, expectedRTT time.Duration) {
}
func TestSentPacketHandlerRetryAfterPTO(t *testing.T) {
var rttStats utils.RTTStats
rttStats := utils.NewRTTStats()
sph := newSentPacketHandler(
0,
1200,
&rttStats,
rttStats,
&utils.ConnectionStats{},
true,
false,
@@ -1076,7 +1076,7 @@ func TestSentPacketHandlerRetryAfterPTO(t *testing.T) {
require.Equal(t, []protocol.PacketNumber{pn1, pn2}, packets.Lost)
// no RTT measurement is taken, since the PTO timer fired
require.Zero(t, rttStats.SmoothedRTT())
require.Equal(t, utils.DefaultInitialRTT, rttStats.SmoothedRTT())
}
func TestSentPacketHandlerECN(t *testing.T) {
@@ -1089,7 +1089,7 @@ func TestSentPacketHandlerECN(t *testing.T) {
sph := newSentPacketHandler(
0,
1200,
&utils.RTTStats{},
utils.NewRTTStats(),
&utils.ConnectionStats{},
true,
false,
@@ -1187,13 +1187,13 @@ func TestSentPacketHandlerECN(t *testing.T) {
func TestSentPacketHandlerPathProbe(t *testing.T) {
const rtt = 10 * time.Millisecond // RTT of the original path
var rttStats utils.RTTStats
rttStats := utils.NewRTTStats()
rttStats.UpdateRTT(rtt, 0)
sph := newSentPacketHandler(
0,
1200,
&rttStats,
rttStats,
&utils.ConnectionStats{},
true,
false,
@@ -1261,19 +1261,19 @@ func TestSentPacketHandlerPathProbe(t *testing.T) {
packets.Lost = packets.Lost[:0]
sph.MigratedPath(now, 1200)
require.Zero(t, sph.getBytesInFlight())
require.Zero(t, rttStats.SmoothedRTT())
require.Equal(t, utils.DefaultInitialRTT, rttStats.SmoothedRTT())
require.Equal(t, []protocol.PacketNumber{pn1, pn2}, packets.Lost)
}
func TestSentPacketHandlerPathProbeAckAndLoss(t *testing.T) {
const rtt = 10 * time.Millisecond // RTT of the original path
var rttStats utils.RTTStats
rttStats := utils.NewRTTStats()
rttStats.UpdateRTT(rtt, 0)
sph := newSentPacketHandler(
0,
1200,
&rttStats,
rttStats,
&utils.ConnectionStats{},
true,
false,
@@ -1337,7 +1337,7 @@ func testSentPacketHandlerRandomized(t *testing.T, seed uint64) {
binary.BigEndian.PutUint64(b[:], seed)
r := rand.New(rand.NewChaCha8(b))
var rttStats utils.RTTStats
rttStats := utils.NewRTTStats()
rtt := []time.Duration{10 * time.Millisecond, 100 * time.Millisecond, 1000 * time.Millisecond}[r.IntN(3)]
t.Logf("rtt: %dms", rtt.Milliseconds())
rttStats.UpdateRTT(rtt, 0) // RTT of the original path
@@ -1349,7 +1349,7 @@ func testSentPacketHandlerRandomized(t *testing.T, seed uint64) {
sph := newSentPacketHandler(
0,
1200,
&rttStats,
rttStats,
&utils.ConnectionStats{},
true,
false,
@@ -1418,7 +1418,7 @@ func TestSentPacketHandlerSpuriousLoss(t *testing.T) {
sph := newSentPacketHandler(
0,
1200,
&utils.RTTStats{},
utils.NewRTTStats(),
&utils.ConnectionStats{},
true,
false,
@@ -1549,11 +1549,11 @@ func BenchmarkSendAndAcknowledge(b *testing.B) {
func benchmarkSendAndAcknowledge(b *testing.B, ackEvery, inFlight int) {
b.ReportAllocs()
var rttStats utils.RTTStats
rttStats := utils.NewRTTStats()
sph := newSentPacketHandler(
0,
1200,
&rttStats,
rttStats,
&utils.ConnectionStats{},
true,
false,

View File

@@ -278,8 +278,8 @@ func (c *cubicSender) isCwndLimited(bytesInFlight protocol.ByteCount) bool {
func (c *cubicSender) BandwidthEstimate() Bandwidth {
srtt := c.rttStats.SmoothedRTT()
if srtt == 0 {
// If we haven't measured an rtt, the bandwidth estimate is unknown.
return infBandwidth
// This should never happen, but if it does, avoid division by zero.
srtt = protocol.TimerGranularity
}
return BandwidthFromDelta(c.GetCongestionWindow(), srtt)
}

View File

@@ -162,7 +162,6 @@ func TestCubicSenderExponentialSlowStart(t *testing.T) {
// At startup make sure we can send.
require.True(t, sender.sender.CanSend(0))
require.Zero(t, sender.sender.TimeUntilSend(0))
require.Equal(t, infBandwidth, sender.sender.BandwidthEstimate())
const numberOfAcks = 20
for range numberOfAcks {

View File

@@ -17,7 +17,7 @@ func TestConnectionFlowControlWindowUpdate(t *testing.T) {
100, // initial receive window
100, // max receive window
nil,
&utils.RTTStats{},
utils.NewRTTStats(),
utils.DefaultLogger,
)
require.False(t, fc.AddBytesRead(1))
@@ -28,7 +28,7 @@ func TestConnectionFlowControlWindowUpdate(t *testing.T) {
func TestConnectionWindowAutoTuningNotAllowed(t *testing.T) {
// the RTT is 1 second
rttStats := &utils.RTTStats{}
rttStats := utils.NewRTTStats()
rttStats.UpdateRTT(time.Second, 0)
require.Equal(t, time.Second, rttStats.SmoothedRTT())
@@ -52,7 +52,7 @@ func TestConnectionWindowAutoTuningNotAllowed(t *testing.T) {
}
func TestConnectionFlowControlViolation(t *testing.T) {
fc := NewConnectionFlowController(100, 100, nil, &utils.RTTStats{}, utils.DefaultLogger)
fc := NewConnectionFlowController(100, 100, nil, utils.NewRTTStats(), utils.DefaultLogger)
require.NoError(t, fc.IncrementHighestReceived(40, monotime.Now()))
require.NoError(t, fc.IncrementHighestReceived(60, monotime.Now()))
err := fc.IncrementHighestReceived(1, monotime.Now())
@@ -62,7 +62,7 @@ func TestConnectionFlowControlViolation(t *testing.T) {
}
func TestConnectionFlowControllerReset(t *testing.T) {
fc := NewConnectionFlowController(0, 0, nil, &utils.RTTStats{}, utils.DefaultLogger)
fc := NewConnectionFlowController(0, 0, nil, utils.NewRTTStats(), utils.DefaultLogger)
fc.UpdateSendWindow(100)
fc.AddBytesSent(10)
require.Equal(t, protocol.ByteCount(90), fc.SendWindowSize())
@@ -71,7 +71,7 @@ func TestConnectionFlowControllerReset(t *testing.T) {
}
func TestConnectionFlowControllerResetAfterReading(t *testing.T) {
fc := NewConnectionFlowController(0, 0, nil, &utils.RTTStats{}, utils.DefaultLogger)
fc := NewConnectionFlowController(0, 0, nil, utils.NewRTTStats(), utils.DefaultLogger)
fc.AddBytesRead(1)
require.EqualError(t, fc.Reset(), "flow controller reset after reading data")
}

View File

@@ -19,13 +19,13 @@ func TestStreamFlowControlReceiving(t *testing.T) {
protocol.MaxByteCount,
protocol.MaxByteCount,
nil,
&utils.RTTStats{},
utils.NewRTTStats(),
utils.DefaultLogger,
),
100,
protocol.MaxByteCount,
protocol.MaxByteCount,
&utils.RTTStats{},
utils.NewRTTStats(),
utils.DefaultLogger,
)
@@ -52,13 +52,13 @@ func TestStreamFlowControllerFinalOffset(t *testing.T) {
protocol.MaxByteCount,
protocol.MaxByteCount,
nil,
&utils.RTTStats{},
utils.NewRTTStats(),
utils.DefaultLogger,
),
protocol.MaxByteCount,
protocol.MaxByteCount,
protocol.MaxByteCount,
&utils.RTTStats{},
utils.NewRTTStats(),
utils.DefaultLogger,
)
}
@@ -109,7 +109,7 @@ func TestStreamAbandoning(t *testing.T) {
100,
protocol.MaxByteCount,
nil,
&utils.RTTStats{},
utils.NewRTTStats(),
utils.DefaultLogger,
)
require.True(t, connFC.UpdateSendWindow(300))
@@ -119,7 +119,7 @@ func TestStreamAbandoning(t *testing.T) {
60,
protocol.MaxByteCount,
100,
&utils.RTTStats{},
utils.NewRTTStats(),
utils.DefaultLogger,
)
@@ -140,7 +140,7 @@ func TestStreamSendWindow(t *testing.T) {
protocol.MaxByteCount,
protocol.MaxByteCount,
nil,
&utils.RTTStats{},
utils.NewRTTStats(),
utils.DefaultLogger,
)
require.True(t, connFC.UpdateSendWindow(300))
@@ -150,7 +150,7 @@ func TestStreamSendWindow(t *testing.T) {
protocol.MaxByteCount,
protocol.MaxByteCount,
100,
&utils.RTTStats{},
utils.NewRTTStats(),
utils.DefaultLogger,
)
// first, we're limited by the stream flow controller
@@ -183,13 +183,13 @@ func TestStreamWindowUpdate(t *testing.T) {
protocol.MaxByteCount,
protocol.MaxByteCount,
nil,
&utils.RTTStats{},
utils.NewRTTStats(),
utils.DefaultLogger,
),
100,
100,
protocol.MaxByteCount,
&utils.RTTStats{},
utils.NewRTTStats(),
utils.DefaultLogger,
)
require.Zero(t, fc.GetWindowUpdate(monotime.Now()))
@@ -221,7 +221,7 @@ func TestStreamConnectionWindowUpdate(t *testing.T) {
100,
protocol.MaxByteCount,
nil,
&utils.RTTStats{},
utils.NewRTTStats(),
utils.DefaultLogger,
)
fc := NewStreamFlowController(
@@ -230,7 +230,7 @@ func TestStreamConnectionWindowUpdate(t *testing.T) {
1000,
protocol.MaxByteCount,
protocol.MaxByteCount,
&utils.RTTStats{},
utils.NewRTTStats(),
utils.DefaultLogger,
)
@@ -243,7 +243,7 @@ func TestStreamConnectionWindowUpdate(t *testing.T) {
func TestStreamWindowAutoTuning(t *testing.T) {
// the RTT is 1 second
rttStats := &utils.RTTStats{}
rttStats := utils.NewRTTStats()
rttStats.UpdateRTT(time.Second, 0)
require.Equal(t, time.Second, rttStats.SmoothedRTT())

View File

@@ -70,7 +70,7 @@ func TestErrorBeforeClientHelloGeneration(t *testing.T) {
&wire.TransportParameters{},
tlsConf,
false,
&utils.RTTStats{},
utils.NewRTTStats(),
nil,
utils.DefaultLogger.WithPrefix("client"),
protocol.Version1,
@@ -92,7 +92,7 @@ func TestMessageReceivedAtWrongEncryptionLevel(t *testing.T) {
&wire.TransportParameters{StatelessResetToken: &token},
testdata.GetTLSConfig(),
false,
&utils.RTTStats{},
utils.NewRTTStats(),
nil,
utils.DefaultLogger.WithPrefix("server"),
protocol.Version1,
@@ -221,7 +221,7 @@ func TestHandshake(t *testing.T) {
_, _, clientErr, _, _, serverErr := handshakeWithTLSConf(
t,
clientConf, serverConf,
&utils.RTTStats{}, &utils.RTTStats{},
utils.NewRTTStats(), utils.NewRTTStats(),
&wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2},
false,
)
@@ -235,7 +235,7 @@ func TestHelloRetryRequest(t *testing.T) {
_, _, clientErr, _, _, serverErr := handshakeWithTLSConf(
t,
clientConf, serverConf,
&utils.RTTStats{}, &utils.RTTStats{},
utils.NewRTTStats(), utils.NewRTTStats(),
&wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2},
false,
)
@@ -267,7 +267,7 @@ func TestWithClientAuth(t *testing.T) {
_, _, clientErr, _, _, serverErr := handshakeWithTLSConf(
t,
clientConf, serverConf,
&utils.RTTStats{}, &utils.RTTStats{},
utils.NewRTTStats(), utils.NewRTTStats(),
&wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2},
false,
)
@@ -283,7 +283,7 @@ func TestTransportParameters(t *testing.T) {
cTransportParameters,
clientConf,
false,
&utils.RTTStats{},
utils.NewRTTStats(),
nil,
utils.DefaultLogger.WithPrefix("client"),
protocol.Version1,
@@ -302,7 +302,7 @@ func TestTransportParameters(t *testing.T) {
sTransportParameters,
serverConf,
false,
&utils.RTTStats{},
utils.NewRTTStats(),
nil,
utils.DefaultLogger.WithPrefix("server"),
protocol.Version1,
@@ -335,7 +335,7 @@ func TestNewSessionTicketAtWrongEncryptionLevel(t *testing.T) {
client, _, clientErr, _, _, serverErr := handshakeWithTLSConf(
t,
clientConf, serverConf,
&utils.RTTStats{}, &utils.RTTStats{},
utils.NewRTTStats(), utils.NewRTTStats(),
&wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2},
false,
)
@@ -354,7 +354,7 @@ func TestHandlingNewSessionTicketFails(t *testing.T) {
client, _, clientErr, _, _, serverErr := handshakeWithTLSConf(
t,
clientConf, serverConf,
&utils.RTTStats{}, &utils.RTTStats{},
utils.NewRTTStats(), utils.NewRTTStats(),
&wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2},
false,
)
@@ -375,7 +375,7 @@ func TestSessionResumption(t *testing.T) {
client, _, clientErr, server, _, serverErr := handshakeWithTLSConf(
t,
clientConf, serverConf,
&utils.RTTStats{}, &utils.RTTStats{},
utils.NewRTTStats(), utils.NewRTTStats(),
&wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2},
false,
)
@@ -389,8 +389,8 @@ func TestSessionResumption(t *testing.T) {
require.False(t, server.ConnectionState().DidResume)
require.False(t, client.ConnectionState().DidResume)
clientRTTStats := &utils.RTTStats{}
serverRTTStats := &utils.RTTStats{}
clientRTTStats := utils.NewRTTStats()
serverRTTStats := utils.NewRTTStats()
client, _, clientErr, server, _, serverErr = handshakeWithTLSConf(
t,
clientConf, serverConf,
@@ -416,7 +416,7 @@ func TestSessionResumptionDisabled(t *testing.T) {
client, _, clientErr, server, _, serverErr := handshakeWithTLSConf(
t,
clientConf, serverConf,
&utils.RTTStats{}, &utils.RTTStats{},
utils.NewRTTStats(), utils.NewRTTStats(),
&wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2},
false,
)
@@ -434,7 +434,7 @@ func TestSessionResumptionDisabled(t *testing.T) {
client, _, clientErr, server, _, serverErr = handshakeWithTLSConf(
t,
clientConf, serverConf,
&utils.RTTStats{}, &utils.RTTStats{},
utils.NewRTTStats(), utils.NewRTTStats(),
&wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2},
false,
)
@@ -457,7 +457,7 @@ func Test0RTT(t *testing.T) {
client, _, clientErr, server, _, serverErr := handshakeWithTLSConf(
t,
clientConf, serverConf,
&utils.RTTStats{}, &utils.RTTStats{},
utils.NewRTTStats(), utils.NewRTTStats(),
&wire.TransportParameters{ActiveConnectionIDLimit: 2},
&wire.TransportParameters{ActiveConnectionIDLimit: 2, InitialMaxData: initialMaxData},
true,
@@ -475,7 +475,7 @@ func Test0RTT(t *testing.T) {
client, clientEvents, clientErr, server, serverEvents, serverErr := handshakeWithTLSConf(
t,
clientConf, serverConf,
&utils.RTTStats{}, &utils.RTTStats{},
utils.NewRTTStats(), utils.NewRTTStats(),
&wire.TransportParameters{ActiveConnectionIDLimit: 2},
&wire.TransportParameters{ActiveConnectionIDLimit: 2, InitialMaxData: initialMaxData},
true,
@@ -520,7 +520,7 @@ func Test0RTTRejectionOnTransportParametersChanged(t *testing.T) {
client, _, clientErr, server, _, serverErr := handshakeWithTLSConf(
t,
clientConf, serverConf,
&utils.RTTStats{}, &utils.RTTStats{},
utils.NewRTTStats(), utils.NewRTTStats(),
&wire.TransportParameters{ActiveConnectionIDLimit: 2},
&wire.TransportParameters{ActiveConnectionIDLimit: 2, InitialMaxData: initialMaxData},
true,
@@ -535,11 +535,11 @@ func Test0RTTRejectionOnTransportParametersChanged(t *testing.T) {
require.False(t, server.ConnectionState().DidResume)
require.False(t, client.ConnectionState().DidResume)
clientRTTStats := &utils.RTTStats{}
clientRTTStats := utils.NewRTTStats()
client, clientEvents, clientErr, server, _, serverErr := handshakeWithTLSConf(
t,
clientConf, serverConf,
clientRTTStats, &utils.RTTStats{},
clientRTTStats, utils.NewRTTStats(),
&wire.TransportParameters{ActiveConnectionIDLimit: 2},
&wire.TransportParameters{ActiveConnectionIDLimit: 2, InitialMaxData: initialMaxData - 1},
true,

View File

@@ -35,7 +35,7 @@ func setupEndpoints(t *testing.T, serverRTTStats *utils.RTTStats) (client, serve
rand.Read(trafficSecret1)
rand.Read(trafficSecret2)
client = newUpdatableAEAD(&utils.RTTStats{}, nil, utils.DefaultLogger, protocol.Version1)
client = newUpdatableAEAD(utils.NewRTTStats(), nil, utils.DefaultLogger, protocol.Version1)
server = newUpdatableAEAD(serverRTTStats, &eventRecorder, utils.DefaultLogger, protocol.Version1)
client.SetReadKey(cs, trafficSecret2)
client.SetWriteKey(cs, trafficSecret1)
@@ -97,7 +97,7 @@ func TestChaChaTestVector(t *testing.T) {
for _, tc := range testCases {
t.Run(fmt.Sprintf("QUIC %s", tc.version), func(t *testing.T) {
secret := splitHexString(t, "9ac312a7f877468ebe69422748ad00a1 5443f18203a07d6060f688f30f21632b")
aead := newUpdatableAEAD(&utils.RTTStats{}, nil, nil, tc.version)
aead := newUpdatableAEAD(utils.NewRTTStats(), nil, nil, tc.version)
chacha := cipherSuites[2]
require.Equal(t, tls.TLS_CHACHA20_POLY1305_SHA256, chacha.ID)
aead.SetWriteKey(chacha, secret)
@@ -123,8 +123,8 @@ func TestUpdatableAEADHeaderProtection(t *testing.T) {
rand.Read(trafficSecret1)
rand.Read(trafficSecret2)
client := newUpdatableAEAD(&utils.RTTStats{}, nil, utils.DefaultLogger, v)
server := newUpdatableAEAD(&utils.RTTStats{}, nil, utils.DefaultLogger, v)
client := newUpdatableAEAD(utils.NewRTTStats(), nil, utils.DefaultLogger, v)
server := newUpdatableAEAD(utils.NewRTTStats(), nil, utils.DefaultLogger, v)
client.SetReadKey(cs, trafficSecret2)
client.SetWriteKey(cs, trafficSecret1)
server.SetReadKey(cs, trafficSecret1)
@@ -188,7 +188,7 @@ func TestUpdatableAEADEncryptDecryptMessage(t *testing.T) {
}
func TestUpdatableAEADPacketNumbers(t *testing.T) {
client, server, _ := setupEndpoints(t, &utils.RTTStats{})
client, server, _ := setupEndpoints(t, utils.NewRTTStats())
msg := []byte("Lorem ipsum")
ad := []byte("Donec in velit neque.")
@@ -208,7 +208,7 @@ func TestUpdatableAEADPacketNumbers(t *testing.T) {
}
func TestAEADLimitReached(t *testing.T) {
client, _, _ := setupEndpoints(t, &utils.RTTStats{})
client, _, _ := setupEndpoints(t, utils.NewRTTStats())
client.invalidPacketLimit = 10
for i := 0; i < 9; i++ {
_, err := client.Open(nil, []byte("foobar"), monotime.Now(), protocol.PacketNumber(i), protocol.KeyPhaseZero, []byte("ad"))
@@ -222,7 +222,7 @@ func TestAEADLimitReached(t *testing.T) {
}
func TestKeyUpdates(t *testing.T) {
client, server, _ := setupEndpoints(t, &utils.RTTStats{})
client, server, _ := setupEndpoints(t, utils.NewRTTStats())
now := monotime.Now()
require.Equal(t, protocol.KeyPhaseZero, server.KeyPhase())
@@ -277,7 +277,7 @@ func TestKeyUpdates(t *testing.T) {
// }
func TestReorderedPacketAfterKeyUpdate(t *testing.T) {
client, server, eventRecorder := setupEndpoints(t, &utils.RTTStats{})
client, server, eventRecorder := setupEndpoints(t, utils.NewRTTStats())
now := monotime.Now()
encrypted01 := client.Seal(nil, []byte(msg), 0x42, []byte(ad))
@@ -305,8 +305,8 @@ func TestReorderedPacketAfterKeyUpdate(t *testing.T) {
}
func TestDropsKeys3PTOsAfterKeyUpdate(t *testing.T) {
var rttStats utils.RTTStats
client, server, eventRecorder := setupEndpoints(t, &rttStats)
rttStats := utils.NewRTTStats()
client, server, eventRecorder := setupEndpoints(t, rttStats)
now := monotime.Now()
rttStats.UpdateRTT(10*time.Millisecond, 0)
@@ -339,7 +339,7 @@ func TestDropsKeys3PTOsAfterKeyUpdate(t *testing.T) {
}
func TestAllowsFirstKeyUpdateImmediately(t *testing.T) {
client, server, serverTracer := setupEndpoints(t, &utils.RTTStats{})
client, server, serverTracer := setupEndpoints(t, utils.NewRTTStats())
client.rollKeys()
encrypted := client.Seal(nil, []byte(msg), 0x1337, []byte(ad))
@@ -357,7 +357,7 @@ func TestAllowsFirstKeyUpdateImmediately(t *testing.T) {
}
func TestRejectFrequentKeyUpdates(t *testing.T) {
client, server, _ := setupEndpoints(t, &utils.RTTStats{})
client, server, _ := setupEndpoints(t, utils.NewRTTStats())
server.rollKeys()
client.rollKeys()
@@ -389,7 +389,7 @@ func TestInitiateKeyUpdateAfterSendingMaxPackets(t *testing.T) {
const keyUpdateInterval = 20
setKeyUpdateIntervals(t, firstKeyUpdateInterval, keyUpdateInterval)
client, server, eventRecorder := setupEndpoints(t, &utils.RTTStats{})
client, server, eventRecorder := setupEndpoints(t, utils.NewRTTStats())
server.SetHandshakeConfirmed()
var pn protocol.PacketNumber
@@ -437,7 +437,7 @@ func TestKeyUpdateEnforceACKKeyPhase(t *testing.T) {
const firstKeyUpdateInterval = 5
setKeyUpdateIntervals(t, firstKeyUpdateInterval, protocol.KeyUpdateInterval)
_, server, eventRecorder := setupEndpoints(t, &utils.RTTStats{})
_, server, eventRecorder := setupEndpoints(t, utils.NewRTTStats())
server.SetHandshakeConfirmed()
// First make sure that we update our keys.
@@ -479,7 +479,7 @@ func TestKeyUpdateAfterOpeningMaxPackets(t *testing.T) {
const keyUpdateInterval = 20
setKeyUpdateIntervals(t, firstKeyUpdateInterval, keyUpdateInterval)
client, server, eventRecorder := setupEndpoints(t, &utils.RTTStats{})
client, server, eventRecorder := setupEndpoints(t, utils.NewRTTStats())
server.SetHandshakeConfirmed()
msg := []byte("message")
@@ -532,9 +532,9 @@ func TestKeyUpdateKeyPhaseSkipping(t *testing.T) {
const keyUpdateInterval = 20
setKeyUpdateIntervals(t, firstKeyUpdateInterval, keyUpdateInterval)
var rttStats utils.RTTStats
rttStats := utils.NewRTTStats()
rttStats.UpdateRTT(10*time.Millisecond, 0)
client, server, eventRecorder := setupEndpoints(t, &rttStats)
client, server, eventRecorder := setupEndpoints(t, rttStats)
server.SetHandshakeConfirmed()
now := monotime.Now()
@@ -567,7 +567,7 @@ func TestFastKeyUpdatesByPeer(t *testing.T) {
const keyUpdateInterval = 20
setKeyUpdateIntervals(t, firstKeyUpdateInterval, keyUpdateInterval)
client, server, eventRecorder := setupEndpoints(t, &utils.RTTStats{})
client, server, eventRecorder := setupEndpoints(t, utils.NewRTTStats())
server.SetHandshakeConfirmed()
var pn protocol.PacketNumber
@@ -618,9 +618,9 @@ func TestFastKeyUpdateByUs(t *testing.T) {
const keyUpdateInterval = 20
setKeyUpdateIntervals(t, firstKeyUpdateInterval, keyUpdateInterval)
var rttStats utils.RTTStats
rttStats := utils.NewRTTStats()
rttStats.UpdateRTT(10*time.Millisecond, 0)
client, server, eventRecorder := setupEndpoints(t, &rttStats)
client, server, eventRecorder := setupEndpoints(t, rttStats)
server.SetHandshakeConfirmed()
// send so many packets that we initiate the first key update
@@ -677,9 +677,9 @@ func getClientAndServer() (client, server *updatableAEAD) {
rand.Read(trafficSecret2)
cs := cipherSuites[0]
var rttStats utils.RTTStats
client = newUpdatableAEAD(&rttStats, nil, utils.DefaultLogger, protocol.Version1)
server = newUpdatableAEAD(&rttStats, nil, utils.DefaultLogger, protocol.Version1)
rttStats := utils.NewRTTStats()
client = newUpdatableAEAD(rttStats, nil, utils.DefaultLogger, protocol.Version1)
server = newUpdatableAEAD(rttStats, nil, utils.DefaultLogger, protocol.Version1)
client.SetReadKey(cs, trafficSecret2)
client.SetWriteKey(cs, trafficSecret1)
server.SetReadKey(cs, trafficSecret1)

View File

@@ -12,10 +12,11 @@ const (
oneMinusAlpha = 1 - rttAlpha
rttBeta = 0.25
oneMinusBeta = 1 - rttBeta
// The default RTT used before an RTT sample is taken.
defaultInitialRTT = 100 * time.Millisecond
)
// The default RTT used before an RTT sample is taken
const DefaultInitialRTT = 100 * time.Millisecond
// RTTStats provides round-trip statistics
type RTTStats struct {
hasMeasurement bool
@@ -28,6 +29,14 @@ type RTTStats struct {
maxAckDelay atomic.Int64 // nanoseconds
}
func NewRTTStats() *RTTStats {
var rttStats RTTStats
rttStats.minRTT.Store(DefaultInitialRTT.Nanoseconds())
rttStats.latestRTT.Store(DefaultInitialRTT.Nanoseconds())
rttStats.smoothedRTT.Store(DefaultInitialRTT.Nanoseconds())
return &rttStats
}
// MinRTT Returns the minRTT for the entire connection.
// May return Zero if no valid updates have occurred.
func (r *RTTStats) MinRTT() time.Duration {
@@ -58,8 +67,8 @@ func (r *RTTStats) MaxAckDelay() time.Duration {
// PTO gets the probe timeout duration.
func (r *RTTStats) PTO(includeMaxAckDelay bool) time.Duration {
if r.SmoothedRTT() == 0 {
return 2 * defaultInitialRTT
if !r.hasMeasurement {
return 2 * DefaultInitialRTT
}
pto := r.SmoothedRTT() + max(4*r.MeanDeviation(), protocol.TimerGranularity)
if includeMaxAckDelay {
@@ -79,9 +88,9 @@ func (r *RTTStats) UpdateRTT(sendDelta, ackDelay time.Duration) {
// the client may cause a high ackDelay to result in underestimation of the
// r.minRTT.
minRTT := time.Duration(r.minRTT.Load())
if minRTT == 0 || minRTT > sendDelta {
if !r.hasMeasurement || minRTT > sendDelta {
minRTT = sendDelta
r.minRTT.Store(int64(sendDelta))
r.minRTT.Store(sendDelta.Nanoseconds())
}
// Correct for ackDelay if information received from the peer results in a
@@ -91,18 +100,18 @@ func (r *RTTStats) UpdateRTT(sendDelta, ackDelay time.Duration) {
if sample-minRTT >= ackDelay {
sample -= ackDelay
}
r.latestRTT.Store(int64(sample))
r.latestRTT.Store(sample.Nanoseconds())
// First time call.
if !r.hasMeasurement {
r.hasMeasurement = true
r.smoothedRTT.Store(int64(sample))
r.meanDeviation.Store(int64(sample / 2))
r.smoothedRTT.Store(sample.Nanoseconds())
r.meanDeviation.Store(sample.Nanoseconds() / 2)
} else {
smoothedRTT := r.SmoothedRTT()
meanDev := time.Duration(oneMinusBeta*float32(r.MeanDeviation()/time.Microsecond)+rttBeta*float32((smoothedRTT-sample).Abs()/time.Microsecond)) * time.Microsecond
newSmoothedRTT := time.Duration((float32(smoothedRTT/time.Microsecond)*oneMinusAlpha)+(float32(sample/time.Microsecond)*rttAlpha)) * time.Microsecond
r.meanDeviation.Store(int64(meanDev))
r.smoothedRTT.Store(int64(newSmoothedRTT))
r.meanDeviation.Store(meanDev.Nanoseconds())
r.smoothedRTT.Store(newSmoothedRTT.Nanoseconds())
}
}
@@ -127,9 +136,9 @@ func (r *RTTStats) SetInitialRTT(t time.Duration) {
func (r *RTTStats) ResetForPathMigration() {
r.hasMeasurement = false
r.minRTT.Store(0)
r.latestRTT.Store(0)
r.smoothedRTT.Store(0)
r.minRTT.Store(DefaultInitialRTT.Nanoseconds())
r.latestRTT.Store(DefaultInitialRTT.Nanoseconds())
r.smoothedRTT.Store(DefaultInitialRTT.Nanoseconds())
r.meanDeviation.Store(0)
// max_ack_delay remains valid
}

View File

@@ -9,13 +9,13 @@ import (
)
func TestRTTStatsDefaults(t *testing.T) {
var rttStats RTTStats
require.Zero(t, rttStats.MinRTT())
require.Zero(t, rttStats.SmoothedRTT())
rttStats := NewRTTStats()
require.Equal(t, DefaultInitialRTT, rttStats.MinRTT())
require.Equal(t, DefaultInitialRTT, rttStats.SmoothedRTT())
}
func TestRTTStatsSmoothedRTT(t *testing.T) {
var rttStats RTTStats
rttStats := NewRTTStats()
// verify that ack_delay is ignored in the first measurement
rttStats.UpdateRTT(300*time.Millisecond, 100*time.Millisecond)
require.Equal(t, 300*time.Millisecond, rttStats.LatestRTT())
@@ -31,7 +31,7 @@ func TestRTTStatsSmoothedRTT(t *testing.T) {
}
func TestRTTStatsMinRTT(t *testing.T) {
var rttStats RTTStats
rttStats := NewRTTStats()
rttStats.UpdateRTT(200*time.Millisecond, 0)
require.Equal(t, 200*time.Millisecond, rttStats.MinRTT())
rttStats.UpdateRTT(10*time.Millisecond, 0)
@@ -48,7 +48,7 @@ func TestRTTStatsMinRTT(t *testing.T) {
}
func TestRTTStatsMaxAckDelay(t *testing.T) {
var rttStats RTTStats
rttStats := NewRTTStats()
rttStats.SetMaxAckDelay(42 * time.Minute)
require.Equal(t, 42*time.Minute, rttStats.MaxAckDelay())
}
@@ -58,7 +58,7 @@ func TestRTTStatsComputePTO(t *testing.T) {
maxAckDelay = 42 * time.Minute
rtt = time.Second
)
var rttStats RTTStats
rttStats := NewRTTStats()
rttStats.SetMaxAckDelay(maxAckDelay)
rttStats.UpdateRTT(rtt, 0)
require.Equal(t, rtt, rttStats.SmoothedRTT())
@@ -69,13 +69,13 @@ func TestRTTStatsComputePTO(t *testing.T) {
func TestRTTStatsPTOWithShortRTT(t *testing.T) {
const rtt = time.Microsecond
var rttStats RTTStats
rttStats := NewRTTStats()
rttStats.UpdateRTT(rtt, 0)
require.Equal(t, rtt+protocol.TimerGranularity, rttStats.PTO(true))
}
func TestRTTStatsUpdateWithBadSendDeltas(t *testing.T) {
var rttStats RTTStats
rttStats := NewRTTStats()
const initialRtt = 10 * time.Millisecond
rttStats.UpdateRTT(initialRtt, 0)
require.Equal(t, initialRtt, rttStats.MinRTT())
@@ -94,7 +94,7 @@ func TestRTTStatsUpdateWithBadSendDeltas(t *testing.T) {
}
func TestRTTStatsRestore(t *testing.T) {
var rttStats RTTStats
rttStats := NewRTTStats()
rttStats.SetInitialRTT(10 * time.Second)
require.Equal(t, 10*time.Second, rttStats.LatestRTT())
require.Equal(t, 10*time.Second, rttStats.SmoothedRTT())
@@ -107,7 +107,7 @@ func TestRTTStatsRestore(t *testing.T) {
}
func TestRTTMeasurementAfterRestore(t *testing.T) {
var rttStats RTTStats
rttStats := NewRTTStats()
const rtt = 10 * time.Millisecond
rttStats.UpdateRTT(rtt, 0)
require.Equal(t, rtt, rttStats.LatestRTT())
@@ -118,7 +118,7 @@ func TestRTTMeasurementAfterRestore(t *testing.T) {
}
func TestRTTStatsResetForPathMigration(t *testing.T) {
var rttStats RTTStats
rttStats := NewRTTStats()
rttStats.SetMaxAckDelay(42 * time.Millisecond)
rttStats.UpdateRTT(time.Second, 0)
rttStats.UpdateRTT(10*time.Second, 0)
@@ -127,10 +127,10 @@ func TestRTTStatsResetForPathMigration(t *testing.T) {
require.NotZero(t, rttStats.SmoothedRTT())
rttStats.ResetForPathMigration()
require.Zero(t, rttStats.MinRTT())
require.Zero(t, rttStats.LatestRTT())
require.Zero(t, rttStats.SmoothedRTT())
require.Equal(t, 2*defaultInitialRTT, rttStats.PTO(false))
require.Equal(t, DefaultInitialRTT, rttStats.MinRTT())
require.Equal(t, DefaultInitialRTT, rttStats.LatestRTT())
require.Equal(t, DefaultInitialRTT, rttStats.SmoothedRTT())
require.Equal(t, 2*DefaultInitialRTT, rttStats.PTO(false))
// make sure that max_ack_delay was not reset
require.Equal(t, 42*time.Millisecond, rttStats.MaxAckDelay())