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.

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