github.com/metacubex/quic-go@v0.44.1-0.20240520163451-20b689a59136/mtu_discoverer.go (about)

     1  package quic
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/metacubex/quic-go/internal/ackhandler"
     7  	"github.com/metacubex/quic-go/internal/protocol"
     8  	"github.com/metacubex/quic-go/internal/utils"
     9  	"github.com/metacubex/quic-go/internal/wire"
    10  	"github.com/metacubex/quic-go/logging"
    11  )
    12  
    13  type mtuDiscoverer interface {
    14  	// Start starts the MTU discovery process.
    15  	// It's unnecessary to call ShouldSendProbe before that.
    16  	Start()
    17  	ShouldSendProbe(now time.Time) bool
    18  	CurrentSize() protocol.ByteCount
    19  	GetPing() (ping ackhandler.Frame, datagramSize protocol.ByteCount)
    20  }
    21  
    22  const (
    23  	// At some point, we have to stop searching for a higher MTU.
    24  	// We're happy to send a packet that's 10 bytes smaller than the actual MTU.
    25  	maxMTUDiff = 20
    26  	// send a probe packet every mtuProbeDelay RTTs
    27  	mtuProbeDelay = 5
    28  )
    29  
    30  type mtuFinder struct {
    31  	lastProbeTime time.Time
    32  	mtuIncreased  func(protocol.ByteCount)
    33  
    34  	rttStats *utils.RTTStats
    35  	inFlight protocol.ByteCount // the size of the probe packet currently in flight. InvalidByteCount if none is in flight
    36  	current  protocol.ByteCount
    37  	max      protocol.ByteCount // the maximum value, as advertised by the peer (or our maximum size buffer)
    38  
    39  	tracer *logging.ConnectionTracer
    40  }
    41  
    42  var _ mtuDiscoverer = &mtuFinder{}
    43  
    44  func newMTUDiscoverer(
    45  	rttStats *utils.RTTStats,
    46  	start, max protocol.ByteCount,
    47  	mtuIncreased func(protocol.ByteCount),
    48  	tracer *logging.ConnectionTracer,
    49  ) *mtuFinder {
    50  	return &mtuFinder{
    51  		inFlight:     protocol.InvalidByteCount,
    52  		current:      start,
    53  		max:          max,
    54  		rttStats:     rttStats,
    55  		mtuIncreased: mtuIncreased,
    56  		tracer:       tracer,
    57  	}
    58  }
    59  
    60  func (f *mtuFinder) done() bool {
    61  	return f.max-f.current <= maxMTUDiff+1
    62  }
    63  
    64  func (f *mtuFinder) SetMax(max protocol.ByteCount) {
    65  	f.max = max
    66  }
    67  
    68  func (f *mtuFinder) Start() {
    69  	if f.max == protocol.InvalidByteCount {
    70  		panic("invalid")
    71  	}
    72  	f.lastProbeTime = time.Now() // makes sure the first probe packet is not sent immediately
    73  }
    74  
    75  func (f *mtuFinder) ShouldSendProbe(now time.Time) bool {
    76  	if f.max == 0 || f.lastProbeTime.IsZero() {
    77  		return false
    78  	}
    79  	if f.inFlight != protocol.InvalidByteCount || f.done() {
    80  		return false
    81  	}
    82  	return !now.Before(f.lastProbeTime.Add(mtuProbeDelay * f.rttStats.SmoothedRTT()))
    83  }
    84  
    85  func (f *mtuFinder) GetPing() (ackhandler.Frame, protocol.ByteCount) {
    86  	size := (f.max + f.current) / 2
    87  	f.lastProbeTime = time.Now()
    88  	f.inFlight = size
    89  	return ackhandler.Frame{
    90  		Frame:   &wire.PingFrame{},
    91  		Handler: &mtuFinderAckHandler{f},
    92  	}, size
    93  }
    94  
    95  func (f *mtuFinder) CurrentSize() protocol.ByteCount {
    96  	return f.current
    97  }
    98  
    99  type mtuFinderAckHandler struct {
   100  	*mtuFinder
   101  }
   102  
   103  var _ ackhandler.FrameHandler = &mtuFinderAckHandler{}
   104  
   105  func (h *mtuFinderAckHandler) OnAcked(wire.Frame) {
   106  	size := h.inFlight
   107  	if size == protocol.InvalidByteCount {
   108  		panic("OnAcked callback called although there's no MTU probe packet in flight")
   109  	}
   110  	h.inFlight = protocol.InvalidByteCount
   111  	h.current = size
   112  	if h.tracer != nil && h.tracer.UpdatedMTU != nil {
   113  		h.tracer.UpdatedMTU(size, h.done())
   114  	}
   115  	h.mtuIncreased(size)
   116  }
   117  
   118  func (h *mtuFinderAckHandler) OnLost(wire.Frame) {
   119  	size := h.inFlight
   120  	if size == protocol.InvalidByteCount {
   121  		panic("OnLost callback called although there's no MTU probe packet in flight")
   122  	}
   123  	h.max = size
   124  	h.inFlight = protocol.InvalidByteCount
   125  }