github.com/MerlinKodo/quic-go@v0.39.2/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/MerlinKodo/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 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 Expect(err).ToNot(HaveOccurred()) 255 sentPackets.Add(1) 256 } 257 }() 258 259 // receive and count non-QUIC packets 260 go func() { 261 defer GinkgoRecover() 262 for { 263 b := make([]byte, 1024) 264 n, addr, err := tr2.ReadNonQUICPacket(ctx, b) 265 if err != nil { 266 Expect(err).To(MatchError(context.Canceled)) 267 return 268 } 269 Expect(addr).To(Equal(tr1.Conn.LocalAddr())) 270 Expect(n).To(Equal(packetLen)) 271 rcvdPackets.Add(1) 272 } 273 }() 274 dial(tr2, server.Addr()) 275 Eventually(func() int64 { return sentPackets.Load() }).Should(BeNumerically(">", 10)) 276 Eventually(func() int64 { return rcvdPackets.Load() }).Should(BeNumerically(">=", sentPackets.Load()*4/5)) 277 }) 278 })