You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

227 lines
6.0 KiB

  1. package p2p_test
  2. import (
  3. "context"
  4. "io"
  5. "net"
  6. "testing"
  7. "time"
  8. "github.com/fortytw2/leaktest"
  9. "github.com/stretchr/testify/require"
  10. "github.com/tendermint/tendermint/internal/p2p"
  11. "github.com/tendermint/tendermint/internal/p2p/conn"
  12. "github.com/tendermint/tendermint/libs/log"
  13. )
  14. // Transports are mainly tested by common tests in transport_test.go, we
  15. // register a transport factory here to get included in those tests.
  16. func init() {
  17. testTransports["mconn"] = func(t *testing.T) p2p.Transport {
  18. transport := p2p.NewMConnTransport(
  19. log.NewNopLogger(),
  20. conn.DefaultMConnConfig(),
  21. []*p2p.ChannelDescriptor{{ID: chID, Priority: 1}},
  22. p2p.MConnTransportOptions{},
  23. )
  24. err := transport.Listen(p2p.Endpoint{
  25. Protocol: p2p.MConnProtocol,
  26. IP: net.IPv4(127, 0, 0, 1),
  27. Port: 0, // assign a random port
  28. })
  29. require.NoError(t, err)
  30. t.Cleanup(func() { _ = transport.Close() })
  31. return transport
  32. }
  33. }
  34. func TestMConnTransport_AcceptBeforeListen(t *testing.T) {
  35. transport := p2p.NewMConnTransport(
  36. log.NewNopLogger(),
  37. conn.DefaultMConnConfig(),
  38. []*p2p.ChannelDescriptor{{ID: chID, Priority: 1}},
  39. p2p.MConnTransportOptions{
  40. MaxAcceptedConnections: 2,
  41. },
  42. )
  43. t.Cleanup(func() {
  44. _ = transport.Close()
  45. })
  46. ctx, cancel := context.WithCancel(context.Background())
  47. defer cancel()
  48. _, err := transport.Accept(ctx)
  49. require.Error(t, err)
  50. require.NotEqual(t, io.EOF, err) // io.EOF should be returned after Close()
  51. }
  52. func TestMConnTransport_AcceptMaxAcceptedConnections(t *testing.T) {
  53. ctx, cancel := context.WithCancel(context.Background())
  54. defer cancel()
  55. transport := p2p.NewMConnTransport(
  56. log.NewNopLogger(),
  57. conn.DefaultMConnConfig(),
  58. []*p2p.ChannelDescriptor{{ID: chID, Priority: 1}},
  59. p2p.MConnTransportOptions{
  60. MaxAcceptedConnections: 2,
  61. },
  62. )
  63. t.Cleanup(func() {
  64. _ = transport.Close()
  65. })
  66. err := transport.Listen(p2p.Endpoint{
  67. Protocol: p2p.MConnProtocol,
  68. IP: net.IPv4(127, 0, 0, 1),
  69. })
  70. require.NoError(t, err)
  71. require.NotEmpty(t, transport.Endpoints())
  72. endpoint := transport.Endpoints()[0]
  73. // Start a goroutine to just accept any connections.
  74. acceptCh := make(chan p2p.Connection, 10)
  75. go func() {
  76. for {
  77. conn, err := transport.Accept(ctx)
  78. if err != nil {
  79. return
  80. }
  81. acceptCh <- conn
  82. }
  83. }()
  84. // The first two connections should be accepted just fine.
  85. dial1, err := transport.Dial(ctx, endpoint)
  86. require.NoError(t, err)
  87. defer dial1.Close()
  88. accept1 := <-acceptCh
  89. defer accept1.Close()
  90. require.Equal(t, dial1.LocalEndpoint(), accept1.RemoteEndpoint())
  91. dial2, err := transport.Dial(ctx, endpoint)
  92. require.NoError(t, err)
  93. defer dial2.Close()
  94. accept2 := <-acceptCh
  95. defer accept2.Close()
  96. require.Equal(t, dial2.LocalEndpoint(), accept2.RemoteEndpoint())
  97. // The third connection will be dialed successfully, but the accept should
  98. // not go through.
  99. dial3, err := transport.Dial(ctx, endpoint)
  100. require.NoError(t, err)
  101. defer dial3.Close()
  102. select {
  103. case <-acceptCh:
  104. require.Fail(t, "unexpected accept")
  105. case <-time.After(time.Second):
  106. }
  107. // However, once either of the other connections are closed, the accept
  108. // goes through.
  109. require.NoError(t, accept1.Close())
  110. accept3 := <-acceptCh
  111. defer accept3.Close()
  112. require.Equal(t, dial3.LocalEndpoint(), accept3.RemoteEndpoint())
  113. }
  114. func TestMConnTransport_Listen(t *testing.T) {
  115. ctx, cancel := context.WithCancel(context.Background())
  116. defer cancel()
  117. testcases := []struct {
  118. endpoint p2p.Endpoint
  119. ok bool
  120. }{
  121. // Valid v4 and v6 addresses, with mconn and tcp protocols.
  122. {p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv4zero}, true},
  123. {p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv4(127, 0, 0, 1)}, true},
  124. {p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv6zero}, true},
  125. {p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv6loopback}, true},
  126. {p2p.Endpoint{Protocol: p2p.TCPProtocol, IP: net.IPv4zero}, true},
  127. // Invalid endpoints.
  128. {p2p.Endpoint{}, false},
  129. {p2p.Endpoint{Protocol: p2p.MConnProtocol, Path: "foo"}, false},
  130. {p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv4zero, Path: "foo"}, false},
  131. }
  132. for _, tc := range testcases {
  133. tc := tc
  134. t.Run(tc.endpoint.String(), func(t *testing.T) {
  135. t.Cleanup(leaktest.Check(t))
  136. transport := p2p.NewMConnTransport(
  137. log.NewNopLogger(),
  138. conn.DefaultMConnConfig(),
  139. []*p2p.ChannelDescriptor{{ID: chID, Priority: 1}},
  140. p2p.MConnTransportOptions{},
  141. )
  142. // Transport should not listen on any endpoints yet.
  143. require.Empty(t, transport.Endpoints())
  144. // Start listening, and check any expected errors.
  145. err := transport.Listen(tc.endpoint)
  146. if !tc.ok {
  147. require.Error(t, err)
  148. return
  149. }
  150. require.NoError(t, err)
  151. // Check the endpoint.
  152. endpoints := transport.Endpoints()
  153. require.Len(t, endpoints, 1)
  154. endpoint := endpoints[0]
  155. require.Equal(t, p2p.MConnProtocol, endpoint.Protocol)
  156. if tc.endpoint.IP.IsUnspecified() {
  157. require.True(t, endpoint.IP.IsUnspecified(),
  158. "expected unspecified IP, got %v", endpoint.IP)
  159. } else {
  160. require.True(t, tc.endpoint.IP.Equal(endpoint.IP),
  161. "expected %v, got %v", tc.endpoint.IP, endpoint.IP)
  162. }
  163. require.NotZero(t, endpoint.Port)
  164. require.Empty(t, endpoint.Path)
  165. dialedChan := make(chan struct{})
  166. var peerConn p2p.Connection
  167. go func() {
  168. // Dialing the endpoint should work.
  169. var err error
  170. ctx, cancel := context.WithCancel(context.Background())
  171. defer cancel()
  172. peerConn, err = transport.Dial(ctx, endpoint)
  173. require.NoError(t, err)
  174. close(dialedChan)
  175. }()
  176. conn, err := transport.Accept(ctx)
  177. require.NoError(t, err)
  178. _ = conn.Close()
  179. <-dialedChan
  180. // closing the connection should not error
  181. require.NoError(t, peerConn.Close())
  182. // try to read from the connection should error
  183. _, _, err = peerConn.ReceiveMessage(ctx)
  184. require.Error(t, err)
  185. // Trying to listen again should error.
  186. err = transport.Listen(tc.endpoint)
  187. require.Error(t, err)
  188. // close the transport
  189. _ = transport.Close()
  190. // Dialing the closed endpoint should error
  191. _, err = transport.Dial(ctx, endpoint)
  192. require.Error(t, err)
  193. })
  194. }
  195. }