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