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