github.com/lmb/consul@v1.4.1/connect/proxy/conn_test.go (about)

     1  package proxy
     2  
     3  import (
     4  	"bufio"
     5  	"io"
     6  	"net"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  )
    13  
    14  // Assert io.Closer implementation
    15  var _ io.Closer = new(Conn)
    16  
    17  // testConnPairSetup creates a TCP connection by listening on a random port, and
    18  // returns both ends. Ready to have data sent down them. It also returns a
    19  // closer function that will close both conns and the listener.
    20  func testConnPairSetup(t *testing.T) (net.Conn, net.Conn, func()) {
    21  	t.Helper()
    22  
    23  	l, err := net.Listen("tcp", "localhost:0")
    24  	require.Nil(t, err)
    25  
    26  	ch := make(chan net.Conn, 1)
    27  	go func() {
    28  		src, err := l.Accept()
    29  		require.Nil(t, err)
    30  		ch <- src
    31  	}()
    32  
    33  	dst, err := net.Dial("tcp", l.Addr().String())
    34  	require.Nil(t, err)
    35  
    36  	src := <-ch
    37  
    38  	stopper := func() {
    39  		l.Close()
    40  		src.Close()
    41  		dst.Close()
    42  	}
    43  
    44  	return src, dst, stopper
    45  }
    46  
    47  // testConnPipelineSetup creates a pipeline consiting of two TCP connection
    48  // pairs and a Conn that copies bytes between them. Data flow looks like this:
    49  //
    50  //   src1 <---> dst1 <== Conn.CopyBytes ==> src2 <---> dst2
    51  //
    52  // The returned values are the src1 and dst2 which should be able to send and
    53  // receive to each other via the Conn, the Conn itself (not running), and a
    54  // stopper func to close everything.
    55  func testConnPipelineSetup(t *testing.T) (net.Conn, net.Conn, *Conn, func()) {
    56  	src1, dst1, stop1 := testConnPairSetup(t)
    57  	src2, dst2, stop2 := testConnPairSetup(t)
    58  	c := NewConn(dst1, src2)
    59  	return src1, dst2, c, func() {
    60  		c.Close()
    61  		stop1()
    62  		stop2()
    63  	}
    64  }
    65  
    66  func TestConn(t *testing.T) {
    67  	t.Parallel()
    68  
    69  	src, dst, c, stop := testConnPipelineSetup(t)
    70  	defer stop()
    71  
    72  	retCh := make(chan error, 1)
    73  	go func() {
    74  		retCh <- c.CopyBytes()
    75  	}()
    76  
    77  	// Now write/read into the other ends of the pipes (src1, dst2)
    78  	srcR := bufio.NewReader(src)
    79  	dstR := bufio.NewReader(dst)
    80  
    81  	_, err := src.Write([]byte("ping 1\n"))
    82  	require.Nil(t, err)
    83  	_, err = dst.Write([]byte("ping 2\n"))
    84  	require.Nil(t, err)
    85  
    86  	got, err := dstR.ReadString('\n')
    87  	require.Nil(t, err)
    88  	require.Equal(t, "ping 1\n", got)
    89  
    90  	got, err = srcR.ReadString('\n')
    91  	require.Nil(t, err)
    92  	require.Equal(t, "ping 2\n", got)
    93  
    94  	tx, rx := c.Stats()
    95  	assert.Equal(t, uint64(7), tx)
    96  	assert.Equal(t, uint64(7), rx)
    97  
    98  	_, err = src.Write([]byte("pong 1\n"))
    99  	require.Nil(t, err)
   100  	_, err = dst.Write([]byte("pong 2\n"))
   101  	require.Nil(t, err)
   102  
   103  	got, err = dstR.ReadString('\n')
   104  	require.Nil(t, err)
   105  	require.Equal(t, "pong 1\n", got)
   106  
   107  	got, err = srcR.ReadString('\n')
   108  	require.Nil(t, err)
   109  	require.Equal(t, "pong 2\n", got)
   110  
   111  	tx, rx = c.Stats()
   112  	assert.Equal(t, uint64(14), tx)
   113  	assert.Equal(t, uint64(14), rx)
   114  
   115  	c.Close()
   116  
   117  	ret := <-retCh
   118  	require.Nil(t, ret, "Close() should not cause error return")
   119  }
   120  
   121  func TestConnSrcClosing(t *testing.T) {
   122  	t.Parallel()
   123  
   124  	src, dst, c, stop := testConnPipelineSetup(t)
   125  	defer stop()
   126  
   127  	retCh := make(chan error, 1)
   128  	go func() {
   129  		retCh <- c.CopyBytes()
   130  	}()
   131  
   132  	// Wait until we can actually get some bytes through both ways so we know that
   133  	// the copy goroutines are running.
   134  	srcR := bufio.NewReader(src)
   135  	dstR := bufio.NewReader(dst)
   136  
   137  	_, err := src.Write([]byte("ping 1\n"))
   138  	require.Nil(t, err)
   139  	_, err = dst.Write([]byte("ping 2\n"))
   140  	require.Nil(t, err)
   141  
   142  	got, err := dstR.ReadString('\n')
   143  	require.Nil(t, err)
   144  	require.Equal(t, "ping 1\n", got)
   145  	got, err = srcR.ReadString('\n')
   146  	require.Nil(t, err)
   147  	require.Equal(t, "ping 2\n", got)
   148  
   149  	// If we close the src conn, we expect CopyBytes to return and dst to be
   150  	// closed too. No good way to assert that the conn is closed really other than
   151  	// assume the retCh receive will hang unless CopyBytes exits and that
   152  	// CopyBytes defers Closing both.
   153  	testTimer := time.AfterFunc(3*time.Second, func() {
   154  		panic("test timeout")
   155  	})
   156  	src.Close()
   157  	<-retCh
   158  	testTimer.Stop()
   159  }
   160  
   161  func TestConnDstClosing(t *testing.T) {
   162  	t.Parallel()
   163  
   164  	src, dst, c, stop := testConnPipelineSetup(t)
   165  	defer stop()
   166  
   167  	retCh := make(chan error, 1)
   168  	go func() {
   169  		retCh <- c.CopyBytes()
   170  	}()
   171  
   172  	// Wait until we can actually get some bytes through both ways so we know that
   173  	// the copy goroutines are running.
   174  	srcR := bufio.NewReader(src)
   175  	dstR := bufio.NewReader(dst)
   176  
   177  	_, err := src.Write([]byte("ping 1\n"))
   178  	require.Nil(t, err)
   179  	_, err = dst.Write([]byte("ping 2\n"))
   180  	require.Nil(t, err)
   181  
   182  	got, err := dstR.ReadString('\n')
   183  	require.Nil(t, err)
   184  	require.Equal(t, "ping 1\n", got)
   185  	got, err = srcR.ReadString('\n')
   186  	require.Nil(t, err)
   187  	require.Equal(t, "ping 2\n", got)
   188  
   189  	// If we close the dst conn, we expect CopyBytes to return and src to be
   190  	// closed too. No good way to assert that the conn is closed really other than
   191  	// assume the retCh receive will hang unless CopyBytes exits and that
   192  	// CopyBytes defers Closing both. i.e. if this test doesn't time out it's
   193  	// good!
   194  	testTimer := time.AfterFunc(3*time.Second, func() {
   195  		panic("test timeout")
   196  	})
   197  	src.Close()
   198  	<-retCh
   199  	testTimer.Stop()
   200  }