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.

230 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.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. _, err := transport.Accept()
  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.TestingLogger(),
  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()
  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. ctx, cancel = context.WithCancel(ctx)
  137. defer cancel()
  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()
  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()
  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. }