proxy: rename to Proxy, refactor initialization (#4921)

* proxy: rename to Proxy, refactor initialization

* improve documentation
This commit is contained in:
Marten Seemann
2025-01-25 11:13:33 +01:00
committed by GitHub
parent f5145eb627
commit 79bae396b4
18 changed files with 274 additions and 303 deletions

View File

@@ -1,11 +1,8 @@
package quicproxy
import (
"bytes"
"net"
"runtime/pprof"
"strconv"
"strings"
"sync/atomic"
"testing"
"time"
@@ -16,6 +13,14 @@ import (
"github.com/stretchr/testify/require"
)
func newUPDConnLocalhost(t testing.TB) *net.UDPConn {
t.Helper()
conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
require.NoError(t, err)
t.Cleanup(func() { conn.Close() })
return conn
}
type packetData []byte
func makePacket(t *testing.T, p protocol.PacketNumber, payload []byte) []byte {
@@ -47,37 +52,6 @@ func readPacketNumber(t *testing.T, b []byte) protocol.PacketNumber {
return extHdr.PacketNumber
}
func TestProxyShutdown(t *testing.T) {
isProxyRunning := func() bool {
var b bytes.Buffer
pprof.Lookup("goroutine").WriteTo(&b, 1)
return strings.Contains(b.String(), "proxy.(*QuicProxy).runProxy")
}
proxy, err := NewQuicProxy("localhost:0", nil)
require.NoError(t, err)
require.Eventually(t, func() bool { return isProxyRunning() }, time.Second, 10*time.Millisecond)
conn, err := net.DialUDP("udp", nil, proxy.LocalAddr().(*net.UDPAddr))
require.NoError(t, err)
_, err = conn.Write(makePacket(t, 1, []byte("foobar")))
require.NoError(t, err)
require.NoError(t, proxy.Close())
// check that the proxy port is not in use anymore
// sometimes it takes a while for the OS to free the port
require.Eventually(t, func() bool {
ln, err := net.ListenUDP("udp", proxy.LocalAddr().(*net.UDPAddr))
if err != nil {
return false
}
ln.Close()
return true
}, time.Second, 10*time.Millisecond)
require.Eventually(t, func() bool { return !isProxyRunning() }, time.Second, 10*time.Millisecond)
}
// Set up a dumb UDP server.
// In production this would be a QUIC server.
func runServer(t *testing.T) (*net.UDPAddr, chan packetData) {
@@ -116,25 +90,19 @@ func runServer(t *testing.T) (*net.UDPAddr, chan packetData) {
return serverConn.LocalAddr().(*net.UDPAddr), serverReceivedPackets
}
func startProxy(t *testing.T, opts *Opts) (clientConn *net.UDPConn) {
proxy, err := NewQuicProxy("localhost:0", opts)
require.NoError(t, err)
clientConn, err = net.DialUDP("udp", nil, proxy.LocalAddr().(*net.UDPAddr))
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, proxy.Close())
require.NoError(t, clientConn.Close())
})
return clientConn
}
func TestProxyyingBackAndForth(t *testing.T) {
serverAddr, _ := runServer(t)
clientConn := startProxy(t, &Opts{RemoteAddr: serverAddr.String()})
proxy := Proxy{
Conn: newUPDConnLocalhost(t),
ServerAddr: serverAddr,
}
require.NoError(t, proxy.Start())
defer proxy.Close()
clientConn, err := net.DialUDP("udp", nil, proxy.LocalAddr().(*net.UDPAddr))
require.NoError(t, err)
// send the first packet
_, err := clientConn.Write(makePacket(t, 1, []byte("foobar")))
_, err = clientConn.Write(makePacket(t, 1, []byte("foobar")))
require.NoError(t, err)
// send the second packet
_, err = clientConn.Write(makePacket(t, 2, []byte("decafbad")))
@@ -153,15 +121,20 @@ func TestDropIncomingPackets(t *testing.T) {
const numPackets = 6
serverAddr, serverReceivedPackets := runServer(t)
var counter atomic.Int32
clientConn := startProxy(t, &Opts{
RemoteAddr: serverAddr.String(),
proxy := Proxy{
Conn: newUPDConnLocalhost(t),
ServerAddr: serverAddr,
DropPacket: func(d Direction, _ []byte) bool {
if d != DirectionIncoming {
return false
}
return counter.Add(1)%2 == 1
},
})
}
require.NoError(t, proxy.Start())
defer proxy.Close()
clientConn, err := net.DialUDP("udp", nil, proxy.LocalAddr().(*net.UDPAddr))
require.NoError(t, err)
for i := 1; i <= numPackets; i++ {
_, err := clientConn.Write(makePacket(t, protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i))))
@@ -186,15 +159,20 @@ func TestDropOutgoingPackets(t *testing.T) {
const numPackets = 6
serverAddr, serverReceivedPackets := runServer(t)
var counter atomic.Int32
clientConn := startProxy(t, &Opts{
RemoteAddr: serverAddr.String(),
proxy := Proxy{
Conn: newUPDConnLocalhost(t),
ServerAddr: serverAddr,
DropPacket: func(d Direction, _ []byte) bool {
if d != DirectionOutgoing {
return false
}
return counter.Add(1)%2 == 1
},
})
}
require.NoError(t, proxy.Start())
defer proxy.Close()
clientConn, err := net.DialUDP("udp", nil, proxy.LocalAddr().(*net.UDPAddr))
require.NoError(t, err)
clientReceivedPackets := make(chan struct{}, numPackets)
// receive the packets echoed by the server on client side
@@ -234,19 +212,24 @@ func TestDelayIncomingPackets(t *testing.T) {
const delay = 200 * time.Millisecond
serverAddr, serverReceivedPackets := runServer(t)
var counter atomic.Int32
clientConn := startProxy(t, &Opts{
RemoteAddr: serverAddr.String(),
// delay packet 1 by 200 ms
// delay packet 2 by 400 ms
// ...
proxy := Proxy{
Conn: newUPDConnLocalhost(t),
ServerAddr: serverAddr,
DelayPacket: func(d Direction, _ []byte) time.Duration {
// delay packet 1 by 200 ms
// delay packet 2 by 400 ms
// ...
if d == DirectionOutgoing {
return 0
}
p := counter.Add(1)
return time.Duration(p) * delay
},
})
}
require.NoError(t, proxy.Start())
defer proxy.Close()
clientConn, err := net.DialUDP("udp", nil, proxy.LocalAddr().(*net.UDPAddr))
require.NoError(t, err)
start := time.Now()
for i := 1; i <= numPackets; i++ {
@@ -276,19 +259,24 @@ func TestPacketReordering(t *testing.T) {
serverAddr, serverReceivedPackets := runServer(t)
var counter atomic.Int32
clientConn := startProxy(t, &Opts{
RemoteAddr: serverAddr.String(),
// delay packet 1 by 600 ms
// delay packet 2 by 400 ms
// delay packet 3 by 200 ms
proxy := Proxy{
Conn: newUPDConnLocalhost(t),
ServerAddr: serverAddr,
DelayPacket: func(d Direction, _ []byte) time.Duration {
// delay packet 1 by 600 ms
// delay packet 2 by 400 ms
// delay packet 3 by 200 ms
if d == DirectionOutgoing {
return 0
}
p := counter.Add(1)
return 600*time.Millisecond - time.Duration(p-1)*delay
},
})
}
require.NoError(t, proxy.Start())
defer proxy.Close()
clientConn, err := net.DialUDP("udp", nil, proxy.LocalAddr().(*net.UDPAddr))
require.NoError(t, err)
// send 3 packets
start := time.Now()
@@ -310,15 +298,20 @@ func TestPacketReordering(t *testing.T) {
func TestConstantDelay(t *testing.T) { // no reordering expected here
serverAddr, serverReceivedPackets := runServer(t)
clientConn := startProxy(t, &Opts{
RemoteAddr: serverAddr.String(),
proxy := Proxy{
Conn: newUPDConnLocalhost(t),
ServerAddr: serverAddr,
DelayPacket: func(d Direction, _ []byte) time.Duration {
if d == DirectionOutgoing {
return 0
}
return 100 * time.Millisecond
},
})
}
require.NoError(t, proxy.Start())
defer proxy.Close()
clientConn, err := net.DialUDP("udp", nil, proxy.LocalAddr().(*net.UDPAddr))
require.NoError(t, err)
// send 100 packets
for i := 0; i < 100; i++ {
@@ -343,19 +336,24 @@ func TestDelayOutgoingPackets(t *testing.T) {
serverAddr, serverReceivedPackets := runServer(t)
var counter atomic.Int32
clientConn := startProxy(t, &Opts{
RemoteAddr: serverAddr.String(),
// delay packet 1 by 200 ms
// delay packet 2 by 400 ms
// ...
proxy := Proxy{
Conn: newUPDConnLocalhost(t),
ServerAddr: serverAddr,
DelayPacket: func(d Direction, _ []byte) time.Duration {
// delay packet 1 by 200 ms
// delay packet 2 by 400 ms
// ...
if d == DirectionIncoming {
return 0
}
p := counter.Add(1)
return time.Duration(p) * delay
},
})
}
require.NoError(t, proxy.Start())
defer proxy.Close()
clientConn, err := net.DialUDP("udp", nil, proxy.LocalAddr().(*net.UDPAddr))
require.NoError(t, err)
clientReceivedPackets := make(chan packetData, numPackets)
// receive the packets echoed by the server on client side