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 }