github.com/MerlinKodo/quic-go@v0.39.2/integrationtests/self/datagram_test.go (about)

     1  package self_test
     2  
     3  import (
     4  	"context"
     5  	"encoding/binary"
     6  	"fmt"
     7  	mrand "math/rand"
     8  	"net"
     9  	"sync"
    10  	"sync/atomic"
    11  	"time"
    12  
    13  	"github.com/MerlinKodo/quic-go"
    14  	quicproxy "github.com/MerlinKodo/quic-go/integrationtests/tools/proxy"
    15  	"github.com/MerlinKodo/quic-go/internal/wire"
    16  
    17  	. "github.com/onsi/ginkgo/v2"
    18  	. "github.com/onsi/gomega"
    19  )
    20  
    21  var _ = Describe("Datagram test", func() {
    22  	const num = 100
    23  
    24  	var (
    25  		serverConn, clientConn *net.UDPConn
    26  		dropped, total         int32
    27  	)
    28  
    29  	startServerAndProxy := func(enableDatagram, expectDatagramSupport bool) (port int, closeFn func()) {
    30  		addr, err := net.ResolveUDPAddr("udp", "localhost:0")
    31  		Expect(err).ToNot(HaveOccurred())
    32  		serverConn, err = net.ListenUDP("udp", addr)
    33  		Expect(err).ToNot(HaveOccurred())
    34  		ln, err := quic.Listen(
    35  			serverConn,
    36  			getTLSConfig(),
    37  			getQuicConfig(&quic.Config{EnableDatagrams: enableDatagram}),
    38  		)
    39  		Expect(err).ToNot(HaveOccurred())
    40  
    41  		accepted := make(chan struct{})
    42  		go func() {
    43  			defer GinkgoRecover()
    44  			defer close(accepted)
    45  			conn, err := ln.Accept(context.Background())
    46  			Expect(err).ToNot(HaveOccurred())
    47  
    48  			if expectDatagramSupport {
    49  				Expect(conn.ConnectionState().SupportsDatagrams).To(BeTrue())
    50  
    51  				if enableDatagram {
    52  					var wg sync.WaitGroup
    53  					wg.Add(num)
    54  					for i := 0; i < num; i++ {
    55  						go func(i int) {
    56  							defer GinkgoRecover()
    57  							defer wg.Done()
    58  							b := make([]byte, 8)
    59  							binary.BigEndian.PutUint64(b, uint64(i))
    60  							Expect(conn.SendMessage(b)).To(Succeed())
    61  						}(i)
    62  					}
    63  					wg.Wait()
    64  				}
    65  			} else {
    66  				Expect(conn.ConnectionState().SupportsDatagrams).To(BeFalse())
    67  			}
    68  		}()
    69  
    70  		serverPort := ln.Addr().(*net.UDPAddr).Port
    71  		proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
    72  			RemoteAddr: fmt.Sprintf("localhost:%d", serverPort),
    73  			// drop 10% of Short Header packets sent from the server
    74  			DropPacket: func(dir quicproxy.Direction, packet []byte) bool {
    75  				if dir == quicproxy.DirectionIncoming {
    76  					return false
    77  				}
    78  				// don't drop Long Header packets
    79  				if wire.IsLongHeaderPacket(packet[0]) {
    80  					return false
    81  				}
    82  				drop := mrand.Int()%10 == 0
    83  				if drop {
    84  					atomic.AddInt32(&dropped, 1)
    85  				}
    86  				atomic.AddInt32(&total, 1)
    87  				return drop
    88  			},
    89  		})
    90  		Expect(err).ToNot(HaveOccurred())
    91  		return proxy.LocalPort(), func() {
    92  			Eventually(accepted).Should(BeClosed())
    93  			proxy.Close()
    94  			ln.Close()
    95  		}
    96  	}
    97  
    98  	BeforeEach(func() {
    99  		addr, err := net.ResolveUDPAddr("udp", "localhost:0")
   100  		Expect(err).ToNot(HaveOccurred())
   101  		clientConn, err = net.ListenUDP("udp", addr)
   102  		Expect(err).ToNot(HaveOccurred())
   103  	})
   104  
   105  	It("sends datagrams", func() {
   106  		proxyPort, close := startServerAndProxy(true, true)
   107  		defer close()
   108  		raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", proxyPort))
   109  		Expect(err).ToNot(HaveOccurred())
   110  		conn, err := quic.Dial(
   111  			context.Background(),
   112  			clientConn,
   113  			raddr,
   114  			getTLSClientConfig(),
   115  			getQuicConfig(&quic.Config{EnableDatagrams: true}),
   116  		)
   117  		Expect(err).ToNot(HaveOccurred())
   118  		Expect(conn.ConnectionState().SupportsDatagrams).To(BeTrue())
   119  		var counter int
   120  		for {
   121  			// Close the connection if no message is received for 100 ms.
   122  			timer := time.AfterFunc(scaleDuration(100*time.Millisecond), func() { conn.CloseWithError(0, "") })
   123  			if _, err := conn.ReceiveMessage(context.Background()); err != nil {
   124  				break
   125  			}
   126  			timer.Stop()
   127  			counter++
   128  		}
   129  
   130  		numDropped := int(atomic.LoadInt32(&dropped))
   131  		expVal := num - numDropped
   132  		fmt.Fprintf(GinkgoWriter, "Dropped %d out of %d packets.\n", numDropped, atomic.LoadInt32(&total))
   133  		fmt.Fprintf(GinkgoWriter, "Received %d out of %d sent datagrams.\n", counter, num)
   134  		Expect(counter).To(And(
   135  			BeNumerically(">", expVal*9/10),
   136  			BeNumerically("<", num),
   137  		))
   138  		Eventually(conn.Context().Done).Should(BeClosed())
   139  	})
   140  
   141  	It("server can disable datagram", func() {
   142  		proxyPort, close := startServerAndProxy(false, true)
   143  		raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", proxyPort))
   144  		Expect(err).ToNot(HaveOccurred())
   145  		conn, err := quic.Dial(
   146  			context.Background(),
   147  			clientConn,
   148  			raddr,
   149  			getTLSClientConfig(),
   150  			getQuicConfig(&quic.Config{EnableDatagrams: true}),
   151  		)
   152  		Expect(err).ToNot(HaveOccurred())
   153  		Expect(conn.ConnectionState().SupportsDatagrams).To(BeFalse())
   154  
   155  		close()
   156  		conn.CloseWithError(0, "")
   157  	})
   158  
   159  	It("client can disable datagram", func() {
   160  		proxyPort, close := startServerAndProxy(false, true)
   161  		raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", proxyPort))
   162  		Expect(err).ToNot(HaveOccurred())
   163  		conn, err := quic.Dial(
   164  			context.Background(),
   165  			clientConn,
   166  			raddr,
   167  			getTLSClientConfig(),
   168  			getQuicConfig(&quic.Config{EnableDatagrams: true}),
   169  		)
   170  		Expect(err).ToNot(HaveOccurred())
   171  		Expect(conn.ConnectionState().SupportsDatagrams).To(BeFalse())
   172  
   173  		Expect(conn.SendMessage([]byte{0})).To(HaveOccurred())
   174  
   175  		close()
   176  		conn.CloseWithError(0, "")
   177  	})
   178  })