forked from quic-go/quic-go
Merge pull request #1892 from lucas-clemente/max-ack-delay
send and receive the max_ack_delay, use it for RTT estimation and PTO calculation
This commit is contained in:
@@ -352,6 +352,7 @@ func (c *client) createNewTLSSession(version protocol.VersionNumber) error {
|
||||
IdleTimeout: c.config.IdleTimeout,
|
||||
MaxBidiStreams: uint64(c.config.MaxIncomingStreams),
|
||||
MaxUniStreams: uint64(c.config.MaxIncomingUniStreams),
|
||||
MaxAckDelay: protocol.MaxAckDelayInclGranularity,
|
||||
AckDelayExponent: protocol.AckDelayExponent,
|
||||
DisableMigration: true,
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ type SentPacketHandler interface {
|
||||
SentPacket(packet *Packet)
|
||||
SentPacketsAsRetransmission(packets []*Packet, retransmissionOf protocol.PacketNumber)
|
||||
ReceivedAck(ackFrame *wire.AckFrame, withPacketNumber protocol.PacketNumber, encLevel protocol.EncryptionLevel, recvTime time.Time) error
|
||||
SetMaxAckDelay(time.Duration)
|
||||
SetHandshakeComplete()
|
||||
ResetForRetry() error
|
||||
|
||||
|
||||
@@ -11,8 +11,6 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// maximum delay that can be applied to an ACK for an ack-eliciting packet
|
||||
ackSendDelay = 25 * time.Millisecond
|
||||
// initial maximum number of ack-eliciting packets received before sending an ack.
|
||||
initialAckElicitingPacketsBeforeAck = 2
|
||||
// number of ack-eliciting that an ACK is sent for
|
||||
@@ -85,14 +83,21 @@ func (h *receivedPacketHandler) GetAlarmTimeout() time.Time {
|
||||
}
|
||||
|
||||
func (h *receivedPacketHandler) GetAckFrame(encLevel protocol.EncryptionLevel) *wire.AckFrame {
|
||||
var ack *wire.AckFrame
|
||||
switch encLevel {
|
||||
case protocol.EncryptionInitial:
|
||||
return h.initialPackets.GetAckFrame()
|
||||
ack = h.initialPackets.GetAckFrame()
|
||||
case protocol.EncryptionHandshake:
|
||||
return h.handshakePackets.GetAckFrame()
|
||||
ack = h.handshakePackets.GetAckFrame()
|
||||
case protocol.Encryption1RTT:
|
||||
return h.oneRTTPackets.GetAckFrame()
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
// For Initial and Handshake ACKs, the delay time is ignored by the receiver.
|
||||
// Set it to 0 in order to save bytes.
|
||||
if ack != nil {
|
||||
ack.DelayTime = 0
|
||||
}
|
||||
return ack
|
||||
}
|
||||
|
||||
@@ -24,24 +24,27 @@ var _ = Describe("Received Packet Handler", func() {
|
||||
})
|
||||
|
||||
It("generates ACKs for different packet number spaces", func() {
|
||||
now := time.Now()
|
||||
Expect(handler.ReceivedPacket(2, protocol.EncryptionInitial, now, true)).To(Succeed())
|
||||
Expect(handler.ReceivedPacket(1, protocol.EncryptionHandshake, now, true)).To(Succeed())
|
||||
Expect(handler.ReceivedPacket(5, protocol.Encryption1RTT, now, true)).To(Succeed())
|
||||
Expect(handler.ReceivedPacket(3, protocol.EncryptionInitial, now, true)).To(Succeed())
|
||||
Expect(handler.ReceivedPacket(2, protocol.EncryptionHandshake, now, true)).To(Succeed())
|
||||
Expect(handler.ReceivedPacket(4, protocol.Encryption1RTT, now, true)).To(Succeed())
|
||||
sendTime := time.Now().Add(-time.Second)
|
||||
Expect(handler.ReceivedPacket(2, protocol.EncryptionInitial, sendTime, true)).To(Succeed())
|
||||
Expect(handler.ReceivedPacket(1, protocol.EncryptionHandshake, sendTime, true)).To(Succeed())
|
||||
Expect(handler.ReceivedPacket(5, protocol.Encryption1RTT, sendTime, true)).To(Succeed())
|
||||
Expect(handler.ReceivedPacket(3, protocol.EncryptionInitial, sendTime, true)).To(Succeed())
|
||||
Expect(handler.ReceivedPacket(2, protocol.EncryptionHandshake, sendTime, true)).To(Succeed())
|
||||
Expect(handler.ReceivedPacket(4, protocol.Encryption1RTT, sendTime, true)).To(Succeed())
|
||||
initialAck := handler.GetAckFrame(protocol.EncryptionInitial)
|
||||
Expect(initialAck).ToNot(BeNil())
|
||||
Expect(initialAck.AckRanges).To(HaveLen(1))
|
||||
Expect(initialAck.AckRanges[0]).To(Equal(wire.AckRange{Smallest: 2, Largest: 3}))
|
||||
Expect(initialAck.DelayTime).To(BeZero())
|
||||
handshakeAck := handler.GetAckFrame(protocol.EncryptionHandshake)
|
||||
Expect(handshakeAck).ToNot(BeNil())
|
||||
Expect(handshakeAck.AckRanges).To(HaveLen(1))
|
||||
Expect(handshakeAck.AckRanges[0]).To(Equal(wire.AckRange{Smallest: 1, Largest: 2}))
|
||||
Expect(handshakeAck.DelayTime).To(BeZero())
|
||||
oneRTTAck := handler.GetAckFrame(protocol.Encryption1RTT)
|
||||
Expect(oneRTTAck).ToNot(BeNil())
|
||||
Expect(oneRTTAck.AckRanges).To(HaveLen(1))
|
||||
Expect(oneRTTAck.AckRanges[0]).To(Equal(wire.AckRange{Smallest: 4, Largest: 5}))
|
||||
Expect(oneRTTAck.DelayTime).To(BeNumerically("~", time.Second, 50*time.Millisecond))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -16,8 +16,8 @@ type receivedPacketTracker struct {
|
||||
|
||||
packetHistory *receivedPacketHistory
|
||||
|
||||
ackSendDelay time.Duration
|
||||
rttStats *congestion.RTTStats
|
||||
maxAckDelay time.Duration
|
||||
rttStats *congestion.RTTStats
|
||||
|
||||
packetsReceivedSinceLastAck int
|
||||
ackElicitingPacketsReceivedSinceLastAck int
|
||||
@@ -37,7 +37,7 @@ func newReceivedPacketTracker(
|
||||
) *receivedPacketTracker {
|
||||
return &receivedPacketTracker{
|
||||
packetHistory: newReceivedPacketHistory(),
|
||||
ackSendDelay: ackSendDelay,
|
||||
maxAckDelay: protocol.MaxAckDelay,
|
||||
rttStats: rttStats,
|
||||
logger: logger,
|
||||
version: version,
|
||||
@@ -126,7 +126,7 @@ func (h *receivedPacketTracker) maybeQueueAck(packetNumber protocol.PacketNumber
|
||||
}
|
||||
} else if h.ackAlarm.IsZero() {
|
||||
// wait for the minimum of the ack decimation delay or the delayed ack time before sending an ack
|
||||
ackDelay := utils.MinDuration(ackSendDelay, time.Duration(float64(h.rttStats.MinRTT())*float64(ackDecimationDelay)))
|
||||
ackDelay := utils.MinDuration(h.maxAckDelay, time.Duration(float64(h.rttStats.MinRTT())*float64(ackDecimationDelay)))
|
||||
h.ackAlarm = rcvTime.Add(ackDelay)
|
||||
if h.logger.Debug() {
|
||||
h.logger.Debugf("\tSetting ACK timer to min(1/4 min-RTT, max ack delay): %s (%s from now)", ackDelay, time.Until(h.ackAlarm))
|
||||
@@ -141,9 +141,9 @@ func (h *receivedPacketTracker) maybeQueueAck(packetNumber protocol.PacketNumber
|
||||
h.ackQueued = true
|
||||
} else if h.ackAlarm.IsZero() {
|
||||
if h.logger.Debug() {
|
||||
h.logger.Debugf("\tSetting ACK timer to max ack delay: %s", ackSendDelay)
|
||||
h.logger.Debugf("\tSetting ACK timer to max ack delay: %s", h.maxAckDelay)
|
||||
}
|
||||
h.ackAlarm = rcvTime.Add(ackSendDelay)
|
||||
h.ackAlarm = rcvTime.Add(h.maxAckDelay)
|
||||
}
|
||||
}
|
||||
// If there are new missing packets to report, set a short timer to send an ACK.
|
||||
|
||||
@@ -151,7 +151,7 @@ var _ = Describe("Received Packet Tracker", func() {
|
||||
err = tracker.ReceivedPacket(12, rcvTime, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(tracker.ackQueued).To(BeFalse())
|
||||
Expect(tracker.GetAlarmTimeout()).To(Equal(rcvTime.Add(ackSendDelay)))
|
||||
Expect(tracker.GetAlarmTimeout()).To(Equal(rcvTime.Add(protocol.MaxAckDelay)))
|
||||
})
|
||||
|
||||
It("queues an ACK if it was reported missing before", func() {
|
||||
|
||||
@@ -17,8 +17,6 @@ const (
|
||||
// Maximum reordering in time space before time based loss detection considers a packet lost.
|
||||
// Specified as an RTT multiplier.
|
||||
timeThreshold = 9.0 / 8
|
||||
// Timer granularity. The timer will not be set to a value smaller than granularity.
|
||||
granularity = time.Millisecond
|
||||
)
|
||||
|
||||
type packetNumberSpace struct {
|
||||
@@ -60,6 +58,7 @@ type sentPacketHandler struct {
|
||||
rttStats *congestion.RTTStats
|
||||
|
||||
handshakeComplete bool
|
||||
maxAckDelay time.Duration
|
||||
|
||||
// The number of times the crypto packets have been retransmitted without receiving an ack.
|
||||
cryptoCount uint32
|
||||
@@ -124,6 +123,10 @@ func (h *sentPacketHandler) SetHandshakeComplete() {
|
||||
h.handshakeComplete = true
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) SetMaxAckDelay(mad time.Duration) {
|
||||
h.maxAckDelay = mad
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) SentPacket(packet *Packet) {
|
||||
if isAckEliciting := h.sentPacketImpl(packet); isAckEliciting {
|
||||
h.getPacketNumberSpace(packet.EncryptionLevel).history.SentPacket(packet)
|
||||
@@ -207,7 +210,12 @@ func (h *sentPacketHandler) ReceivedAck(ackFrame *wire.AckFrame, withPacketNumbe
|
||||
|
||||
// maybe update the RTT
|
||||
if p := pnSpace.history.GetPacket(ackFrame.LargestAcked()); p != nil {
|
||||
h.rttStats.UpdateRTT(rcvTime.Sub(p.SendTime), ackFrame.DelayTime, rcvTime)
|
||||
// don't use the ack delay for Initial and Handshake packets
|
||||
var ackDelay time.Duration
|
||||
if encLevel == protocol.Encryption1RTT {
|
||||
ackDelay = utils.MinDuration(ackFrame.DelayTime, h.maxAckDelay)
|
||||
}
|
||||
h.rttStats.UpdateRTT(rcvTime.Sub(p.SendTime), ackDelay, rcvTime)
|
||||
if h.logger.Debug() {
|
||||
h.logger.Debugf("\tupdated RTT: %s (σ: %s)", h.rttStats.SmoothedRTT(), h.rttStats.MeanDeviation())
|
||||
}
|
||||
@@ -341,7 +349,7 @@ func (h *sentPacketHandler) detectLostPackets(
|
||||
lossDelay := time.Duration(timeThreshold * maxRTT)
|
||||
|
||||
// Minimum time of granularity before packets are deemed lost.
|
||||
lossDelay = utils.MaxDuration(lossDelay, granularity)
|
||||
lossDelay = utils.MaxDuration(lossDelay, protocol.TimerGranularity)
|
||||
|
||||
var lostPackets []*Packet
|
||||
pnSpace.history.Iterate(func(packet *Packet) (bool, error) {
|
||||
@@ -616,15 +624,14 @@ func (h *sentPacketHandler) queuePacketForRetransmission(p *Packet, pnSpace *pac
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) computeCryptoTimeout() time.Duration {
|
||||
duration := utils.MaxDuration(2*h.rttStats.SmoothedOrInitialRTT(), granularity)
|
||||
duration := utils.MaxDuration(2*h.rttStats.SmoothedOrInitialRTT(), protocol.TimerGranularity)
|
||||
// exponential backoff
|
||||
// There's an implicit limit to this set by the crypto timeout.
|
||||
return duration << h.cryptoCount
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) computePTOTimeout() time.Duration {
|
||||
// TODO(#1236): include the max_ack_delay
|
||||
duration := utils.MaxDuration(h.rttStats.SmoothedOrInitialRTT()+4*h.rttStats.MeanDeviation(), granularity)
|
||||
duration := h.rttStats.SmoothedOrInitialRTT() + utils.MaxDuration(4*h.rttStats.MeanDeviation(), protocol.TimerGranularity) + h.maxAckDelay
|
||||
return duration << h.ptoCount
|
||||
}
|
||||
|
||||
|
||||
@@ -292,19 +292,45 @@ var _ = Describe("SentPacketHandler", func() {
|
||||
Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 1*time.Minute, 1*time.Second))
|
||||
})
|
||||
|
||||
It("uses the DelayTime in the ACK frame", func() {
|
||||
now := time.Now()
|
||||
It("ignores the DelayTime for Initial and Handshake packets", func() {
|
||||
handler.SentPacket(cryptoPacket(&Packet{PacketNumber: 1}))
|
||||
handler.SetMaxAckDelay(time.Hour)
|
||||
// make sure the rttStats have a min RTT, so that the delay is used
|
||||
handler.rttStats.UpdateRTT(5*time.Minute, 0, time.Now())
|
||||
getPacket(1, protocol.Encryption1RTT).SendTime = now.Add(-10 * time.Minute)
|
||||
getPacket(1, protocol.EncryptionInitial).SendTime = time.Now().Add(-10 * time.Minute)
|
||||
ack := &wire.AckFrame{
|
||||
AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}},
|
||||
DelayTime: 5 * time.Minute,
|
||||
}
|
||||
err := handler.ReceivedAck(ack, 1, protocol.Encryption1RTT, time.Now())
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(handler.ReceivedAck(ack, 1, protocol.EncryptionInitial, time.Now())).To(Succeed())
|
||||
Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 10*time.Minute, 1*time.Second))
|
||||
})
|
||||
|
||||
It("uses the DelayTime in the ACK frame", func() {
|
||||
handler.SetMaxAckDelay(time.Hour)
|
||||
// make sure the rttStats have a min RTT, so that the delay is used
|
||||
handler.rttStats.UpdateRTT(5*time.Minute, 0, time.Now())
|
||||
getPacket(1, protocol.Encryption1RTT).SendTime = time.Now().Add(-10 * time.Minute)
|
||||
ack := &wire.AckFrame{
|
||||
AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}},
|
||||
DelayTime: 5 * time.Minute,
|
||||
}
|
||||
Expect(handler.ReceivedAck(ack, 1, protocol.Encryption1RTT, time.Now())).To(Succeed())
|
||||
Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 5*time.Minute, 1*time.Second))
|
||||
})
|
||||
|
||||
It("limits the DelayTime in the ACK frame to max_ack_delay", func() {
|
||||
handler.SetMaxAckDelay(time.Minute)
|
||||
// make sure the rttStats have a min RTT, so that the delay is used
|
||||
handler.rttStats.UpdateRTT(5*time.Minute, 0, time.Now())
|
||||
getPacket(1, protocol.Encryption1RTT).SendTime = time.Now().Add(-10 * time.Minute)
|
||||
ack := &wire.AckFrame{
|
||||
AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}},
|
||||
DelayTime: 5 * time.Minute,
|
||||
}
|
||||
Expect(handler.ReceivedAck(ack, 1, protocol.Encryption1RTT, time.Now())).To(Succeed())
|
||||
Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 9*time.Minute, 1*time.Second))
|
||||
})
|
||||
})
|
||||
|
||||
Context("determining which ACKs we have received an ACK for", func() {
|
||||
@@ -593,7 +619,7 @@ var _ = Describe("SentPacketHandler", func() {
|
||||
It("uses the granularity for short RTTs", func() {
|
||||
rtt := time.Microsecond
|
||||
updateRTT(rtt)
|
||||
Expect(handler.computePTOTimeout()).To(Equal(granularity))
|
||||
Expect(handler.computePTOTimeout()).To(Equal(rtt + protocol.TimerGranularity))
|
||||
})
|
||||
|
||||
It("implements exponential backoff", func() {
|
||||
|
||||
@@ -31,9 +31,10 @@ var _ = Describe("Transport Parameters", func() {
|
||||
IdleTimeout: 42 * time.Second,
|
||||
OriginalConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef},
|
||||
AckDelayExponent: 14,
|
||||
MaxAckDelay: 37 * time.Millisecond,
|
||||
StatelessResetToken: &[16]byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00},
|
||||
}
|
||||
Expect(p.String()).To(Equal("&handshake.TransportParameters{OriginalConnectionID: 0xdeadbeef, InitialMaxStreamDataBidiLocal: 0x1234, InitialMaxStreamDataBidiRemote: 0x2345, InitialMaxStreamDataUni: 0x3456, InitialMaxData: 0x4567, MaxBidiStreams: 1337, MaxUniStreams: 7331, IdleTimeout: 42s, AckDelayExponent: 14, StatelessResetToken: 0x112233445566778899aabbccddeeff00}"))
|
||||
Expect(p.String()).To(Equal("&handshake.TransportParameters{OriginalConnectionID: 0xdeadbeef, InitialMaxStreamDataBidiLocal: 0x1234, InitialMaxStreamDataBidiRemote: 0x2345, InitialMaxStreamDataUni: 0x3456, InitialMaxData: 0x4567, MaxBidiStreams: 1337, MaxUniStreams: 7331, IdleTimeout: 42s, AckDelayExponent: 14, MaxAckDelay: 37ms, StatelessResetToken: 0x112233445566778899aabbccddeeff00}"))
|
||||
})
|
||||
|
||||
It("has a string representation, if there's no stateless reset token", func() {
|
||||
@@ -47,17 +48,18 @@ var _ = Describe("Transport Parameters", func() {
|
||||
IdleTimeout: 42 * time.Second,
|
||||
OriginalConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef},
|
||||
AckDelayExponent: 14,
|
||||
MaxAckDelay: 37 * time.Second,
|
||||
}
|
||||
Expect(p.String()).To(Equal("&handshake.TransportParameters{OriginalConnectionID: 0xdeadbeef, InitialMaxStreamDataBidiLocal: 0x1234, InitialMaxStreamDataBidiRemote: 0x2345, InitialMaxStreamDataUni: 0x3456, InitialMaxData: 0x4567, MaxBidiStreams: 1337, MaxUniStreams: 7331, IdleTimeout: 42s, AckDelayExponent: 14}"))
|
||||
Expect(p.String()).To(Equal("&handshake.TransportParameters{OriginalConnectionID: 0xdeadbeef, InitialMaxStreamDataBidiLocal: 0x1234, InitialMaxStreamDataBidiRemote: 0x2345, InitialMaxStreamDataUni: 0x3456, InitialMaxData: 0x4567, MaxBidiStreams: 1337, MaxUniStreams: 7331, IdleTimeout: 42s, AckDelayExponent: 14, MaxAckDelay: 37s}"))
|
||||
})
|
||||
|
||||
getRandomValue := func() uint64 {
|
||||
maxVals := []int64{math.MaxUint8 / 4, math.MaxUint16 / 4, math.MaxUint32 / 4, math.MaxUint64 / 4}
|
||||
rand.Seed(GinkgoRandomSeed())
|
||||
return uint64(rand.Int63n(maxVals[int(rand.Int31n(4))]))
|
||||
}
|
||||
|
||||
It("marshals and unmarshals", func() {
|
||||
rand.Seed(GinkgoRandomSeed())
|
||||
getRandomValue := func() uint64 {
|
||||
maxVals := []int64{math.MaxUint8 / 4, math.MaxUint16 / 4, math.MaxUint32 / 4, math.MaxUint64 / 4}
|
||||
return uint64(rand.Int63n(maxVals[int(rand.Int31n(4))]))
|
||||
}
|
||||
|
||||
var token [16]byte
|
||||
rand.Read(token[:])
|
||||
params := &TransportParameters{
|
||||
@@ -72,6 +74,7 @@ var _ = Describe("Transport Parameters", func() {
|
||||
StatelessResetToken: &token,
|
||||
OriginalConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef},
|
||||
AckDelayExponent: 13,
|
||||
MaxAckDelay: 42 * time.Millisecond,
|
||||
}
|
||||
data := params.Marshal()
|
||||
|
||||
@@ -88,6 +91,7 @@ var _ = Describe("Transport Parameters", func() {
|
||||
Expect(p.StatelessResetToken).To(Equal(params.StatelessResetToken))
|
||||
Expect(p.OriginalConnectionID).To(Equal(protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef}))
|
||||
Expect(p.AckDelayExponent).To(Equal(uint8(13)))
|
||||
Expect(p.MaxAckDelay).To(Equal(42 * time.Millisecond))
|
||||
})
|
||||
|
||||
It("errors if the transport parameters are too short to contain the length", func() {
|
||||
@@ -128,6 +132,19 @@ var _ = Describe("Transport Parameters", func() {
|
||||
Expect(p.Unmarshal(prependLength(b.Bytes()), protocol.PerspectiveServer)).To(MatchError("wrong length for disable_migration: 6 (expected empty)"))
|
||||
})
|
||||
|
||||
It("errors when the max_ack_delay is too large", func() {
|
||||
data := (&TransportParameters{MaxAckDelay: 1 << 14 * time.Millisecond}).Marshal()
|
||||
p := &TransportParameters{}
|
||||
Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(MatchError("invalid value for max_ack_delay: 16384ms (maximum 16383ms)"))
|
||||
})
|
||||
|
||||
It("doesn't send the max_ack_delay, if it has the default value", func() {
|
||||
dataDefault := (&TransportParameters{MaxAckDelay: protocol.DefaultMaxAckDelay}).Marshal()
|
||||
defaultLen := len(dataDefault)
|
||||
data := (&TransportParameters{MaxAckDelay: protocol.DefaultMaxAckDelay + time.Millisecond}).Marshal()
|
||||
Expect(len(data)).To(Equal(defaultLen + 2 /* parameter ID */ + 2 /* length field */ + 1 /* value */))
|
||||
})
|
||||
|
||||
It("errors when the ack_delay_exponenent is too large", func() {
|
||||
data := (&TransportParameters{AckDelayExponent: 21}).Marshal()
|
||||
p := &TransportParameters{}
|
||||
|
||||
@@ -27,6 +27,7 @@ const (
|
||||
initialMaxStreamsBidiParameterID transportParameterID = 0x8
|
||||
initialMaxStreamsUniParameterID transportParameterID = 0x9
|
||||
ackDelayExponentParameterID transportParameterID = 0xa
|
||||
maxAckDelayParameterID transportParameterID = 0xb
|
||||
disableMigrationParameterID transportParameterID = 0xc
|
||||
)
|
||||
|
||||
@@ -37,6 +38,7 @@ type TransportParameters struct {
|
||||
InitialMaxStreamDataUni protocol.ByteCount
|
||||
InitialMaxData protocol.ByteCount
|
||||
|
||||
MaxAckDelay time.Duration
|
||||
AckDelayExponent uint8
|
||||
|
||||
MaxPacketSize protocol.ByteCount
|
||||
@@ -65,6 +67,7 @@ func (p *TransportParameters) Unmarshal(data []byte, sentBy protocol.Perspective
|
||||
var parameterIDs []transportParameterID
|
||||
|
||||
var readAckDelayExponent bool
|
||||
var readMaxAckDelay bool
|
||||
|
||||
r := bytes.NewReader(data[2:])
|
||||
for r.Len() >= 4 {
|
||||
@@ -75,7 +78,14 @@ func (p *TransportParameters) Unmarshal(data []byte, sentBy protocol.Perspective
|
||||
switch paramID {
|
||||
case ackDelayExponentParameterID:
|
||||
readAckDelayExponent = true
|
||||
fallthrough
|
||||
if err := p.readNumericTransportParameter(r, paramID, int(paramLen)); err != nil {
|
||||
return err
|
||||
}
|
||||
case maxAckDelayParameterID:
|
||||
readMaxAckDelay = true
|
||||
if err := p.readNumericTransportParameter(r, paramID, int(paramLen)); err != nil {
|
||||
return err
|
||||
}
|
||||
case initialMaxStreamDataBidiLocalParameterID,
|
||||
initialMaxStreamDataBidiRemoteParameterID,
|
||||
initialMaxStreamDataUniParameterID,
|
||||
@@ -121,6 +131,9 @@ func (p *TransportParameters) Unmarshal(data []byte, sentBy protocol.Perspective
|
||||
if !readAckDelayExponent {
|
||||
p.AckDelayExponent = protocol.DefaultAckDelayExponent
|
||||
}
|
||||
if !readMaxAckDelay {
|
||||
p.MaxAckDelay = protocol.DefaultMaxAckDelay
|
||||
}
|
||||
|
||||
// check that every transport parameter was sent at most once
|
||||
sort.Slice(parameterIDs, func(i, j int) bool { return parameterIDs[i] < parameterIDs[j] })
|
||||
@@ -174,6 +187,12 @@ func (p *TransportParameters) readNumericTransportParameter(
|
||||
return fmt.Errorf("invalid value for ack_delay_exponent: %d (maximum %d)", val, protocol.MaxAckDelayExponent)
|
||||
}
|
||||
p.AckDelayExponent = uint8(val)
|
||||
case maxAckDelayParameterID:
|
||||
maxAckDelay := time.Duration(val) * time.Millisecond
|
||||
if maxAckDelay >= protocol.MaxMaxAckDelay {
|
||||
return fmt.Errorf("invalid value for max_ack_delay: %dms (maximum %dms)", maxAckDelay/time.Millisecond, (protocol.MaxMaxAckDelay-time.Millisecond)/time.Millisecond)
|
||||
}
|
||||
p.MaxAckDelay = maxAckDelay
|
||||
default:
|
||||
return fmt.Errorf("TransportParameter BUG: transport parameter %d not found", paramID)
|
||||
}
|
||||
@@ -186,44 +205,30 @@ func (p *TransportParameters) Marshal() []byte {
|
||||
b.Write([]byte{0, 0}) // length. Will be replaced later
|
||||
|
||||
// initial_max_stream_data_bidi_local
|
||||
utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamDataBidiLocalParameterID))
|
||||
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.InitialMaxStreamDataBidiLocal))))
|
||||
utils.WriteVarInt(b, uint64(p.InitialMaxStreamDataBidiLocal))
|
||||
p.marshalVarintParam(b, initialMaxStreamDataBidiLocalParameterID, uint64(p.InitialMaxStreamDataBidiLocal))
|
||||
// initial_max_stream_data_bidi_remote
|
||||
utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamDataBidiRemoteParameterID))
|
||||
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.InitialMaxStreamDataBidiRemote))))
|
||||
utils.WriteVarInt(b, uint64(p.InitialMaxStreamDataBidiRemote))
|
||||
p.marshalVarintParam(b, initialMaxStreamDataBidiRemoteParameterID, uint64(p.InitialMaxStreamDataBidiRemote))
|
||||
// initial_max_stream_data_uni
|
||||
utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamDataUniParameterID))
|
||||
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.InitialMaxStreamDataUni))))
|
||||
utils.WriteVarInt(b, uint64(p.InitialMaxStreamDataUni))
|
||||
p.marshalVarintParam(b, initialMaxStreamDataUniParameterID, uint64(p.InitialMaxStreamDataUni))
|
||||
// initial_max_data
|
||||
utils.BigEndian.WriteUint16(b, uint16(initialMaxDataParameterID))
|
||||
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.InitialMaxData))))
|
||||
utils.WriteVarInt(b, uint64(p.InitialMaxData))
|
||||
p.marshalVarintParam(b, initialMaxDataParameterID, uint64(p.InitialMaxData))
|
||||
// initial_max_bidi_streams
|
||||
utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamsBidiParameterID))
|
||||
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(p.MaxBidiStreams)))
|
||||
utils.WriteVarInt(b, p.MaxBidiStreams)
|
||||
p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, p.MaxBidiStreams)
|
||||
// initial_max_uni_streams
|
||||
utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamsUniParameterID))
|
||||
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(p.MaxUniStreams)))
|
||||
utils.WriteVarInt(b, p.MaxUniStreams)
|
||||
p.marshalVarintParam(b, initialMaxStreamsUniParameterID, p.MaxUniStreams)
|
||||
// idle_timeout
|
||||
idleTimeout := uint64(p.IdleTimeout / time.Millisecond)
|
||||
utils.BigEndian.WriteUint16(b, uint16(idleTimeoutParameterID))
|
||||
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(idleTimeout)))
|
||||
utils.WriteVarInt(b, idleTimeout)
|
||||
p.marshalVarintParam(b, idleTimeoutParameterID, uint64(p.IdleTimeout/time.Millisecond))
|
||||
// max_packet_size
|
||||
utils.BigEndian.WriteUint16(b, uint16(maxPacketSizeParameterID))
|
||||
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(protocol.MaxReceivePacketSize))))
|
||||
utils.WriteVarInt(b, uint64(protocol.MaxReceivePacketSize))
|
||||
p.marshalVarintParam(b, maxPacketSizeParameterID, uint64(protocol.MaxReceivePacketSize))
|
||||
// max_ack_delay
|
||||
// Only send it if is different from the default value.
|
||||
if p.MaxAckDelay != protocol.DefaultMaxAckDelay {
|
||||
p.marshalVarintParam(b, maxAckDelayParameterID, uint64(p.MaxAckDelay/time.Millisecond))
|
||||
}
|
||||
// ack_delay_exponent
|
||||
// Only send it if is different from the default value.
|
||||
if p.AckDelayExponent != protocol.DefaultAckDelayExponent {
|
||||
utils.BigEndian.WriteUint16(b, uint16(ackDelayExponentParameterID))
|
||||
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.AckDelayExponent))))
|
||||
utils.WriteVarInt(b, uint64(p.AckDelayExponent))
|
||||
p.marshalVarintParam(b, ackDelayExponentParameterID, uint64(p.AckDelayExponent))
|
||||
}
|
||||
// disable_migration
|
||||
if p.DisableMigration {
|
||||
@@ -247,10 +252,16 @@ func (p *TransportParameters) Marshal() []byte {
|
||||
return data
|
||||
}
|
||||
|
||||
func (p *TransportParameters) marshalVarintParam(b *bytes.Buffer, id transportParameterID, val uint64) {
|
||||
utils.BigEndian.WriteUint16(b, uint16(id))
|
||||
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(val)))
|
||||
utils.WriteVarInt(b, val)
|
||||
}
|
||||
|
||||
// String returns a string representation, intended for logging.
|
||||
func (p *TransportParameters) String() string {
|
||||
logString := "&handshake.TransportParameters{OriginalConnectionID: %s, InitialMaxStreamDataBidiLocal: %#x, InitialMaxStreamDataBidiRemote: %#x, InitialMaxStreamDataUni: %#x, InitialMaxData: %#x, MaxBidiStreams: %d, MaxUniStreams: %d, IdleTimeout: %s, AckDelayExponent: %d"
|
||||
logParams := []interface{}{p.OriginalConnectionID, p.InitialMaxStreamDataBidiLocal, p.InitialMaxStreamDataBidiRemote, p.InitialMaxStreamDataUni, p.InitialMaxData, p.MaxBidiStreams, p.MaxUniStreams, p.IdleTimeout, p.AckDelayExponent}
|
||||
logString := "&handshake.TransportParameters{OriginalConnectionID: %s, InitialMaxStreamDataBidiLocal: %#x, InitialMaxStreamDataBidiRemote: %#x, InitialMaxStreamDataUni: %#x, InitialMaxData: %#x, MaxBidiStreams: %d, MaxUniStreams: %d, IdleTimeout: %s, AckDelayExponent: %d, MaxAckDelay: %s"
|
||||
logParams := []interface{}{p.OriginalConnectionID, p.InitialMaxStreamDataBidiLocal, p.InitialMaxStreamDataBidiRemote, p.InitialMaxStreamDataUni, p.InitialMaxData, p.MaxBidiStreams, p.MaxUniStreams, p.IdleTimeout, p.AckDelayExponent, p.MaxAckDelay}
|
||||
if p.StatelessResetToken != nil { // the client never sends a stateless reset token
|
||||
logString += ", StatelessResetToken: %#x"
|
||||
logParams = append(logParams, *p.StatelessResetToken)
|
||||
|
||||
@@ -215,6 +215,18 @@ func (mr *MockSentPacketHandlerMockRecorder) SetHandshakeComplete() *gomock.Call
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHandshakeComplete", reflect.TypeOf((*MockSentPacketHandler)(nil).SetHandshakeComplete))
|
||||
}
|
||||
|
||||
// SetMaxAckDelay mocks base method
|
||||
func (m *MockSentPacketHandler) SetMaxAckDelay(arg0 time.Duration) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "SetMaxAckDelay", arg0)
|
||||
}
|
||||
|
||||
// SetMaxAckDelay indicates an expected call of SetMaxAckDelay
|
||||
func (mr *MockSentPacketHandlerMockRecorder) SetMaxAckDelay(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetMaxAckDelay", reflect.TypeOf((*MockSentPacketHandler)(nil).SetMaxAckDelay), arg0)
|
||||
}
|
||||
|
||||
// ShouldSendNumPackets mocks base method
|
||||
func (m *MockSentPacketHandler) ShouldSendNumPackets() int {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
@@ -125,3 +125,14 @@ const DefaultConnectionIDLength = 4
|
||||
|
||||
// AckDelayExponent is the ack delay exponent used when sending ACKs.
|
||||
const AckDelayExponent = 3
|
||||
|
||||
// Estimated timer granularity.
|
||||
// The loss detection timer will not be set to a value smaller than granularity.
|
||||
const TimerGranularity = time.Millisecond
|
||||
|
||||
// MaxAckDelay is the maximum time by which we delay sending ACKs.
|
||||
const MaxAckDelay = 25 * time.Millisecond
|
||||
|
||||
// MaxAckDelayInclGranularity is the max_ack_delay including the timer granularity.
|
||||
// This is the value that should be advertised to the peer.
|
||||
const MaxAckDelayInclGranularity = MaxAckDelay + TimerGranularity
|
||||
|
||||
@@ -2,6 +2,7 @@ package protocol
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A PacketNumber in QUIC
|
||||
@@ -73,3 +74,9 @@ const DefaultAckDelayExponent = 3
|
||||
|
||||
// MaxAckDelayExponent is the maximum ack delay exponent
|
||||
const MaxAckDelayExponent = 20
|
||||
|
||||
// DefaultMaxAckDelay is the default max_ack_delay
|
||||
const DefaultMaxAckDelay = 25 * time.Millisecond
|
||||
|
||||
// MaxMaxAckDelay is the maximum max_ack_delay
|
||||
const MaxMaxAckDelay = 1 << 14 * time.Millisecond
|
||||
|
||||
@@ -442,6 +442,7 @@ func (s *server) createNewSession(
|
||||
IdleTimeout: s.config.IdleTimeout,
|
||||
MaxBidiStreams: uint64(s.config.MaxIncomingStreams),
|
||||
MaxUniStreams: uint64(s.config.MaxIncomingUniStreams),
|
||||
MaxAckDelay: protocol.MaxAckDelayInclGranularity,
|
||||
AckDelayExponent: protocol.AckDelayExponent,
|
||||
DisableMigration: true,
|
||||
StatelessResetToken: &token,
|
||||
|
||||
@@ -939,6 +939,7 @@ func (s *session) processTransportParameters(data []byte) {
|
||||
s.packer.HandleTransportParameters(params)
|
||||
s.frameParser.SetAckDelayExponent(params.AckDelayExponent)
|
||||
s.connFlowController.UpdateSendWindow(params.InitialMaxData)
|
||||
s.sentPacketHandler.SetMaxAckDelay(params.MaxAckDelay)
|
||||
if params.StatelessResetToken != nil {
|
||||
s.sessionRunner.AddResetToken(*params.StatelessResetToken, s)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user