85 lines
2.2 KiB
Go
85 lines
2.2 KiB
Go
|
package quic
|
||
|
|
||
|
import (
|
||
|
"time"
|
||
|
|
||
|
"github.com/lucas-clemente/quic-go/internal/ackhandler"
|
||
|
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||
|
"github.com/lucas-clemente/quic-go/internal/wire"
|
||
|
)
|
||
|
|
||
|
type mtuDiscoverer interface {
|
||
|
ShouldSendProbe(now time.Time) bool
|
||
|
NextProbeTime() time.Time
|
||
|
GetPing() (ping ackhandler.Frame, datagramSize protocol.ByteCount)
|
||
|
}
|
||
|
|
||
|
const (
|
||
|
// At some point, we have to stop searching for a higher MTU.
|
||
|
// We're happy to send a packet that's 10 bytes smaller than the actual MTU.
|
||
|
maxMTUDiff = 20
|
||
|
// send a probe packet every mtuProbeDelay RTTs
|
||
|
mtuProbeDelay = 5
|
||
|
)
|
||
|
|
||
|
type mtuFinder struct {
|
||
|
lastProbeTime time.Time
|
||
|
probeInFlight bool
|
||
|
mtuIncreased func(protocol.ByteCount)
|
||
|
|
||
|
rttStats *utils.RTTStats
|
||
|
current protocol.ByteCount
|
||
|
max protocol.ByteCount // the maximum value, as advertised by the peer (or our maximum size buffer)
|
||
|
}
|
||
|
|
||
|
var _ mtuDiscoverer = &mtuFinder{}
|
||
|
|
||
|
func newMTUDiscoverer(rttStats *utils.RTTStats, start, max protocol.ByteCount, mtuIncreased func(protocol.ByteCount)) mtuDiscoverer {
|
||
|
return &mtuFinder{
|
||
|
current: start,
|
||
|
rttStats: rttStats,
|
||
|
lastProbeTime: time.Now(), // to make sure the first probe packet is not sent immediately
|
||
|
mtuIncreased: mtuIncreased,
|
||
|
max: max,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (f *mtuFinder) done() bool {
|
||
|
return f.max-f.current <= maxMTUDiff+1
|
||
|
}
|
||
|
|
||
|
func (f *mtuFinder) ShouldSendProbe(now time.Time) bool {
|
||
|
if f.probeInFlight || f.done() {
|
||
|
return false
|
||
|
}
|
||
|
return !now.Before(f.NextProbeTime())
|
||
|
}
|
||
|
|
||
|
// NextProbeTime returns the time when the next probe packet should be sent.
|
||
|
// It returns the zero value if no probe packet should be sent.
|
||
|
func (f *mtuFinder) NextProbeTime() time.Time {
|
||
|
if f.probeInFlight || f.done() {
|
||
|
return time.Time{}
|
||
|
}
|
||
|
return f.lastProbeTime.Add(mtuProbeDelay * f.rttStats.SmoothedRTT())
|
||
|
}
|
||
|
|
||
|
func (f *mtuFinder) GetPing() (ackhandler.Frame, protocol.ByteCount) {
|
||
|
size := (f.max + f.current) / 2
|
||
|
f.lastProbeTime = time.Now()
|
||
|
f.probeInFlight = true
|
||
|
return ackhandler.Frame{
|
||
|
Frame: &wire.PingFrame{},
|
||
|
OnLost: func(wire.Frame) {
|
||
|
f.probeInFlight = false
|
||
|
f.max = size
|
||
|
},
|
||
|
OnAcked: func(wire.Frame) {
|
||
|
f.probeInFlight = false
|
||
|
f.current = size
|
||
|
f.mtuIncreased(size)
|
||
|
},
|
||
|
}, size
|
||
|
}
|