github.com/danielpfeifer02/quic-go-prio-packs@v0.41.0-28/integrationtests/self/multiplex_test.go (about)

     1  package self_test
     2  
     3  import (
     4  	"context"
     5  	"crypto/rand"
     6  	"io"
     7  	"net"
     8  	"runtime"
     9  	"sync/atomic"
    10  	"time"
    11  
    12  	"github.com/danielpfeifer02/quic-go-prio-packs"
    13  
    14  	. "github.com/onsi/ginkgo/v2"
    15  	. "github.com/onsi/gomega"
    16  )
    17  
    18  var _ = Describe("Multiplexing", func() {
    19  	runServer := func(ln *quic.Listener) {
    20  		go func() {
    21  			defer GinkgoRecover()
    22  			for {
    23  				conn, err := ln.Accept(context.Background())
    24  				if err != nil {
    25  					return
    26  				}
    27  				go func() {
    28  					defer GinkgoRecover()
    29  					str, err := conn.OpenStream()
    30  					Expect(err).ToNot(HaveOccurred())
    31  					defer str.Close()
    32  					_, err = str.Write(PRData)
    33  					Expect(err).ToNot(HaveOccurred())
    34  				}()
    35  			}
    36  		}()
    37  	}
    38  
    39  	dial := func(tr *quic.Transport, addr net.Addr) {
    40  		conn, err := tr.Dial(
    41  			context.Background(),
    42  			addr,
    43  			getTLSClientConfig(),
    44  			getQuicConfig(nil),
    45  		)
    46  		Expect(err).ToNot(HaveOccurred())
    47  		defer conn.CloseWithError(0, "")
    48  		str, err := conn.AcceptStream(context.Background())
    49  		Expect(err).ToNot(HaveOccurred())
    50  		data, err := io.ReadAll(str)
    51  		Expect(err).ToNot(HaveOccurred())
    52  		Expect(data).To(Equal(PRData))
    53  	}
    54  
    55  	Context("multiplexing clients on the same conn", func() {
    56  		getListener := func() *quic.Listener {
    57  			ln, err := quic.ListenAddr(
    58  				"localhost:0",
    59  				getTLSConfig(),
    60  				getQuicConfig(nil),
    61  			)
    62  			Expect(err).ToNot(HaveOccurred())
    63  			return ln
    64  		}
    65  
    66  		It("multiplexes connections to the same server", func() {
    67  			server := getListener()
    68  			runServer(server)
    69  			defer server.Close()
    70  
    71  			addr, err := net.ResolveUDPAddr("udp", "localhost:0")
    72  			Expect(err).ToNot(HaveOccurred())
    73  			conn, err := net.ListenUDP("udp", addr)
    74  			Expect(err).ToNot(HaveOccurred())
    75  			defer conn.Close()
    76  			tr := &quic.Transport{Conn: conn}
    77  
    78  			done1 := make(chan struct{})
    79  			done2 := make(chan struct{})
    80  			go func() {
    81  				defer GinkgoRecover()
    82  				dial(tr, server.Addr())
    83  				close(done1)
    84  			}()
    85  			go func() {
    86  				defer GinkgoRecover()
    87  				dial(tr, server.Addr())
    88  				close(done2)
    89  			}()
    90  			timeout := 30 * time.Second
    91  			if debugLog() {
    92  				timeout = time.Minute
    93  			}
    94  			Eventually(done1, timeout).Should(BeClosed())
    95  			Eventually(done2, timeout).Should(BeClosed())
    96  		})
    97  
    98  		It("multiplexes connections to different servers", func() {
    99  			server1 := getListener()
   100  			runServer(server1)
   101  			defer server1.Close()
   102  			server2 := getListener()
   103  			runServer(server2)
   104  			defer server2.Close()
   105  
   106  			addr, err := net.ResolveUDPAddr("udp", "localhost:0")
   107  			Expect(err).ToNot(HaveOccurred())
   108  			conn, err := net.ListenUDP("udp", addr)
   109  			Expect(err).ToNot(HaveOccurred())
   110  			defer conn.Close()
   111  			tr := &quic.Transport{Conn: conn}
   112  
   113  			done1 := make(chan struct{})
   114  			done2 := make(chan struct{})
   115  			go func() {
   116  				defer GinkgoRecover()
   117  				dial(tr, server1.Addr())
   118  				close(done1)
   119  			}()
   120  			go func() {
   121  				defer GinkgoRecover()
   122  				dial(tr, server2.Addr())
   123  				close(done2)
   124  			}()
   125  			timeout := 30 * time.Second
   126  			if debugLog() {
   127  				timeout = time.Minute
   128  			}
   129  			Eventually(done1, timeout).Should(BeClosed())
   130  			Eventually(done2, timeout).Should(BeClosed())
   131  		})
   132  	})
   133  
   134  	Context("multiplexing server and client on the same conn", func() {
   135  		It("connects to itself", func() {
   136  			addr, err := net.ResolveUDPAddr("udp", "localhost:0")
   137  			Expect(err).ToNot(HaveOccurred())
   138  			conn, err := net.ListenUDP("udp", addr)
   139  			Expect(err).ToNot(HaveOccurred())
   140  			defer conn.Close()
   141  			tr := &quic.Transport{Conn: conn}
   142  			server, err := tr.Listen(
   143  				getTLSConfig(),
   144  				getQuicConfig(nil),
   145  			)
   146  			Expect(err).ToNot(HaveOccurred())
   147  			runServer(server)
   148  			done := make(chan struct{})
   149  			go func() {
   150  				defer GinkgoRecover()
   151  				dial(tr, server.Addr())
   152  				close(done)
   153  			}()
   154  			timeout := 30 * time.Second
   155  			if debugLog() {
   156  				timeout = time.Minute
   157  			}
   158  			Eventually(done, timeout).Should(BeClosed())
   159  		})
   160  
   161  		// This test would require setting of iptables rules, see https://stackoverflow.com/questions/23859164/linux-udp-socket-sendto-operation-not-permitted.
   162  		if runtime.GOOS != "linux" {
   163  			It("runs a server and client on the same conn", func() {
   164  				addr1, err := net.ResolveUDPAddr("udp", "localhost:0")
   165  				Expect(err).ToNot(HaveOccurred())
   166  				conn1, err := net.ListenUDP("udp", addr1)
   167  				Expect(err).ToNot(HaveOccurred())
   168  				defer conn1.Close()
   169  				tr1 := &quic.Transport{Conn: conn1}
   170  
   171  				addr2, err := net.ResolveUDPAddr("udp", "localhost:0")
   172  				Expect(err).ToNot(HaveOccurred())
   173  				conn2, err := net.ListenUDP("udp", addr2)
   174  				Expect(err).ToNot(HaveOccurred())
   175  				defer conn2.Close()
   176  				tr2 := &quic.Transport{Conn: conn2}
   177  
   178  				server1, err := tr1.Listen(
   179  					getTLSConfig(),
   180  					getQuicConfig(nil),
   181  				)
   182  				Expect(err).ToNot(HaveOccurred())
   183  				runServer(server1)
   184  				defer server1.Close()
   185  
   186  				server2, err := tr2.Listen(
   187  					getTLSConfig(),
   188  					getQuicConfig(nil),
   189  				)
   190  				Expect(err).ToNot(HaveOccurred())
   191  				runServer(server2)
   192  				defer server2.Close()
   193  
   194  				done1 := make(chan struct{})
   195  				done2 := make(chan struct{})
   196  				go func() {
   197  					defer GinkgoRecover()
   198  					dial(tr2, server1.Addr())
   199  					close(done1)
   200  				}()
   201  				go func() {
   202  					defer GinkgoRecover()
   203  					dial(tr1, server2.Addr())
   204  					close(done2)
   205  				}()
   206  				timeout := 30 * time.Second
   207  				if debugLog() {
   208  					timeout = time.Minute
   209  				}
   210  				Eventually(done1, timeout).Should(BeClosed())
   211  				Eventually(done2, timeout).Should(BeClosed())
   212  			})
   213  		}
   214  	})
   215  
   216  	It("sends and receives non-QUIC packets", func() {
   217  		addr1, err := net.ResolveUDPAddr("udp", "localhost:0")
   218  		Expect(err).ToNot(HaveOccurred())
   219  		conn1, err := net.ListenUDP("udp", addr1)
   220  		Expect(err).ToNot(HaveOccurred())
   221  		defer conn1.Close()
   222  		tr1 := &quic.Transport{Conn: conn1}
   223  
   224  		addr2, err := net.ResolveUDPAddr("udp", "localhost:0")
   225  		Expect(err).ToNot(HaveOccurred())
   226  		conn2, err := net.ListenUDP("udp", addr2)
   227  		Expect(err).ToNot(HaveOccurred())
   228  		defer conn2.Close()
   229  		tr2 := &quic.Transport{Conn: conn2}
   230  
   231  		server, err := tr1.Listen(getTLSConfig(), getQuicConfig(nil))
   232  		Expect(err).ToNot(HaveOccurred())
   233  		runServer(server)
   234  		defer server.Close()
   235  
   236  		ctx, cancel := context.WithCancel(context.Background())
   237  		defer cancel()
   238  		var sentPackets, rcvdPackets atomic.Int64
   239  		const packetLen = 128
   240  		// send a non-QUIC packet every 100µs
   241  		go func() {
   242  			defer GinkgoRecover()
   243  			ticker := time.NewTicker(time.Millisecond / 10)
   244  			defer ticker.Stop()
   245  			for {
   246  				select {
   247  				case <-ticker.C:
   248  				case <-ctx.Done():
   249  					return
   250  				}
   251  				b := make([]byte, packetLen)
   252  				rand.Read(b[1:]) // keep the first byte set to 0, so it's not classified as a QUIC packet
   253  				_, err := tr1.WriteTo(b, tr2.Conn.LocalAddr())
   254  				if ctx.Err() != nil { // ctx canceled while Read was executing
   255  					return
   256  				}
   257  				Expect(err).ToNot(HaveOccurred())
   258  				sentPackets.Add(1)
   259  			}
   260  		}()
   261  
   262  		// receive and count non-QUIC packets
   263  		go func() {
   264  			defer GinkgoRecover()
   265  			for {
   266  				b := make([]byte, 1024)
   267  				n, addr, err := tr2.ReadNonQUICPacket(ctx, b)
   268  				if err != nil {
   269  					Expect(err).To(MatchError(context.Canceled))
   270  					return
   271  				}
   272  				Expect(addr).To(Equal(tr1.Conn.LocalAddr()))
   273  				Expect(n).To(Equal(packetLen))
   274  				rcvdPackets.Add(1)
   275  			}
   276  		}()
   277  		dial(tr2, server.Addr())
   278  		Eventually(func() int64 { return sentPackets.Load() }).Should(BeNumerically(">", 10))
   279  		Eventually(func() int64 { return rcvdPackets.Load() }).Should(BeNumerically(">=", sentPackets.Load()*4/5))
   280  	})
   281  })