github.com/quic-go/quic-go@v0.44.0/integrationtests/self/drop_test.go (about)

     1  package self_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math/rand"
     7  	"net"
     8  	"sync/atomic"
     9  	"time"
    10  
    11  	"github.com/quic-go/quic-go"
    12  	quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy"
    13  
    14  	. "github.com/onsi/ginkgo/v2"
    15  	. "github.com/onsi/gomega"
    16  )
    17  
    18  func randomDuration(min, max time.Duration) time.Duration {
    19  	return min + time.Duration(rand.Int63n(int64(max-min)))
    20  }
    21  
    22  var _ = Describe("Drop Tests", func() {
    23  	var (
    24  		proxy *quicproxy.QuicProxy
    25  		ln    *quic.Listener
    26  	)
    27  
    28  	startListenerAndProxy := func(dropCallback quicproxy.DropCallback) {
    29  		var err error
    30  		ln, err = quic.ListenAddr(
    31  			"localhost:0",
    32  			getTLSConfig(),
    33  			getQuicConfig(nil),
    34  		)
    35  		Expect(err).ToNot(HaveOccurred())
    36  		serverPort := ln.Addr().(*net.UDPAddr).Port
    37  		proxy, err = quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
    38  			RemoteAddr: fmt.Sprintf("localhost:%d", serverPort),
    39  			DelayPacket: func(dir quicproxy.Direction, _ []byte) time.Duration {
    40  				return 5 * time.Millisecond // 10ms RTT
    41  			},
    42  			DropPacket: dropCallback,
    43  		},
    44  		)
    45  		Expect(err).ToNot(HaveOccurred())
    46  	}
    47  
    48  	AfterEach(func() {
    49  		Expect(proxy.Close()).To(Succeed())
    50  		Expect(ln.Close()).To(Succeed())
    51  	})
    52  
    53  	for _, d := range directions {
    54  		direction := d
    55  
    56  		// The purpose of this test is to create a lot of tails, by sending 1 byte messages.
    57  		// The interval, the length of the drop period, and the time when the drop period starts are randomized.
    58  		// To cover different scenarios, repeat this test a few times.
    59  		for rep := 0; rep < 3; rep++ {
    60  			It(fmt.Sprintf("sends short messages, dropping packets in %s direction", direction), func() {
    61  				const numMessages = 15
    62  
    63  				messageInterval := randomDuration(10*time.Millisecond, 100*time.Millisecond)
    64  				dropDuration := randomDuration(messageInterval*3/2, 2*time.Second)
    65  				dropDelay := randomDuration(25*time.Millisecond, numMessages*messageInterval/2) // makes sure we don't interfere with the handshake
    66  				fmt.Fprintf(GinkgoWriter, "Sending a message every %s, %d times.\n", messageInterval, numMessages)
    67  				fmt.Fprintf(GinkgoWriter, "Dropping packets for %s, after a delay of %s\n", dropDuration, dropDelay)
    68  				startTime := time.Now()
    69  
    70  				var numDroppedPackets atomic.Int32
    71  				startListenerAndProxy(func(d quicproxy.Direction, _ []byte) bool {
    72  					if !d.Is(direction) {
    73  						return false
    74  					}
    75  					drop := time.Now().After(startTime.Add(dropDelay)) && time.Now().Before(startTime.Add(dropDelay).Add(dropDuration))
    76  					if drop {
    77  						numDroppedPackets.Add(1)
    78  					}
    79  					return drop
    80  				})
    81  
    82  				done := make(chan struct{})
    83  				go func() {
    84  					defer GinkgoRecover()
    85  					conn, err := ln.Accept(context.Background())
    86  					Expect(err).ToNot(HaveOccurred())
    87  					str, err := conn.OpenStream()
    88  					Expect(err).ToNot(HaveOccurred())
    89  					for i := uint8(1); i <= numMessages; i++ {
    90  						n, err := str.Write([]byte{i})
    91  						Expect(err).ToNot(HaveOccurred())
    92  						Expect(n).To(Equal(1))
    93  						time.Sleep(messageInterval)
    94  					}
    95  					<-done
    96  					Expect(conn.CloseWithError(0, "")).To(Succeed())
    97  				}()
    98  
    99  				conn, err := quic.DialAddr(
   100  					context.Background(),
   101  					fmt.Sprintf("localhost:%d", proxy.LocalPort()),
   102  					getTLSClientConfig(),
   103  					getQuicConfig(nil),
   104  				)
   105  				Expect(err).ToNot(HaveOccurred())
   106  				defer conn.CloseWithError(0, "")
   107  				str, err := conn.AcceptStream(context.Background())
   108  				Expect(err).ToNot(HaveOccurred())
   109  				for i := uint8(1); i <= numMessages; i++ {
   110  					b := []byte{0}
   111  					n, err := str.Read(b)
   112  					Expect(err).ToNot(HaveOccurred())
   113  					Expect(n).To(Equal(1))
   114  					Expect(b[0]).To(Equal(i))
   115  				}
   116  				close(done)
   117  				numDropped := numDroppedPackets.Load()
   118  				fmt.Fprintf(GinkgoWriter, "Dropped %d packets.\n", numDropped)
   119  				Expect(numDropped).To(BeNumerically(">", 0))
   120  			})
   121  		}
   122  	}
   123  })