github.com/uber-go/tally/v4@v4.1.17/m3/thriftudp/transport_test.go (about)

     1  // Copyright (c) 2021 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package thriftudp
    22  
    23  import (
    24  	"net"
    25  	"strings"
    26  	"sync"
    27  	"syscall"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/stretchr/testify/assert"
    32  	"github.com/stretchr/testify/require"
    33  )
    34  
    35  var (
    36  	localListenAddr = &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}
    37  	listenConfig    = net.ListenConfig{}
    38  )
    39  
    40  func TestNewTUDPClientTransport(t *testing.T) {
    41  	_, err := NewTUDPClientTransport("fakeAddressAndPort", "")
    42  	require.Error(t, err)
    43  
    44  	_, err = NewTUDPClientTransport("localhost:9090", "fakeaddressandport")
    45  	require.Error(t, err)
    46  
    47  	withLocalServer(t, func(addr string) {
    48  		trans, err := NewTUDPClientTransport(addr, "")
    49  		require.NoError(t, err)
    50  		require.True(t, trans.IsOpen())
    51  		require.NotNil(t, trans.Addr())
    52  
    53  		// Check address
    54  		assert.True(t, strings.HasPrefix(trans.Addr().String(), "127.0.0.1:"), "address check")
    55  		require.Equal(t, "udp", trans.Addr().Network())
    56  
    57  		err = trans.Open()
    58  		require.NoError(t, err)
    59  
    60  		err = trans.Close()
    61  		require.NoError(t, err)
    62  		require.False(t, trans.IsOpen())
    63  	})
    64  }
    65  
    66  func TestNewTUDPServerTransportWithListenConfig(t *testing.T) {
    67  	_, err := NewTUDPServerTransportWithListenConfig("fakeAddressAndPort", listenConfig)
    68  	require.Error(t, err)
    69  
    70  	trans, err := NewTUDPServerTransportWithListenConfig(localListenAddr.String(), listenConfig)
    71  	require.NoError(t, err)
    72  	require.True(t, trans.IsOpen())
    73  	require.Equal(t, ^uint64(0), trans.RemainingBytes())
    74  
    75  	// Ensure a second server can't be created on the same address
    76  	trans2, err := NewTUDPServerTransportWithListenConfig(trans.Addr().String(), listenConfig)
    77  	if trans2 != nil {
    78  		// close the second server if one got created
    79  		trans2.Close()
    80  	}
    81  	require.Error(t, err)
    82  
    83  	require.NoError(t, trans.Close())
    84  	require.False(t, trans.IsOpen())
    85  
    86  	// test if net.ListenConfig is used
    87  	ch := make(chan struct{})
    88  	_, _ = NewTUDPServerTransportWithListenConfig(
    89  		localListenAddr.String(),
    90  		net.ListenConfig{
    91  			Control: func(_, _ string, _ syscall.RawConn) error {
    92  				close(ch)
    93  				return nil
    94  			},
    95  		},
    96  	)
    97  
    98  	select {
    99  	case <-ch:
   100  	case <-time.After(time.Second):
   101  		t.Fatal("expected control function to execute")
   102  	}
   103  }
   104  
   105  func TestTUDPServerTransportIsOpen(t *testing.T) {
   106  	_, err := NewTUDPServerTransport("fakeAddressAndPort")
   107  	require.Error(t, err)
   108  
   109  	trans, err := NewTUDPServerTransport(localListenAddr.String())
   110  	require.NoError(t, err)
   111  	require.True(t, trans.IsOpen())
   112  	require.Equal(t, ^uint64(0), trans.RemainingBytes())
   113  
   114  	wg := sync.WaitGroup{}
   115  	wg.Add(2)
   116  	go func() {
   117  		time.Sleep(2 * time.Millisecond)
   118  		err = trans.Close()
   119  		require.NoError(t, err)
   120  		wg.Done()
   121  	}()
   122  
   123  	go func() {
   124  		for i := 0; i < 4; i++ {
   125  			time.Sleep(1 * time.Millisecond)
   126  			trans.IsOpen()
   127  		}
   128  		wg.Done()
   129  	}()
   130  
   131  	wg.Wait()
   132  	require.False(t, trans.IsOpen())
   133  }
   134  
   135  func TestWriteRead(t *testing.T) {
   136  	server, err := NewTUDPServerTransport(localListenAddr.String())
   137  	require.NoError(t, err)
   138  	defer server.Close()
   139  
   140  	client, err := NewTUDPClientTransport(server.Addr().String(), "")
   141  	require.NoError(t, err)
   142  	defer client.Close()
   143  
   144  	n, err := client.Write([]byte("test"))
   145  	require.NoError(t, err)
   146  	require.Equal(t, 4, n)
   147  	n, err = client.WriteString("string")
   148  	require.NoError(t, err)
   149  	require.Equal(t, 6, n)
   150  	err = client.Flush()
   151  	require.NoError(t, err)
   152  
   153  	expected := []byte("teststring")
   154  	readBuf := make([]byte, 20)
   155  	n, err = server.Read(readBuf)
   156  	require.NoError(t, err)
   157  	require.Equal(t, len(expected), n)
   158  	require.Equal(t, expected, readBuf[0:n])
   159  }
   160  
   161  func TestWriteByteReadByte(t *testing.T) {
   162  	server, err := NewTUDPServerTransport(localListenAddr.String())
   163  	require.NoError(t, err)
   164  	defer server.Close()
   165  
   166  	client, err := NewTUDPClientTransport(server.Addr().String(), "")
   167  	require.NoError(t, err)
   168  	client.Open()
   169  	defer client.Close()
   170  
   171  	for _, b := range []byte("test") {
   172  		err := client.WriteByte(b)
   173  		require.NoError(t, err)
   174  
   175  		err = client.Flush()
   176  		require.NoError(t, err)
   177  	}
   178  
   179  	want := []byte("test")
   180  	for i := range want {
   181  		b, err := server.ReadByte()
   182  		require.NoError(t, err)
   183  		assert.Equal(t, want[i], b, "byte %v mismatch", i)
   184  	}
   185  }
   186  
   187  func TestReadByteEmptyPacket(t *testing.T) {
   188  	server, err := NewTUDPServerTransport(localListenAddr.String())
   189  	require.NoError(t, err)
   190  	defer server.Close()
   191  
   192  	client, err := NewTUDPClientTransport(server.Addr().String(), "")
   193  	require.NoError(t, err)
   194  	defer client.Close()
   195  
   196  	err = client.Flush()
   197  	require.NoError(t, err)
   198  
   199  	_, err = server.ReadByte()
   200  	require.Error(t, err)
   201  }
   202  
   203  func TestIndirectCloseError(t *testing.T) {
   204  	trans, err := NewTUDPServerTransport(localListenAddr.String())
   205  	require.NoError(t, err)
   206  	require.True(t, trans.IsOpen())
   207  
   208  	// Close connection object directly
   209  	conn := trans.Conn()
   210  	require.NotNil(t, conn)
   211  	err = conn.Close()
   212  	require.NoError(t, err, "calling close directly on connection")
   213  
   214  	err = trans.Close()
   215  	require.Error(t, err, "calling close on transport")
   216  }
   217  
   218  // Note: this test is here merely to capture the existing functionality.
   219  // It's questionable whether multiple calls to Close() should succeed or not.
   220  func TestDoubleCloseIsOK(t *testing.T) {
   221  	trans, err := NewTUDPServerTransport(localListenAddr.String())
   222  	require.NoError(t, err)
   223  	require.True(t, trans.IsOpen())
   224  
   225  	conn := trans.Conn()
   226  	require.NotNil(t, conn)
   227  	err = trans.Close()
   228  	require.NoError(t, err, "closing transport")
   229  
   230  	err = trans.Close()
   231  	require.NoError(t, err, "closing transport second time")
   232  }
   233  
   234  func TestConnClosedReadWrite(t *testing.T) {
   235  	trans, err := NewTUDPServerTransport(localListenAddr.String())
   236  	require.NoError(t, err)
   237  	require.True(t, trans.IsOpen())
   238  	trans.Close()
   239  	require.False(t, trans.IsOpen())
   240  
   241  	_, err = trans.Read(make([]byte, 1))
   242  	require.Error(t, err)
   243  
   244  	_, err = trans.ReadByte()
   245  	require.Error(t, err)
   246  
   247  	_, err = trans.Write([]byte("test"))
   248  	require.Error(t, err)
   249  	_, err = trans.WriteString("test")
   250  	require.Error(t, err)
   251  	err = trans.WriteByte('t')
   252  	require.Error(t, err)
   253  }
   254  
   255  func TestHugeWrite(t *testing.T) {
   256  	withLocalServer(t, func(addr string) {
   257  		trans, err := NewTUDPClientTransport(addr, "")
   258  		require.NoError(t, err)
   259  
   260  		hugeMessage := make([]byte, 40000)
   261  		_, err = trans.Write(hugeMessage)
   262  		require.NoError(t, err)
   263  
   264  		// expect buffer to exceed max
   265  		_, err = trans.Write(hugeMessage)
   266  		require.Error(t, err)
   267  
   268  		_, err = trans.WriteString(string(hugeMessage))
   269  		require.Error(t, err)
   270  	})
   271  }
   272  
   273  func TestWriteByteLimit(t *testing.T) {
   274  	withLocalServer(t, func(addr string) {
   275  		trans, err := NewTUDPClientTransport(addr, "")
   276  		require.NoError(t, err)
   277  
   278  		hugeMessage := make([]byte, MaxLength)
   279  		_, err = trans.Write(hugeMessage)
   280  		require.NoError(t, err)
   281  
   282  		// expect buffer to exceed max
   283  		err = trans.WriteByte('a')
   284  		require.Error(t, err)
   285  	})
   286  }
   287  
   288  func TestFlushErrors(t *testing.T) {
   289  	withLocalServer(t, func(addr string) {
   290  		trans, err := NewTUDPClientTransport(addr, "")
   291  		require.NoError(t, err)
   292  
   293  		// flushing closed transport
   294  		trans.Close()
   295  		err = trans.Flush()
   296  		require.Error(t, err)
   297  
   298  		// error when trying to write in flush
   299  		trans, err = NewTUDPClientTransport(addr, "")
   300  		require.NoError(t, err)
   301  		trans.conn.Close()
   302  
   303  		_, err = trans.Write([]byte{1, 2, 3, 4})
   304  		require.NoError(t, err)
   305  		require.Error(t, trans.Flush(), "Flush with data should fail")
   306  	})
   307  }
   308  
   309  func TestResetInFlush(t *testing.T) {
   310  	conn, err := net.ListenUDP(localListenAddr.Network(), localListenAddr)
   311  	require.NoError(t, err, "ListenUDP failed")
   312  
   313  	trans, err := NewTUDPClientTransport(conn.LocalAddr().String(), "")
   314  	require.NoError(t, err)
   315  
   316  	_, err = trans.Write([]byte("some nonsense"))
   317  	require.NoError(t, err)
   318  
   319  	trans.conn.Close() // close the transport's connection via back door
   320  
   321  	require.NotNil(t, trans.Flush(), "should fail to write to closed connection")
   322  	assert.Equal(t, 0, trans.writeBuf.Len(), "should reset the buffer")
   323  }
   324  
   325  func withLocalServer(t *testing.T, f func(addr string)) {
   326  	conn, err := net.ListenUDP(localListenAddr.Network(), localListenAddr)
   327  	require.NoError(t, err, "ListenUDP failed")
   328  
   329  	f(conn.LocalAddr().String())
   330  	require.NoError(t, conn.Close(), "Close failed")
   331  }