github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/p2p/conn/connection_test.go (about) 1 package conn 2 3 import ( 4 "net" 5 "testing" 6 "time" 7 8 "github.com/fortytw2/leaktest" 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11 12 "github.com/tendermint/tendermint/libs/log" 13 "github.com/tendermint/tendermint/libs/protoio" 14 tmp2p "github.com/tendermint/tendermint/proto/tendermint/p2p" 15 "github.com/tendermint/tendermint/proto/tendermint/types" 16 ) 17 18 const maxPingPongPacketSize = 1024 // bytes 19 20 func createTestMConnection(conn net.Conn) *MConnection { 21 onReceive := func(chID byte, msgBytes []byte) { 22 } 23 onError := func(r interface{}) { 24 } 25 c := createMConnectionWithCallbacks(conn, onReceive, onError) 26 c.SetLogger(log.TestingLogger()) 27 return c 28 } 29 30 func createMConnectionWithCallbacks( 31 conn net.Conn, 32 onReceive func(chID byte, msgBytes []byte), 33 onError func(r interface{}), 34 ) *MConnection { 35 cfg := DefaultMConnConfig() 36 cfg.PingInterval = 90 * time.Millisecond 37 cfg.PongTimeout = 45 * time.Millisecond 38 chDescs := []*ChannelDescriptor{{ID: 0x01, Priority: 1, SendQueueCapacity: 1}} 39 c := NewMConnectionWithConfig(conn, chDescs, onReceive, onError, cfg) 40 c.SetLogger(log.TestingLogger()) 41 return c 42 } 43 44 func TestMConnectionSendFlushStop(t *testing.T) { 45 server, client := NetPipe() 46 defer server.Close() 47 defer client.Close() 48 49 clientConn := createTestMConnection(client) 50 err := clientConn.Start() 51 require.Nil(t, err) 52 defer clientConn.Stop() // nolint:errcheck // ignore for tests 53 54 msg := []byte("abc") 55 assert.True(t, clientConn.Send(0x01, msg)) 56 57 msgLength := 14 58 59 // start the reader in a new routine, so we can flush 60 errCh := make(chan error) 61 go func() { 62 msgB := make([]byte, msgLength) 63 _, err := server.Read(msgB) 64 if err != nil { 65 t.Error(err) 66 return 67 } 68 errCh <- err 69 }() 70 71 // stop the conn - it should flush all conns 72 clientConn.FlushStop() 73 74 timer := time.NewTimer(3 * time.Second) 75 select { 76 case <-errCh: 77 case <-timer.C: 78 t.Error("timed out waiting for msgs to be read") 79 } 80 } 81 82 func TestMConnectionSend(t *testing.T) { 83 server, client := NetPipe() 84 defer server.Close() 85 defer client.Close() 86 87 mconn := createTestMConnection(client) 88 err := mconn.Start() 89 require.Nil(t, err) 90 defer mconn.Stop() // nolint:errcheck // ignore for tests 91 92 msg := []byte("Ant-Man") 93 assert.True(t, mconn.Send(0x01, msg)) 94 // Note: subsequent Send/TrySend calls could pass because we are reading from 95 // the send queue in a separate goroutine. 96 _, err = server.Read(make([]byte, len(msg))) 97 if err != nil { 98 t.Error(err) 99 } 100 assert.True(t, mconn.CanSend(0x01)) 101 102 msg = []byte("Spider-Man") 103 assert.True(t, mconn.TrySend(0x01, msg)) 104 _, err = server.Read(make([]byte, len(msg))) 105 if err != nil { 106 t.Error(err) 107 } 108 109 assert.False(t, mconn.CanSend(0x05), "CanSend should return false because channel is unknown") 110 assert.False(t, mconn.Send(0x05, []byte("Absorbing Man")), "Send should return false because channel is unknown") 111 } 112 113 func TestMConnectionReceive(t *testing.T) { 114 server, client := NetPipe() 115 defer server.Close() 116 defer client.Close() 117 118 receivedCh := make(chan []byte) 119 errorsCh := make(chan interface{}) 120 onReceive := func(chID byte, msgBytes []byte) { 121 receivedCh <- msgBytes 122 } 123 onError := func(r interface{}) { 124 errorsCh <- r 125 } 126 mconn1 := createMConnectionWithCallbacks(client, onReceive, onError) 127 err := mconn1.Start() 128 require.Nil(t, err) 129 defer mconn1.Stop() // nolint:errcheck // ignore for tests 130 131 mconn2 := createTestMConnection(server) 132 err = mconn2.Start() 133 require.Nil(t, err) 134 defer mconn2.Stop() // nolint:errcheck // ignore for tests 135 136 msg := []byte("Cyclops") 137 assert.True(t, mconn2.Send(0x01, msg)) 138 139 select { 140 case receivedBytes := <-receivedCh: 141 assert.Equal(t, msg, receivedBytes) 142 case err := <-errorsCh: 143 t.Fatalf("Expected %s, got %+v", msg, err) 144 case <-time.After(500 * time.Millisecond): 145 t.Fatalf("Did not receive %s message in 500ms", msg) 146 } 147 } 148 149 func TestMConnectionStatus(t *testing.T) { 150 server, client := NetPipe() 151 defer server.Close() 152 defer client.Close() 153 154 mconn := createTestMConnection(client) 155 err := mconn.Start() 156 require.Nil(t, err) 157 defer mconn.Stop() // nolint:errcheck // ignore for tests 158 159 status := mconn.Status() 160 assert.NotNil(t, status) 161 assert.Zero(t, status.Channels[0].SendQueueSize) 162 } 163 164 func TestMConnectionPongTimeoutResultsInError(t *testing.T) { 165 server, client := net.Pipe() 166 defer server.Close() 167 defer client.Close() 168 169 receivedCh := make(chan []byte) 170 errorsCh := make(chan interface{}) 171 onReceive := func(chID byte, msgBytes []byte) { 172 receivedCh <- msgBytes 173 } 174 onError := func(r interface{}) { 175 errorsCh <- r 176 } 177 mconn := createMConnectionWithCallbacks(client, onReceive, onError) 178 err := mconn.Start() 179 require.Nil(t, err) 180 defer mconn.Stop() // nolint:errcheck // ignore for tests 181 182 serverGotPing := make(chan struct{}) 183 go func() { 184 // read ping 185 var pkt tmp2p.Packet 186 err := protoio.NewDelimitedReader(server, maxPingPongPacketSize).ReadMsg(&pkt) 187 require.NoError(t, err) 188 serverGotPing <- struct{}{} 189 }() 190 <-serverGotPing 191 192 pongTimerExpired := mconn.config.PongTimeout + 20*time.Millisecond 193 select { 194 case msgBytes := <-receivedCh: 195 t.Fatalf("Expected error, but got %v", msgBytes) 196 case err := <-errorsCh: 197 assert.NotNil(t, err) 198 case <-time.After(pongTimerExpired): 199 t.Fatalf("Expected to receive error after %v", pongTimerExpired) 200 } 201 } 202 203 func TestMConnectionMultiplePongsInTheBeginning(t *testing.T) { 204 server, client := net.Pipe() 205 defer server.Close() 206 defer client.Close() 207 208 receivedCh := make(chan []byte) 209 errorsCh := make(chan interface{}) 210 onReceive := func(chID byte, msgBytes []byte) { 211 receivedCh <- msgBytes 212 } 213 onError := func(r interface{}) { 214 errorsCh <- r 215 } 216 mconn := createMConnectionWithCallbacks(client, onReceive, onError) 217 err := mconn.Start() 218 require.Nil(t, err) 219 defer mconn.Stop() // nolint:errcheck // ignore for tests 220 221 // sending 3 pongs in a row (abuse) 222 protoWriter := protoio.NewDelimitedWriter(server) 223 224 _, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{})) 225 require.NoError(t, err) 226 227 _, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{})) 228 require.NoError(t, err) 229 230 _, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{})) 231 require.NoError(t, err) 232 233 serverGotPing := make(chan struct{}) 234 go func() { 235 // read ping (one byte) 236 var packet tmp2p.Packet 237 err := protoio.NewDelimitedReader(server, maxPingPongPacketSize).ReadMsg(&packet) 238 require.NoError(t, err) 239 serverGotPing <- struct{}{} 240 241 // respond with pong 242 _, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{})) 243 require.NoError(t, err) 244 }() 245 <-serverGotPing 246 247 pongTimerExpired := mconn.config.PongTimeout + 20*time.Millisecond 248 select { 249 case msgBytes := <-receivedCh: 250 t.Fatalf("Expected no data, but got %v", msgBytes) 251 case err := <-errorsCh: 252 t.Fatalf("Expected no error, but got %v", err) 253 case <-time.After(pongTimerExpired): 254 assert.True(t, mconn.IsRunning()) 255 } 256 } 257 258 func TestMConnectionMultiplePings(t *testing.T) { 259 server, client := net.Pipe() 260 defer server.Close() 261 defer client.Close() 262 263 receivedCh := make(chan []byte) 264 errorsCh := make(chan interface{}) 265 onReceive := func(chID byte, msgBytes []byte) { 266 receivedCh <- msgBytes 267 } 268 onError := func(r interface{}) { 269 errorsCh <- r 270 } 271 mconn := createMConnectionWithCallbacks(client, onReceive, onError) 272 err := mconn.Start() 273 require.Nil(t, err) 274 defer mconn.Stop() // nolint:errcheck // ignore for tests 275 276 // sending 3 pings in a row (abuse) 277 // see https://github.com/tendermint/tendermint/issues/1190 278 protoReader := protoio.NewDelimitedReader(server, maxPingPongPacketSize) 279 protoWriter := protoio.NewDelimitedWriter(server) 280 var pkt tmp2p.Packet 281 282 _, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPing{})) 283 require.NoError(t, err) 284 285 err = protoReader.ReadMsg(&pkt) 286 require.NoError(t, err) 287 288 _, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPing{})) 289 require.NoError(t, err) 290 291 err = protoReader.ReadMsg(&pkt) 292 require.NoError(t, err) 293 294 _, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPing{})) 295 require.NoError(t, err) 296 297 err = protoReader.ReadMsg(&pkt) 298 require.NoError(t, err) 299 300 assert.True(t, mconn.IsRunning()) 301 } 302 303 func TestMConnectionPingPongs(t *testing.T) { 304 // check that we are not leaking any go-routines 305 defer leaktest.CheckTimeout(t, 10*time.Second)() 306 307 server, client := net.Pipe() 308 309 defer server.Close() 310 defer client.Close() 311 312 receivedCh := make(chan []byte) 313 errorsCh := make(chan interface{}) 314 onReceive := func(chID byte, msgBytes []byte) { 315 receivedCh <- msgBytes 316 } 317 onError := func(r interface{}) { 318 errorsCh <- r 319 } 320 mconn := createMConnectionWithCallbacks(client, onReceive, onError) 321 err := mconn.Start() 322 require.Nil(t, err) 323 defer mconn.Stop() // nolint:errcheck // ignore for tests 324 325 serverGotPing := make(chan struct{}) 326 go func() { 327 protoReader := protoio.NewDelimitedReader(server, maxPingPongPacketSize) 328 protoWriter := protoio.NewDelimitedWriter(server) 329 var pkt tmp2p.PacketPing 330 331 // read ping 332 err = protoReader.ReadMsg(&pkt) 333 require.NoError(t, err) 334 serverGotPing <- struct{}{} 335 336 // respond with pong 337 _, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{})) 338 require.NoError(t, err) 339 340 time.Sleep(mconn.config.PingInterval) 341 342 // read ping 343 err = protoReader.ReadMsg(&pkt) 344 require.NoError(t, err) 345 serverGotPing <- struct{}{} 346 347 // respond with pong 348 _, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{})) 349 require.NoError(t, err) 350 }() 351 <-serverGotPing 352 <-serverGotPing 353 354 pongTimerExpired := (mconn.config.PongTimeout + 20*time.Millisecond) * 2 355 select { 356 case msgBytes := <-receivedCh: 357 t.Fatalf("Expected no data, but got %v", msgBytes) 358 case err := <-errorsCh: 359 t.Fatalf("Expected no error, but got %v", err) 360 case <-time.After(2 * pongTimerExpired): 361 assert.True(t, mconn.IsRunning()) 362 } 363 } 364 365 func TestMConnectionStopsAndReturnsError(t *testing.T) { 366 server, client := NetPipe() 367 defer server.Close() 368 defer client.Close() 369 370 receivedCh := make(chan []byte) 371 errorsCh := make(chan interface{}) 372 onReceive := func(chID byte, msgBytes []byte) { 373 receivedCh <- msgBytes 374 } 375 onError := func(r interface{}) { 376 errorsCh <- r 377 } 378 mconn := createMConnectionWithCallbacks(client, onReceive, onError) 379 err := mconn.Start() 380 require.Nil(t, err) 381 defer mconn.Stop() // nolint:errcheck // ignore for tests 382 383 if err := client.Close(); err != nil { 384 t.Error(err) 385 } 386 387 select { 388 case receivedBytes := <-receivedCh: 389 t.Fatalf("Expected error, got %v", receivedBytes) 390 case err := <-errorsCh: 391 assert.NotNil(t, err) 392 assert.False(t, mconn.IsRunning()) 393 case <-time.After(500 * time.Millisecond): 394 t.Fatal("Did not receive error in 500ms") 395 } 396 } 397 398 func newClientAndServerConnsForReadErrors(t *testing.T, chOnErr chan struct{}) (*MConnection, *MConnection) { 399 server, client := NetPipe() 400 401 onReceive := func(chID byte, msgBytes []byte) {} 402 onError := func(r interface{}) {} 403 404 // create client conn with two channels 405 chDescs := []*ChannelDescriptor{ 406 {ID: 0x01, Priority: 1, SendQueueCapacity: 1}, 407 {ID: 0x02, Priority: 1, SendQueueCapacity: 1}, 408 } 409 mconnClient := NewMConnection(client, chDescs, onReceive, onError) 410 mconnClient.SetLogger(log.TestingLogger().With("module", "client")) 411 err := mconnClient.Start() 412 require.Nil(t, err) 413 414 // create server conn with 1 channel 415 // it fires on chOnErr when there's an error 416 serverLogger := log.TestingLogger().With("module", "server") 417 onError = func(r interface{}) { 418 chOnErr <- struct{}{} 419 } 420 mconnServer := createMConnectionWithCallbacks(server, onReceive, onError) 421 mconnServer.SetLogger(serverLogger) 422 err = mconnServer.Start() 423 require.Nil(t, err) 424 return mconnClient, mconnServer 425 } 426 427 func expectSend(ch chan struct{}) bool { 428 after := time.After(time.Second * 5) 429 select { 430 case <-ch: 431 return true 432 case <-after: 433 return false 434 } 435 } 436 437 func TestMConnectionReadErrorBadEncoding(t *testing.T) { 438 chOnErr := make(chan struct{}) 439 mconnClient, mconnServer := newClientAndServerConnsForReadErrors(t, chOnErr) 440 441 client := mconnClient.conn 442 443 // Write it. 444 _, err := client.Write([]byte{1, 2, 3, 4, 5}) 445 require.NoError(t, err) 446 assert.True(t, expectSend(chOnErr), "badly encoded msgPacket") 447 448 t.Cleanup(func() { 449 if err := mconnClient.Stop(); err != nil { 450 t.Log(err) 451 } 452 }) 453 454 t.Cleanup(func() { 455 if err := mconnServer.Stop(); err != nil { 456 t.Log(err) 457 } 458 }) 459 } 460 461 func TestMConnectionReadErrorUnknownChannel(t *testing.T) { 462 chOnErr := make(chan struct{}) 463 mconnClient, mconnServer := newClientAndServerConnsForReadErrors(t, chOnErr) 464 465 msg := []byte("Ant-Man") 466 467 // fail to send msg on channel unknown by client 468 assert.False(t, mconnClient.Send(0x03, msg)) 469 470 // send msg on channel unknown by the server. 471 // should cause an error 472 assert.True(t, mconnClient.Send(0x02, msg)) 473 assert.True(t, expectSend(chOnErr), "unknown channel") 474 475 t.Cleanup(func() { 476 if err := mconnClient.Stop(); err != nil { 477 t.Log(err) 478 } 479 }) 480 481 t.Cleanup(func() { 482 if err := mconnServer.Stop(); err != nil { 483 t.Log(err) 484 } 485 }) 486 } 487 488 func TestMConnectionReadErrorLongMessage(t *testing.T) { 489 chOnErr := make(chan struct{}) 490 chOnRcv := make(chan struct{}) 491 492 mconnClient, mconnServer := newClientAndServerConnsForReadErrors(t, chOnErr) 493 defer mconnClient.Stop() // nolint:errcheck // ignore for tests 494 defer mconnServer.Stop() // nolint:errcheck // ignore for tests 495 496 mconnServer.onReceive = func(chID byte, msgBytes []byte) { 497 chOnRcv <- struct{}{} 498 } 499 500 client := mconnClient.conn 501 protoWriter := protoio.NewDelimitedWriter(client) 502 503 // send msg thats just right 504 var packet = tmp2p.PacketMsg{ 505 ChannelID: 0x01, 506 EOF: true, 507 Data: make([]byte, mconnClient.config.MaxPacketMsgPayloadSize), 508 } 509 510 _, err := protoWriter.WriteMsg(mustWrapPacket(&packet)) 511 require.NoError(t, err) 512 assert.True(t, expectSend(chOnRcv), "msg just right") 513 514 // send msg thats too long 515 packet = tmp2p.PacketMsg{ 516 ChannelID: 0x01, 517 EOF: true, 518 Data: make([]byte, mconnClient.config.MaxPacketMsgPayloadSize+100), 519 } 520 521 _, err = protoWriter.WriteMsg(mustWrapPacket(&packet)) 522 require.Error(t, err) 523 assert.True(t, expectSend(chOnErr), "msg too long") 524 } 525 526 func TestMConnectionReadErrorUnknownMsgType(t *testing.T) { 527 chOnErr := make(chan struct{}) 528 mconnClient, mconnServer := newClientAndServerConnsForReadErrors(t, chOnErr) 529 defer mconnClient.Stop() // nolint:errcheck // ignore for tests 530 defer mconnServer.Stop() // nolint:errcheck // ignore for tests 531 532 // send msg with unknown msg type 533 _, err := protoio.NewDelimitedWriter(mconnClient.conn).WriteMsg(&types.Header{ChainID: "x"}) 534 require.NoError(t, err) 535 assert.True(t, expectSend(chOnErr), "unknown msg type") 536 } 537 538 func TestMConnectionTrySend(t *testing.T) { 539 server, client := NetPipe() 540 defer server.Close() 541 defer client.Close() 542 543 mconn := createTestMConnection(client) 544 err := mconn.Start() 545 require.Nil(t, err) 546 defer mconn.Stop() // nolint:errcheck // ignore for tests 547 548 msg := []byte("Semicolon-Woman") 549 resultCh := make(chan string, 2) 550 assert.True(t, mconn.TrySend(0x01, msg)) 551 _, err = server.Read(make([]byte, len(msg))) 552 require.NoError(t, err) 553 assert.True(t, mconn.CanSend(0x01)) 554 assert.True(t, mconn.TrySend(0x01, msg)) 555 assert.False(t, mconn.CanSend(0x01)) 556 go func() { 557 mconn.TrySend(0x01, msg) 558 resultCh <- "TrySend" 559 }() 560 assert.False(t, mconn.CanSend(0x01)) 561 assert.False(t, mconn.TrySend(0x01, msg)) 562 assert.Equal(t, "TrySend", <-resultCh) 563 }