1package quic 2 3import ( 4 "time" 5 6 "github.com/lucas-clemente/quic-go/internal/ackhandler" 7 "github.com/lucas-clemente/quic-go/internal/protocol" 8 "github.com/lucas-clemente/quic-go/internal/utils" 9 "github.com/lucas-clemente/quic-go/internal/wire" 10) 11 12type mtuDiscoverer interface { 13 ShouldSendProbe(now time.Time) bool 14 NextProbeTime() time.Time 15 GetPing() (ping ackhandler.Frame, datagramSize protocol.ByteCount) 16} 17 18const ( 19 // At some point, we have to stop searching for a higher MTU. 20 // We're happy to send a packet that's 10 bytes smaller than the actual MTU. 21 maxMTUDiff = 20 22 // send a probe packet every mtuProbeDelay RTTs 23 mtuProbeDelay = 5 24) 25 26type mtuFinder struct { 27 lastProbeTime time.Time 28 probeInFlight bool 29 mtuIncreased func(protocol.ByteCount) 30 31 rttStats *utils.RTTStats 32 current protocol.ByteCount 33 max protocol.ByteCount // the maximum value, as advertised by the peer (or our maximum size buffer) 34} 35 36var _ mtuDiscoverer = &mtuFinder{} 37 38func newMTUDiscoverer(rttStats *utils.RTTStats, start, max protocol.ByteCount, mtuIncreased func(protocol.ByteCount)) mtuDiscoverer { 39 return &mtuFinder{ 40 current: start, 41 rttStats: rttStats, 42 lastProbeTime: time.Now(), // to make sure the first probe packet is not sent immediately 43 mtuIncreased: mtuIncreased, 44 max: max, 45 } 46} 47 48func (f *mtuFinder) done() bool { 49 return f.max-f.current <= maxMTUDiff+1 50} 51 52func (f *mtuFinder) ShouldSendProbe(now time.Time) bool { 53 if f.probeInFlight || f.done() { 54 return false 55 } 56 return !now.Before(f.NextProbeTime()) 57} 58 59// NextProbeTime returns the time when the next probe packet should be sent. 60// It returns the zero value if no probe packet should be sent. 61func (f *mtuFinder) NextProbeTime() time.Time { 62 if f.probeInFlight || f.done() { 63 return time.Time{} 64 } 65 return f.lastProbeTime.Add(mtuProbeDelay * f.rttStats.SmoothedRTT()) 66} 67 68func (f *mtuFinder) GetPing() (ackhandler.Frame, protocol.ByteCount) { 69 size := (f.max + f.current) / 2 70 f.lastProbeTime = time.Now() 71 f.probeInFlight = true 72 return ackhandler.Frame{ 73 Frame: &wire.PingFrame{}, 74 OnLost: func(wire.Frame) { 75 f.probeInFlight = false 76 f.max = size 77 }, 78 OnAcked: func(wire.Frame) { 79 f.probeInFlight = false 80 f.current = size 81 f.mtuIncreased(size) 82 }, 83 }, size 84} 85