github.com/apernet/quic-go@v0.43.1-0.20240515053213-5e9e635fd9f0/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/apernet/quic-go"
    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  			addTracer(tr)
    78  
    79  			done1 := make(chan struct{})
    80  			done2 := make(chan struct{})
    81  			go func() {
    82  				defer GinkgoRecover()
    83  				dial(tr, server.Addr())
    84  				close(done1)
    85  			}()
    86  			go func() {
    87  				defer GinkgoRecover()
    88  				dial(tr, server.Addr())
    89  				close(done2)
    90  			}()
    91  			timeout := 30 * time.Second
    92  			if debugLog() {
    93  				timeout = time.Minute
    94  			}
    95  			Eventually(done1, timeout).Should(BeClosed())
    96  			Eventually(done2, timeout).Should(BeClosed())
    97  		})
    98  
    99  		It("multiplexes connections to different servers", func() {
   100  			server1 := getListener()
   101  			runServer(server1)
   102  			defer server1.Close()
   103  			server2 := getListener()
   104  			runServer(server2)
   105  			defer server2.Close()
   106  
   107  			addr, err := net.ResolveUDPAddr("udp", "localhost:0")
   108  			Expect(err).ToNot(HaveOccurred())
   109  			conn, err := net.ListenUDP("udp", addr)
   110  			Expect(err).ToNot(HaveOccurred())
   111  			defer conn.Close()
   112  			tr := &quic.Transport{Conn: conn}
   113  			addTracer(tr)
   114  
   115  			done1 := make(chan struct{})
   116  			done2 := make(chan struct{})
   117  			go func() {
   118  				defer GinkgoRecover()
   119  				dial(tr, server1.Addr())
   120  				close(done1)
   121  			}()
   122  			go func() {
   123  				defer GinkgoRecover()
   124  				dial(tr, server2.Addr())
   125  				close(done2)
   126  			}()
   127  			timeout := 30 * time.Second
   128  			if debugLog() {
   129  				timeout = time.Minute
   130  			}
   131  			Eventually(done1, timeout).Should(BeClosed())
   132  			Eventually(done2, timeout).Should(BeClosed())
   133  		})
   134  	})
   135  
   136  	Context("multiplexing server and client on the same conn", func() {
   137  		It("connects to itself", func() {
   138  			addr, err := net.ResolveUDPAddr("udp", "localhost:0")
   139  			Expect(err).ToNot(HaveOccurred())
   140  			conn, err := net.ListenUDP("udp", addr)
   141  			Expect(err).ToNot(HaveOccurred())
   142  			defer conn.Close()
   143  			tr := &quic.Transport{Conn: conn}
   144  			addTracer(tr)
   145  			server, err := tr.Listen(
   146  				getTLSConfig(),
   147  				getQuicConfig(nil),
   148  			)
   149  			Expect(err).ToNot(HaveOccurred())
   150  			runServer(server)
   151  			done := make(chan struct{})
   152  			go func() {
   153  				defer GinkgoRecover()
   154  				dial(tr, server.Addr())
   155  				close(done)
   156  			}()
   157  			timeout := 30 * time.Second
   158  			if debugLog() {
   159  				timeout = time.Minute
   160  			}
   161  			Eventually(done, timeout).Should(BeClosed())
   162  		})
   163  
   164  		// This test would require setting of iptables rules, see https://stackoverflow.com/questions/23859164/linux-udp-socket-sendto-operation-not-permitted.
   165  		if runtime.GOOS != "linux" {
   166  			It("runs a server and client on the same conn", func() {
   167  				addr1, err := net.ResolveUDPAddr("udp", "localhost:0")
   168  				Expect(err).ToNot(HaveOccurred())
   169  				conn1, err := net.ListenUDP("udp", addr1)
   170  				Expect(err).ToNot(HaveOccurred())
   171  				defer conn1.Close()
   172  				tr1 := &quic.Transport{Conn: conn1}
   173  				addTracer(tr1)
   174  
   175  				addr2, err := net.ResolveUDPAddr("udp", "localhost:0")
   176  				Expect(err).ToNot(HaveOccurred())
   177  				conn2, err := net.ListenUDP("udp", addr2)
   178  				Expect(err).ToNot(HaveOccurred())
   179  				defer conn2.Close()
   180  				tr2 := &quic.Transport{Conn: conn2}
   181  				addTracer(tr2)
   182  
   183  				server1, err := tr1.Listen(
   184  					getTLSConfig(),
   185  					getQuicConfig(nil),
   186  				)
   187  				Expect(err).ToNot(HaveOccurred())
   188  				runServer(server1)
   189  				defer server1.Close()
   190  
   191  				server2, err := tr2.Listen(
   192  					getTLSConfig(),
   193  					getQuicConfig(nil),
   194  				)
   195  				Expect(err).ToNot(HaveOccurred())
   196  				runServer(server2)
   197  				defer server2.Close()
   198  
   199  				done1 := make(chan struct{})
   200  				done2 := make(chan struct{})
   201  				go func() {
   202  					defer GinkgoRecover()
   203  					dial(tr2, server1.Addr())
   204  					close(done1)
   205  				}()
   206  				go func() {
   207  					defer GinkgoRecover()
   208  					dial(tr1, server2.Addr())
   209  					close(done2)
   210  				}()
   211  				timeout := 30 * time.Second
   212  				if debugLog() {
   213  					timeout = time.Minute
   214  				}
   215  				Eventually(done1, timeout).Should(BeClosed())
   216  				Eventually(done2, timeout).Should(BeClosed())
   217  			})
   218  		}
   219  	})
   220  
   221  	It("sends and receives non-QUIC packets", func() {
   222  		addr1, err := net.ResolveUDPAddr("udp", "localhost:0")
   223  		Expect(err).ToNot(HaveOccurred())
   224  		conn1, err := net.ListenUDP("udp", addr1)
   225  		Expect(err).ToNot(HaveOccurred())
   226  		defer conn1.Close()
   227  		tr1 := &quic.Transport{Conn: conn1}
   228  		addTracer(tr1)
   229  
   230  		addr2, err := net.ResolveUDPAddr("udp", "localhost:0")
   231  		Expect(err).ToNot(HaveOccurred())
   232  		conn2, err := net.ListenUDP("udp", addr2)
   233  		Expect(err).ToNot(HaveOccurred())
   234  		defer conn2.Close()
   235  		tr2 := &quic.Transport{Conn: conn2}
   236  		addTracer(tr2)
   237  
   238  		server, err := tr1.Listen(getTLSConfig(), getQuicConfig(nil))
   239  		Expect(err).ToNot(HaveOccurred())
   240  		runServer(server)
   241  		defer server.Close()
   242  
   243  		ctx, cancel := context.WithCancel(context.Background())
   244  		defer cancel()
   245  		var sentPackets, rcvdPackets atomic.Int64
   246  		const packetLen = 128
   247  		// send a non-QUIC packet every 100µs
   248  		go func() {
   249  			defer GinkgoRecover()
   250  			ticker := time.NewTicker(time.Millisecond / 10)
   251  			defer ticker.Stop()
   252  			var wroteFirstPacket bool
   253  			for {
   254  				select {
   255  				case <-ticker.C:
   256  				case <-ctx.Done():
   257  					return
   258  				}
   259  				b := make([]byte, packetLen)
   260  				rand.Read(b[1:]) // keep the first byte set to 0, so it's not classified as a QUIC packet
   261  				_, err := tr1.WriteTo(b, tr2.Conn.LocalAddr())
   262  				// The first sendmsg call on a new UDP socket sometimes errors on Linux.
   263  				// It's not clear why this happens.
   264  				// See https://github.com/golang/go/issues/63322.
   265  				if err != nil && !wroteFirstPacket && isPermissionError(err) {
   266  					_, err = tr1.WriteTo(b, tr2.Conn.LocalAddr())
   267  				}
   268  				if ctx.Err() != nil { // ctx canceled while Read was executing
   269  					return
   270  				}
   271  				Expect(err).ToNot(HaveOccurred())
   272  				sentPackets.Add(1)
   273  				wroteFirstPacket = true
   274  			}
   275  		}()
   276  
   277  		// receive and count non-QUIC packets
   278  		go func() {
   279  			defer GinkgoRecover()
   280  			for {
   281  				b := make([]byte, 1024)
   282  				n, addr, err := tr2.ReadNonQUICPacket(ctx, b)
   283  				if err != nil {
   284  					Expect(err).To(MatchError(context.Canceled))
   285  					return
   286  				}
   287  				Expect(addr).To(Equal(tr1.Conn.LocalAddr()))
   288  				Expect(n).To(Equal(packetLen))
   289  				rcvdPackets.Add(1)
   290  			}
   291  		}()
   292  		dial(tr2, server.Addr())
   293  		Eventually(func() int64 { return sentPackets.Load() }).Should(BeNumerically(">", 10))
   294  		Eventually(func() int64 { return rcvdPackets.Load() }).Should(BeNumerically(">=", sentPackets.Load()*4/5))
   295  	})
   296  })