github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/cmd/docker-proxy/network_proxy_test.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "net" 8 "runtime" 9 "strings" 10 "testing" 11 "time" 12 13 "github.com/ishidawataru/sctp" 14 "gotest.tools/v3/skip" 15 ) 16 17 var testBuf = []byte("Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo") 18 var testBufSize = len(testBuf) 19 20 type EchoServer interface { 21 Run() 22 Close() 23 LocalAddr() net.Addr 24 } 25 26 type EchoServerOptions struct { 27 TCPHalfClose bool 28 } 29 30 type StreamEchoServer struct { 31 listener net.Listener 32 testCtx *testing.T 33 opts EchoServerOptions 34 } 35 36 type UDPEchoServer struct { 37 conn net.PacketConn 38 testCtx *testing.T 39 } 40 41 func NewEchoServer(t *testing.T, proto, address string, opts EchoServerOptions) EchoServer { 42 var server EchoServer 43 if !strings.HasPrefix(proto, "tcp") && opts.TCPHalfClose { 44 t.Fatalf("TCPHalfClose is not supported for %s", proto) 45 } 46 47 switch { 48 case strings.HasPrefix(proto, "tcp"): 49 listener, err := net.Listen(proto, address) 50 if err != nil { 51 t.Fatal(err) 52 } 53 server = &StreamEchoServer{listener: listener, testCtx: t, opts: opts} 54 case strings.HasPrefix(proto, "udp"): 55 socket, err := net.ListenPacket(proto, address) 56 if err != nil { 57 t.Fatal(err) 58 } 59 server = &UDPEchoServer{conn: socket, testCtx: t} 60 case strings.HasPrefix(proto, "sctp"): 61 addr, err := sctp.ResolveSCTPAddr(proto, address) 62 if err != nil { 63 t.Fatal(err) 64 } 65 listener, err := sctp.ListenSCTP(proto, addr) 66 if err != nil { 67 t.Fatal(err) 68 } 69 server = &StreamEchoServer{listener: listener, testCtx: t} 70 default: 71 t.Fatalf("unknown protocol: %s", proto) 72 } 73 return server 74 } 75 76 func (server *StreamEchoServer) Run() { 77 go func() { 78 for { 79 client, err := server.listener.Accept() 80 if err != nil { 81 return 82 } 83 go func(client net.Conn) { 84 if server.opts.TCPHalfClose { 85 data, err := io.ReadAll(client) 86 if err != nil { 87 server.testCtx.Logf("io.ReadAll() failed for the client: %v\n", err.Error()) 88 } 89 if _, err := client.Write(data); err != nil { 90 server.testCtx.Logf("can't echo to the client: %v\n", err.Error()) 91 } 92 client.(*net.TCPConn).CloseWrite() 93 } else { 94 if _, err := io.Copy(client, client); err != nil { 95 server.testCtx.Logf("can't echo to the client: %v\n", err.Error()) 96 } 97 client.Close() 98 } 99 }(client) 100 } 101 }() 102 } 103 104 func (server *StreamEchoServer) LocalAddr() net.Addr { return server.listener.Addr() } 105 func (server *StreamEchoServer) Close() { server.listener.Close() } 106 107 func (server *UDPEchoServer) Run() { 108 go func() { 109 readBuf := make([]byte, 1024) 110 for { 111 read, from, err := server.conn.ReadFrom(readBuf) 112 if err != nil { 113 return 114 } 115 for i := 0; i != read; { 116 written, err := server.conn.WriteTo(readBuf[i:read], from) 117 if err != nil { 118 break 119 } 120 i += written 121 } 122 } 123 }() 124 } 125 126 func (server *UDPEchoServer) LocalAddr() net.Addr { return server.conn.LocalAddr() } 127 func (server *UDPEchoServer) Close() { server.conn.Close() } 128 129 func testProxyAt(t *testing.T, proto string, proxy Proxy, addr string, halfClose bool) { 130 defer proxy.Close() 131 go proxy.Run() 132 var client net.Conn 133 var err error 134 if strings.HasPrefix(proto, "sctp") { 135 var a *sctp.SCTPAddr 136 a, err = sctp.ResolveSCTPAddr(proto, addr) 137 if err != nil { 138 t.Fatal(err) 139 } 140 client, err = sctp.DialSCTP(proto, nil, a) 141 } else { 142 client, err = net.Dial(proto, addr) 143 } 144 145 if err != nil { 146 t.Fatalf("Can't connect to the proxy: %v", err) 147 } 148 defer client.Close() 149 client.SetDeadline(time.Now().Add(10 * time.Second)) 150 if _, err = client.Write(testBuf); err != nil { 151 t.Fatal(err) 152 } 153 if halfClose { 154 if proto != "tcp" { 155 t.Fatalf("halfClose is not supported for %s", proto) 156 } 157 client.(*net.TCPConn).CloseWrite() 158 } 159 recvBuf := make([]byte, testBufSize) 160 if _, err = client.Read(recvBuf); err != nil { 161 t.Fatal(err) 162 } 163 if !bytes.Equal(testBuf, recvBuf) { 164 t.Fatal(fmt.Errorf("Expected [%v] but got [%v]", testBuf, recvBuf)) 165 } 166 } 167 168 func testProxy(t *testing.T, proto string, proxy Proxy, halfClose bool) { 169 testProxyAt(t, proto, proxy, proxy.FrontendAddr().String(), halfClose) 170 } 171 172 func testTCP4Proxy(t *testing.T, halfClose bool) { 173 backend := NewEchoServer(t, "tcp", "127.0.0.1:0", EchoServerOptions{TCPHalfClose: halfClose}) 174 defer backend.Close() 175 backend.Run() 176 frontendAddr := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0} 177 proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) 178 if err != nil { 179 t.Fatal(err) 180 } 181 testProxy(t, "tcp", proxy, halfClose) 182 } 183 184 func TestTCP4Proxy(t *testing.T) { 185 testTCP4Proxy(t, false) 186 } 187 188 func TestTCP4ProxyHalfClose(t *testing.T) { 189 testTCP4Proxy(t, true) 190 } 191 192 func TestTCP6Proxy(t *testing.T) { 193 t.Skip("Need to start CI docker with --ipv6") 194 backend := NewEchoServer(t, "tcp", "[::1]:0", EchoServerOptions{}) 195 defer backend.Close() 196 backend.Run() 197 frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0} 198 proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) 199 if err != nil { 200 t.Fatal(err) 201 } 202 testProxy(t, "tcp", proxy, false) 203 } 204 205 func TestTCPDualStackProxy(t *testing.T) { 206 // If I understand `godoc -src net favoriteAddrFamily` (used by the 207 // net.Listen* functions) correctly this should work, but it doesn't. 208 t.Skip("No support for dual stack yet") 209 backend := NewEchoServer(t, "tcp", "[::1]:0", EchoServerOptions{}) 210 defer backend.Close() 211 backend.Run() 212 frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0} 213 proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) 214 if err != nil { 215 t.Fatal(err) 216 } 217 ipv4ProxyAddr := &net.TCPAddr{ 218 IP: net.IPv4(127, 0, 0, 1), 219 Port: proxy.FrontendAddr().(*net.TCPAddr).Port, 220 } 221 testProxyAt(t, "tcp", proxy, ipv4ProxyAddr.String(), false) 222 } 223 224 func TestUDP4Proxy(t *testing.T) { 225 backend := NewEchoServer(t, "udp", "127.0.0.1:0", EchoServerOptions{}) 226 defer backend.Close() 227 backend.Run() 228 frontendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0} 229 proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) 230 if err != nil { 231 t.Fatal(err) 232 } 233 testProxy(t, "udp", proxy, false) 234 } 235 236 func TestUDP6Proxy(t *testing.T) { 237 t.Skip("Need to start CI docker with --ipv6") 238 backend := NewEchoServer(t, "udp", "[::1]:0", EchoServerOptions{}) 239 defer backend.Close() 240 backend.Run() 241 frontendAddr := &net.UDPAddr{IP: net.IPv6loopback, Port: 0} 242 proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) 243 if err != nil { 244 t.Fatal(err) 245 } 246 testProxy(t, "udp", proxy, false) 247 } 248 249 func TestUDPWriteError(t *testing.T) { 250 frontendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0} 251 // Hopefully, this port will be free: */ 252 backendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 25587} 253 proxy, err := NewProxy(frontendAddr, backendAddr) 254 if err != nil { 255 t.Fatal(err) 256 } 257 defer proxy.Close() 258 go proxy.Run() 259 client, err := net.Dial("udp", "127.0.0.1:25587") 260 if err != nil { 261 t.Fatalf("Can't connect to the proxy: %v", err) 262 } 263 defer client.Close() 264 // Make sure the proxy doesn't stop when there is no actual backend: 265 client.Write(testBuf) 266 client.Write(testBuf) 267 backend := NewEchoServer(t, "udp", "127.0.0.1:25587", EchoServerOptions{}) 268 defer backend.Close() 269 backend.Run() 270 client.SetDeadline(time.Now().Add(10 * time.Second)) 271 if _, err = client.Write(testBuf); err != nil { 272 t.Fatal(err) 273 } 274 recvBuf := make([]byte, testBufSize) 275 if _, err = client.Read(recvBuf); err != nil { 276 t.Fatal(err) 277 } 278 if !bytes.Equal(testBuf, recvBuf) { 279 t.Fatal(fmt.Errorf("Expected [%v] but got [%v]", testBuf, recvBuf)) 280 } 281 } 282 283 func TestSCTP4Proxy(t *testing.T) { 284 skip.If(t, runtime.GOOS == "windows", "sctp is not supported on windows") 285 286 backend := NewEchoServer(t, "sctp", "127.0.0.1:0", EchoServerOptions{}) 287 defer backend.Close() 288 backend.Run() 289 frontendAddr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.IPv4(127, 0, 0, 1)}}, Port: 0} 290 proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) 291 if err != nil { 292 t.Fatal(err) 293 } 294 testProxy(t, "sctp", proxy, false) 295 } 296 297 func TestSCTP6Proxy(t *testing.T) { 298 t.Skip("Need to start CI docker with --ipv6") 299 skip.If(t, runtime.GOOS == "windows", "sctp is not supported on windows") 300 301 backend := NewEchoServer(t, "sctp", "[::1]:0", EchoServerOptions{}) 302 defer backend.Close() 303 backend.Run() 304 frontendAddr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.IPv6loopback}}, Port: 0} 305 proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) 306 if err != nil { 307 t.Fatal(err) 308 } 309 testProxy(t, "sctp", proxy, false) 310 }