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.

229 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. transport := p2p.NewMConnTransport(
  139. log.TestingLogger(),
  140. conn.DefaultMConnConfig(),
  141. []*p2p.ChannelDescriptor{{ID: chID, Priority: 1}},
  142. p2p.MConnTransportOptions{},
  143. )
  144. // Transport should not listen on any endpoints yet.
  145. require.Empty(t, transport.Endpoints())
  146. // Start listening, and check any expected errors.
  147. err := transport.Listen(tc.endpoint)
  148. if !tc.ok {
  149. require.Error(t, err)
  150. return
  151. }
  152. require.NoError(t, err)
  153. // Check the endpoint.
  154. endpoints := transport.Endpoints()
  155. require.Len(t, endpoints, 1)
  156. endpoint := endpoints[0]
  157. require.Equal(t, p2p.MConnProtocol, endpoint.Protocol)
  158. if tc.endpoint.IP.IsUnspecified() {
  159. require.True(t, endpoint.IP.IsUnspecified(),
  160. "expected unspecified IP, got %v", endpoint.IP)
  161. } else {
  162. require.True(t, tc.endpoint.IP.Equal(endpoint.IP),
  163. "expected %v, got %v", tc.endpoint.IP, endpoint.IP)
  164. }
  165. require.NotZero(t, endpoint.Port)
  166. require.Empty(t, endpoint.Path)
  167. dialedChan := make(chan struct{})
  168. var peerConn p2p.Connection
  169. go func() {
  170. // Dialing the endpoint should work.
  171. var err error
  172. ctx, cancel := context.WithCancel(context.Background())
  173. defer cancel()
  174. peerConn, err = transport.Dial(ctx, endpoint)
  175. require.NoError(t, err)
  176. close(dialedChan)
  177. }()
  178. conn, err := transport.Accept(ctx)
  179. require.NoError(t, err)
  180. _ = conn.Close()
  181. <-dialedChan
  182. // closing the connection should not error
  183. require.NoError(t, peerConn.Close())
  184. // try to read from the connection should error
  185. _, _, err = peerConn.ReceiveMessage(ctx)
  186. require.Error(t, err)
  187. // Trying to listen again should error.
  188. err = transport.Listen(tc.endpoint)
  189. require.Error(t, err)
  190. // close the transport
  191. _ = transport.Close()
  192. // Dialing the closed endpoint should error
  193. _, err = transport.Dial(ctx, endpoint)
  194. require.Error(t, err)
  195. })
  196. }
  197. }