github.com/tumi8/quic-go@v0.37.4-tum/integrationtests/tools/proxy/proxy_test.go (about) 1 package quicproxy 2 3 import ( 4 "bytes" 5 "fmt" 6 "net" 7 "runtime/pprof" 8 "strconv" 9 "strings" 10 "sync/atomic" 11 "time" 12 13 "github.com/tumi8/quic-go/noninternal/protocol" 14 "github.com/tumi8/quic-go/noninternal/wire" 15 16 . "github.com/onsi/ginkgo/v2" 17 . "github.com/onsi/gomega" 18 ) 19 20 type packetData []byte 21 22 func isProxyRunning() bool { 23 var b bytes.Buffer 24 pprof.Lookup("goroutine").WriteTo(&b, 1) 25 return strings.Contains(b.String(), "proxy.(*QuicProxy).runIncomingConnection") || 26 strings.Contains(b.String(), "proxy.(*QuicProxy).runOutgoingConnection") 27 } 28 29 var _ = Describe("QUIC Proxy", func() { 30 makePacket := func(p protocol.PacketNumber, payload []byte) []byte { 31 hdr := wire.ExtendedHeader{ 32 Header: wire.Header{ 33 Type: protocol.PacketTypeInitial, 34 Version: protocol.Version1, 35 Length: 4 + protocol.ByteCount(len(payload)), 36 DestConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef, 0, 0, 0x13, 0x37}), 37 SrcConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef, 0, 0, 0x13, 0x37}), 38 }, 39 PacketNumber: p, 40 PacketNumberLen: protocol.PacketNumberLen4, 41 } 42 b, err := hdr.Append(nil, protocol.Version1) 43 Expect(err).ToNot(HaveOccurred()) 44 b = append(b, payload...) 45 return b 46 } 47 48 readPacketNumber := func(b []byte) protocol.PacketNumber { 49 hdr, data, _, err := wire.ParsePacket(b) 50 ExpectWithOffset(1, err).ToNot(HaveOccurred()) 51 Expect(hdr.Type).To(Equal(protocol.PacketTypeInitial)) 52 extHdr, err := hdr.ParseExtended(bytes.NewReader(data), protocol.Version1) 53 ExpectWithOffset(1, err).ToNot(HaveOccurred()) 54 return extHdr.PacketNumber 55 } 56 57 AfterEach(func() { 58 Eventually(isProxyRunning).Should(BeFalse()) 59 }) 60 61 Context("Proxy setup and teardown", func() { 62 It("sets up the UDPProxy", func() { 63 proxy, err := NewQuicProxy("localhost:0", nil) 64 Expect(err).ToNot(HaveOccurred()) 65 Expect(proxy.clientDict).To(HaveLen(0)) 66 67 // check that the proxy port is in use 68 addr, err := net.ResolveUDPAddr("udp", "localhost:"+strconv.Itoa(proxy.LocalPort())) 69 Expect(err).ToNot(HaveOccurred()) 70 _, err = net.ListenUDP("udp", addr) 71 Expect(err).To(MatchError(fmt.Sprintf("listen udp 127.0.0.1:%d: bind: address already in use", proxy.LocalPort()))) 72 Expect(proxy.Close()).To(Succeed()) // stopping is tested in the next test 73 }) 74 75 It("stops the UDPProxy", func() { 76 isProxyRunning := func() bool { 77 var b bytes.Buffer 78 pprof.Lookup("goroutine").WriteTo(&b, 1) 79 return strings.Contains(b.String(), "proxy.(*QuicProxy).runProxy") 80 } 81 82 proxy, err := NewQuicProxy("localhost:0", nil) 83 Expect(err).ToNot(HaveOccurred()) 84 port := proxy.LocalPort() 85 Eventually(isProxyRunning).Should(BeTrue()) 86 err = proxy.Close() 87 Expect(err).ToNot(HaveOccurred()) 88 89 // check that the proxy port is not in use anymore 90 addr, err := net.ResolveUDPAddr("udp", "localhost:"+strconv.Itoa(port)) 91 Expect(err).ToNot(HaveOccurred()) 92 // sometimes it takes a while for the OS to free the port 93 Eventually(func() error { 94 ln, err := net.ListenUDP("udp", addr) 95 if err != nil { 96 return err 97 } 98 ln.Close() 99 return nil 100 }).ShouldNot(HaveOccurred()) 101 Eventually(isProxyRunning).Should(BeFalse()) 102 }) 103 104 It("stops listening for proxied connections", func() { 105 serverAddr, err := net.ResolveUDPAddr("udp", "localhost:0") 106 Expect(err).ToNot(HaveOccurred()) 107 serverConn, err := net.ListenUDP("udp", serverAddr) 108 Expect(err).ToNot(HaveOccurred()) 109 defer serverConn.Close() 110 111 proxy, err := NewQuicProxy("localhost:0", &Opts{RemoteAddr: serverConn.LocalAddr().String()}) 112 Expect(err).ToNot(HaveOccurred()) 113 Expect(isProxyRunning()).To(BeFalse()) 114 115 // check that the proxy port is not in use anymore 116 conn, err := net.DialUDP("udp", nil, proxy.LocalAddr().(*net.UDPAddr)) 117 Expect(err).ToNot(HaveOccurred()) 118 _, err = conn.Write(makePacket(1, []byte("foobar"))) 119 Expect(err).ToNot(HaveOccurred()) 120 Eventually(isProxyRunning).Should(BeTrue()) 121 Expect(proxy.Close()).To(Succeed()) 122 Eventually(isProxyRunning).Should(BeFalse()) 123 }) 124 125 It("has the correct LocalAddr and LocalPort", func() { 126 proxy, err := NewQuicProxy("localhost:0", nil) 127 Expect(err).ToNot(HaveOccurred()) 128 129 Expect(proxy.LocalAddr().String()).To(Equal("127.0.0.1:" + strconv.Itoa(proxy.LocalPort()))) 130 Expect(proxy.LocalPort()).ToNot(BeZero()) 131 132 Expect(proxy.Close()).To(Succeed()) 133 }) 134 }) 135 136 Context("Proxy tests", func() { 137 var ( 138 serverConn *net.UDPConn 139 serverNumPacketsSent int32 140 serverReceivedPackets chan packetData 141 clientConn *net.UDPConn 142 proxy *QuicProxy 143 stoppedReading chan struct{} 144 ) 145 146 startProxy := func(opts *Opts) { 147 var err error 148 proxy, err = NewQuicProxy("localhost:0", opts) 149 Expect(err).ToNot(HaveOccurred()) 150 clientConn, err = net.DialUDP("udp", nil, proxy.LocalAddr().(*net.UDPAddr)) 151 Expect(err).ToNot(HaveOccurred()) 152 } 153 154 BeforeEach(func() { 155 stoppedReading = make(chan struct{}) 156 serverReceivedPackets = make(chan packetData, 100) 157 atomic.StoreInt32(&serverNumPacketsSent, 0) 158 159 // setup a dump UDP server 160 // in production this would be a QUIC server 161 raddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0") 162 Expect(err).ToNot(HaveOccurred()) 163 serverConn, err = net.ListenUDP("udp", raddr) 164 Expect(err).ToNot(HaveOccurred()) 165 166 go func() { 167 defer GinkgoRecover() 168 defer close(stoppedReading) 169 for { 170 buf := make([]byte, protocol.MaxPacketBufferSize) 171 // the ReadFromUDP will error as soon as the UDP conn is closed 172 n, addr, err2 := serverConn.ReadFromUDP(buf) 173 if err2 != nil { 174 return 175 } 176 data := buf[0:n] 177 serverReceivedPackets <- packetData(data) 178 // echo the packet 179 atomic.AddInt32(&serverNumPacketsSent, 1) 180 serverConn.WriteToUDP(data, addr) 181 } 182 }() 183 }) 184 185 AfterEach(func() { 186 Expect(proxy.Close()).To(Succeed()) 187 Expect(serverConn.Close()).To(Succeed()) 188 Expect(clientConn.Close()).To(Succeed()) 189 Eventually(stoppedReading).Should(BeClosed()) 190 }) 191 192 Context("no packet drop", func() { 193 It("relays packets from the client to the server", func() { 194 startProxy(&Opts{RemoteAddr: serverConn.LocalAddr().String()}) 195 // send the first packet 196 _, err := clientConn.Write(makePacket(1, []byte("foobar"))) 197 Expect(err).ToNot(HaveOccurred()) 198 199 // send the second packet 200 _, err = clientConn.Write(makePacket(2, []byte("decafbad"))) 201 Expect(err).ToNot(HaveOccurred()) 202 203 Eventually(serverReceivedPackets).Should(HaveLen(2)) 204 Expect(string(<-serverReceivedPackets)).To(ContainSubstring("foobar")) 205 Expect(string(<-serverReceivedPackets)).To(ContainSubstring("decafbad")) 206 }) 207 208 It("relays packets from the server to the client", func() { 209 startProxy(&Opts{RemoteAddr: serverConn.LocalAddr().String()}) 210 // send the first packet 211 _, err := clientConn.Write(makePacket(1, []byte("foobar"))) 212 Expect(err).ToNot(HaveOccurred()) 213 214 // send the second packet 215 _, err = clientConn.Write(makePacket(2, []byte("decafbad"))) 216 Expect(err).ToNot(HaveOccurred()) 217 218 clientReceivedPackets := make(chan packetData, 2) 219 // receive the packets echoed by the server on client side 220 go func() { 221 for { 222 buf := make([]byte, protocol.MaxPacketBufferSize) 223 // the ReadFromUDP will error as soon as the UDP conn is closed 224 n, _, err2 := clientConn.ReadFromUDP(buf) 225 if err2 != nil { 226 return 227 } 228 data := buf[0:n] 229 clientReceivedPackets <- packetData(data) 230 } 231 }() 232 233 Eventually(serverReceivedPackets).Should(HaveLen(2)) 234 Expect(atomic.LoadInt32(&serverNumPacketsSent)).To(BeEquivalentTo(2)) 235 Eventually(clientReceivedPackets).Should(HaveLen(2)) 236 Expect(string(<-clientReceivedPackets)).To(ContainSubstring("foobar")) 237 Expect(string(<-clientReceivedPackets)).To(ContainSubstring("decafbad")) 238 }) 239 }) 240 241 Context("Drop Callbacks", func() { 242 It("drops incoming packets", func() { 243 var counter int32 244 opts := &Opts{ 245 RemoteAddr: serverConn.LocalAddr().String(), 246 DropPacket: func(d Direction, _ []byte) bool { 247 if d != DirectionIncoming { 248 return false 249 } 250 return atomic.AddInt32(&counter, 1)%2 == 1 251 }, 252 } 253 startProxy(opts) 254 255 for i := 1; i <= 6; i++ { 256 _, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i)))) 257 Expect(err).ToNot(HaveOccurred()) 258 } 259 Eventually(serverReceivedPackets).Should(HaveLen(3)) 260 Consistently(serverReceivedPackets).Should(HaveLen(3)) 261 }) 262 263 It("drops outgoing packets", func() { 264 const numPackets = 6 265 var counter int32 266 opts := &Opts{ 267 RemoteAddr: serverConn.LocalAddr().String(), 268 DropPacket: func(d Direction, _ []byte) bool { 269 if d != DirectionOutgoing { 270 return false 271 } 272 return atomic.AddInt32(&counter, 1)%2 == 1 273 }, 274 } 275 startProxy(opts) 276 277 clientReceivedPackets := make(chan packetData, numPackets) 278 // receive the packets echoed by the server on client side 279 go func() { 280 for { 281 buf := make([]byte, protocol.MaxPacketBufferSize) 282 // the ReadFromUDP will error as soon as the UDP conn is closed 283 n, _, err2 := clientConn.ReadFromUDP(buf) 284 if err2 != nil { 285 return 286 } 287 data := buf[0:n] 288 clientReceivedPackets <- packetData(data) 289 } 290 }() 291 292 for i := 1; i <= numPackets; i++ { 293 _, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i)))) 294 Expect(err).ToNot(HaveOccurred()) 295 } 296 297 Eventually(clientReceivedPackets).Should(HaveLen(numPackets / 2)) 298 Consistently(clientReceivedPackets).Should(HaveLen(numPackets / 2)) 299 }) 300 }) 301 302 Context("Delay Callback", func() { 303 const delay = 200 * time.Millisecond 304 expectDelay := func(startTime time.Time, numRTTs int) { 305 expectedReceiveTime := startTime.Add(time.Duration(numRTTs) * delay) 306 Expect(time.Now()).To(SatisfyAll( 307 BeTemporally(">=", expectedReceiveTime), 308 BeTemporally("<", expectedReceiveTime.Add(delay/2)), 309 )) 310 } 311 312 It("delays incoming packets", func() { 313 var counter int32 314 opts := &Opts{ 315 RemoteAddr: serverConn.LocalAddr().String(), 316 // delay packet 1 by 200 ms 317 // delay packet 2 by 400 ms 318 // ... 319 DelayPacket: func(d Direction, _ []byte) time.Duration { 320 if d == DirectionOutgoing { 321 return 0 322 } 323 p := atomic.AddInt32(&counter, 1) 324 return time.Duration(p) * delay 325 }, 326 } 327 startProxy(opts) 328 329 // send 3 packets 330 start := time.Now() 331 for i := 1; i <= 3; i++ { 332 _, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i)))) 333 Expect(err).ToNot(HaveOccurred()) 334 } 335 Eventually(serverReceivedPackets).Should(HaveLen(1)) 336 expectDelay(start, 1) 337 Eventually(serverReceivedPackets).Should(HaveLen(2)) 338 expectDelay(start, 2) 339 Eventually(serverReceivedPackets).Should(HaveLen(3)) 340 expectDelay(start, 3) 341 Expect(readPacketNumber(<-serverReceivedPackets)).To(Equal(protocol.PacketNumber(1))) 342 Expect(readPacketNumber(<-serverReceivedPackets)).To(Equal(protocol.PacketNumber(2))) 343 Expect(readPacketNumber(<-serverReceivedPackets)).To(Equal(protocol.PacketNumber(3))) 344 }) 345 346 It("handles reordered packets", func() { 347 var counter int32 348 opts := &Opts{ 349 RemoteAddr: serverConn.LocalAddr().String(), 350 // delay packet 1 by 600 ms 351 // delay packet 2 by 400 ms 352 // delay packet 3 by 200 ms 353 DelayPacket: func(d Direction, _ []byte) time.Duration { 354 if d == DirectionOutgoing { 355 return 0 356 } 357 p := atomic.AddInt32(&counter, 1) 358 return 600*time.Millisecond - time.Duration(p-1)*delay 359 }, 360 } 361 startProxy(opts) 362 363 // send 3 packets 364 start := time.Now() 365 for i := 1; i <= 3; i++ { 366 _, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i)))) 367 Expect(err).ToNot(HaveOccurred()) 368 } 369 Eventually(serverReceivedPackets).Should(HaveLen(1)) 370 expectDelay(start, 1) 371 Eventually(serverReceivedPackets).Should(HaveLen(2)) 372 expectDelay(start, 2) 373 Eventually(serverReceivedPackets).Should(HaveLen(3)) 374 expectDelay(start, 3) 375 Expect(readPacketNumber(<-serverReceivedPackets)).To(Equal(protocol.PacketNumber(3))) 376 Expect(readPacketNumber(<-serverReceivedPackets)).To(Equal(protocol.PacketNumber(2))) 377 Expect(readPacketNumber(<-serverReceivedPackets)).To(Equal(protocol.PacketNumber(1))) 378 }) 379 380 It("doesn't reorder packets when a constant delay is used", func() { 381 opts := &Opts{ 382 RemoteAddr: serverConn.LocalAddr().String(), 383 DelayPacket: func(d Direction, _ []byte) time.Duration { 384 if d == DirectionOutgoing { 385 return 0 386 } 387 return 100 * time.Millisecond 388 }, 389 } 390 startProxy(opts) 391 392 // send 100 packets 393 for i := 0; i < 100; i++ { 394 _, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i)))) 395 Expect(err).ToNot(HaveOccurred()) 396 } 397 Eventually(serverReceivedPackets).Should(HaveLen(100)) 398 for i := 0; i < 100; i++ { 399 Expect(readPacketNumber(<-serverReceivedPackets)).To(Equal(protocol.PacketNumber(i))) 400 } 401 }) 402 403 It("delays outgoing packets", func() { 404 const numPackets = 3 405 var counter int32 406 opts := &Opts{ 407 RemoteAddr: serverConn.LocalAddr().String(), 408 // delay packet 1 by 200 ms 409 // delay packet 2 by 400 ms 410 // ... 411 DelayPacket: func(d Direction, _ []byte) time.Duration { 412 if d == DirectionIncoming { 413 return 0 414 } 415 p := atomic.AddInt32(&counter, 1) 416 return time.Duration(p) * delay 417 }, 418 } 419 startProxy(opts) 420 421 clientReceivedPackets := make(chan packetData, numPackets) 422 // receive the packets echoed by the server on client side 423 go func() { 424 for { 425 buf := make([]byte, protocol.MaxPacketBufferSize) 426 // the ReadFromUDP will error as soon as the UDP conn is closed 427 n, _, err2 := clientConn.ReadFromUDP(buf) 428 if err2 != nil { 429 return 430 } 431 data := buf[0:n] 432 clientReceivedPackets <- packetData(data) 433 } 434 }() 435 436 start := time.Now() 437 for i := 1; i <= numPackets; i++ { 438 _, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i)))) 439 Expect(err).ToNot(HaveOccurred()) 440 } 441 // the packets should have arrived immediately at the server 442 Eventually(serverReceivedPackets).Should(HaveLen(3)) 443 expectDelay(start, 0) 444 Eventually(clientReceivedPackets).Should(HaveLen(1)) 445 expectDelay(start, 1) 446 Eventually(clientReceivedPackets).Should(HaveLen(2)) 447 expectDelay(start, 2) 448 Eventually(clientReceivedPackets).Should(HaveLen(3)) 449 expectDelay(start, 3) 450 Expect(readPacketNumber(<-clientReceivedPackets)).To(Equal(protocol.PacketNumber(1))) 451 Expect(readPacketNumber(<-clientReceivedPackets)).To(Equal(protocol.PacketNumber(2))) 452 Expect(readPacketNumber(<-clientReceivedPackets)).To(Equal(protocol.PacketNumber(3))) 453 }) 454 }) 455 }) 456 })