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  }