package reconnect import ( "context" "io" "math/rand" "runtime" "sync" "testing" "time" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/protocol" bhost "github.com/libp2p/go-libp2p/p2p/host/basic" swarmt "github.com/libp2p/go-libp2p/p2p/net/swarm/testing" "github.com/stretchr/testify/require" ) func EchoStreamHandler(stream network.Stream) { _, err := io.CopyBuffer(stream, stream, make([]byte, 64)) // use a small buffer here to avoid problems with flow control if err == nil { stream.Close() } else { stream.Reset() } } func TestReconnect5(t *testing.T) { runTest := func(t *testing.T, swarmOpt swarmt.Option) { t.Helper() const num = 5 hosts := make([]host.Host, 0, num) for i := 0; i < num; i++ { h, err := bhost.NewHost(swarmt.GenSwarm(t, swarmOpt), nil) require.NoError(t, err) defer h.Close() h.Start() hosts = append(hosts, h) h.SetStreamHandler(protocol.TestingID, EchoStreamHandler) } for i := 0; i < 4; i++ { runRound(t, hosts) } } t.Run("using TCP", func(t *testing.T) { if runtime.GOOS == "darwin" { t.Skip("TCP RST handling is flaky in OSX, see https://github.com/golang/go/issues/50254") } runTest(t, swarmt.OptDisableQUIC) }) t.Run("using QUIC", func(t *testing.T) { runTest(t, swarmt.OptDisableTCP) }) } func runRound(t *testing.T, hosts []host.Host) { for _, h1 := range hosts { h1.SetStreamHandler(protocol.TestingID, EchoStreamHandler) for _, h2 := range hosts { h1.Peerstore().AddAddrs(h2.ID(), h2.Addrs(), time.Hour) } } const ( numStreams = 5 maxDataLen = 64 << 10 ) rnd := rand.New(rand.NewSource(12345)) // exchange some data for _, h1 := range hosts { for _, h2 := range hosts { if h1 == h2 { continue } var wg sync.WaitGroup wg.Add(numStreams) for i := 0; i < numStreams; i++ { data := make([]byte, rand.Intn(maxDataLen)+1) rnd.Read(data) go func() { defer wg.Done() str, err := h1.NewStream(context.Background(), h2.ID(), protocol.TestingID) require.NoError(t, err) defer str.Close() _, err = str.Write(data) require.NoError(t, err) }() } wg.Wait() } } // disconnect all hosts for _, h1 := range hosts { // close connection cs := h1.Network().Conns() for _, c := range cs { c.Close() } } require.Eventually(t, func() bool { for _, h1 := range hosts { for _, h2 := range hosts { if len(h1.Network().ConnsToPeer(h2.ID())) > 0 { return false } } } return true }, 5000*time.Millisecond, 10*time.Millisecond) }