github.com/quic-go/quic-go@v0.44.0/mtu_discoverer_test.go (about)

     1  package quic
     2  
     3  import (
     4  	"math/rand"
     5  	"time"
     6  
     7  	"github.com/quic-go/quic-go/internal/protocol"
     8  	"github.com/quic-go/quic-go/internal/utils"
     9  	"github.com/quic-go/quic-go/logging"
    10  
    11  	. "github.com/onsi/ginkgo/v2"
    12  	. "github.com/onsi/gomega"
    13  )
    14  
    15  var _ = Describe("MTU Discoverer", func() {
    16  	const (
    17  		rtt                         = 100 * time.Millisecond
    18  		startMTU protocol.ByteCount = 1000
    19  		maxMTU   protocol.ByteCount = 2000
    20  	)
    21  
    22  	var (
    23  		d             *mtuFinder
    24  		rttStats      *utils.RTTStats
    25  		now           time.Time
    26  		discoveredMTU protocol.ByteCount
    27  	)
    28  
    29  	BeforeEach(func() {
    30  		rttStats = &utils.RTTStats{}
    31  		rttStats.SetInitialRTT(rtt)
    32  		Expect(rttStats.SmoothedRTT()).To(Equal(rtt))
    33  		d = newMTUDiscoverer(
    34  			rttStats,
    35  			startMTU,
    36  			maxMTU,
    37  			func(s protocol.ByteCount) { discoveredMTU = s },
    38  			nil,
    39  		)
    40  		d.Start()
    41  		now = time.Now()
    42  	})
    43  
    44  	It("only allows a probe 5 RTTs after the handshake completes", func() {
    45  		Expect(d.ShouldSendProbe(now)).To(BeFalse())
    46  		Expect(d.ShouldSendProbe(now.Add(rtt * 9 / 2))).To(BeFalse())
    47  		Expect(d.ShouldSendProbe(now.Add(rtt * 5))).To(BeTrue())
    48  	})
    49  
    50  	It("doesn't allow a probe if another probe is still in flight", func() {
    51  		ping, _ := d.GetPing()
    52  		Expect(d.ShouldSendProbe(now.Add(10 * rtt))).To(BeFalse())
    53  		ping.Handler.OnLost(ping.Frame)
    54  		Expect(d.ShouldSendProbe(now.Add(10 * rtt))).To(BeTrue())
    55  	})
    56  
    57  	It("tries a lower size when a probe is lost", func() {
    58  		ping, size := d.GetPing()
    59  		Expect(size).To(Equal(protocol.ByteCount(1500)))
    60  		ping.Handler.OnLost(ping.Frame)
    61  		_, size = d.GetPing()
    62  		Expect(size).To(Equal(protocol.ByteCount(1250)))
    63  	})
    64  
    65  	It("tries a higher size and calls the callback when a probe is acknowledged", func() {
    66  		ping, size := d.GetPing()
    67  		Expect(size).To(Equal(protocol.ByteCount(1500)))
    68  		ping.Handler.OnAcked(ping.Frame)
    69  		Expect(discoveredMTU).To(Equal(protocol.ByteCount(1500)))
    70  		_, size = d.GetPing()
    71  		Expect(size).To(Equal(protocol.ByteCount(1750)))
    72  	})
    73  
    74  	It("stops discovery after getting close enough to the MTU", func() {
    75  		var sizes []protocol.ByteCount
    76  		t := now.Add(5 * rtt)
    77  		for d.ShouldSendProbe(t) {
    78  			ping, size := d.GetPing()
    79  			ping.Handler.OnAcked(ping.Frame)
    80  			sizes = append(sizes, size)
    81  			t = t.Add(5 * rtt)
    82  		}
    83  		Expect(sizes).To(Equal([]protocol.ByteCount{1500, 1750, 1875, 1937, 1968, 1984}))
    84  		Expect(d.ShouldSendProbe(t.Add(10 * rtt))).To(BeFalse())
    85  	})
    86  
    87  	It("doesn't do discovery before being started", func() {
    88  		d := newMTUDiscoverer(rttStats, startMTU, protocol.MaxByteCount, func(s protocol.ByteCount) {}, nil)
    89  		for i := 0; i < 5; i++ {
    90  			Expect(d.ShouldSendProbe(time.Now())).To(BeFalse())
    91  		}
    92  	})
    93  
    94  	It("finds the MTU", func() {
    95  		const rep = 3000
    96  		var maxDiff protocol.ByteCount
    97  		for i := 0; i < rep; i++ {
    98  			maxMTU := protocol.ByteCount(rand.Intn(int(3000-startMTU))) + startMTU + 1
    99  			currentMTU := startMTU
   100  			var tracedMTU protocol.ByteCount
   101  			var tracerDone bool
   102  			d := newMTUDiscoverer(
   103  				rttStats,
   104  				startMTU,
   105  				maxMTU,
   106  				func(s protocol.ByteCount) { currentMTU = s },
   107  				&logging.ConnectionTracer{
   108  					UpdatedMTU: func(mtu logging.ByteCount, done bool) {
   109  						tracedMTU = mtu
   110  						tracerDone = done
   111  					},
   112  				},
   113  			)
   114  			d.Start()
   115  			now := time.Now()
   116  			realMTU := protocol.ByteCount(rand.Intn(int(maxMTU-startMTU))) + startMTU
   117  			t := now.Add(mtuProbeDelay * rtt)
   118  			var count int
   119  			for d.ShouldSendProbe(t) {
   120  				if count > 25 {
   121  					Fail("too many iterations")
   122  				}
   123  				count++
   124  
   125  				ping, size := d.GetPing()
   126  				if size <= realMTU {
   127  					ping.Handler.OnAcked(ping.Frame)
   128  				} else {
   129  					ping.Handler.OnLost(ping.Frame)
   130  				}
   131  				t = t.Add(mtuProbeDelay * rtt)
   132  			}
   133  			diff := realMTU - currentMTU
   134  			Expect(diff).To(BeNumerically(">=", 0))
   135  			maxDiff = max(maxDiff, diff)
   136  			if maxMTU > currentMTU+maxMTU {
   137  				Expect(tracedMTU).To(Equal(currentMTU))
   138  				Expect(tracerDone).To(BeTrue())
   139  			}
   140  		}
   141  		Expect(maxDiff).To(BeEquivalentTo(maxMTUDiff))
   142  	})
   143  })