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