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  }