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 })