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