]> Sergey Matveev's repositories - btrtrc.git/blobdiff - ut-holepunching_test.go
Drop support for go 1.20
[btrtrc.git] / ut-holepunching_test.go
index 5df98bfbb11b6e1fe74215ef44f98e60ee17966d..ef7cda6ba7770455c22576466e0083cc7cf81c6d 100644 (file)
@@ -18,6 +18,7 @@ import (
        qt "github.com/frankban/quicktest"
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
+       "golang.org/x/time/rate"
 
        "github.com/anacrolix/torrent/internal/testutil"
 )
@@ -36,9 +37,13 @@ func TestHolepunchConnect(t *testing.T) {
        cfg.DisablePEX = true
        cfg.Debug = true
        cfg.AcceptPeerConnections = false
-       // Listening, even without accepting, still means the leecher-leecher completes the dial to the seeder, and so it
-       // won't attempt to holepunch.
+       // Listening, even without accepting, still means the leecher-leecher completes the dial to the
+       // seeder, and so it won't attempt to holepunch.
        cfg.DisableTCP = true
+       // Ensure that responding to holepunch connects don't wait around for the dial limit. We also
+       // have to allow the initial connection to the leecher though, so it can rendezvous for us.
+       cfg.DialRateLimiter = rate.NewLimiter(0, 1)
+       cfg.Logger = cfg.Logger.WithContextText("seeder")
        seeder, err := NewClient(cfg)
        require.NoError(t, err)
        defer seeder.Close()
@@ -52,10 +57,11 @@ func TestHolepunchConnect(t *testing.T) {
        cfg.Seed = true
        cfg.DataDir = t.TempDir()
        cfg.AlwaysWantConns = true
+       cfg.Logger = cfg.Logger.WithContextText("leecher")
        // This way the leecher leecher will still try to use this peer as a relay, but won't be told
        // about the seeder via PEX.
        //cfg.DisablePEX = true
-       //cfg.Debug = true
+       cfg.Debug = true
        leecher, err := NewClient(cfg)
        require.NoError(t, err)
        defer leecher.Close()
@@ -67,6 +73,7 @@ func TestHolepunchConnect(t *testing.T) {
        cfg.MaxAllocPeerRequestDataPerConn = 4
        cfg.Debug = true
        cfg.NominalDialTimeout = time.Second
+       cfg.Logger = cfg.Logger.WithContextText("leecher-leecher")
        //cfg.DisableUTP = true
        leecherLeecher, _ := NewClient(cfg)
        require.NoError(t, err)
@@ -303,6 +310,7 @@ const defaultMsg = "hello"
 // get separate connections. This means that holepunch connect may result in an accept (and dial)
 // for one or both peers involved.
 func TestUtpSimultaneousOpen(t *testing.T) {
+       t.Parallel()
        c := qt.New(t)
        const network = "udp"
        ctx := context.Background()
@@ -318,34 +326,52 @@ func TestUtpSimultaneousOpen(t *testing.T) {
                c.Assert(err, qt.IsNil)
                return socket
        }
-       first := newUtpSocket("localhost:3000")
+       first := newUtpSocket("localhost:0")
        defer first.Close()
-       second := newUtpSocket("localhost:3001")
+       second := newUtpSocket("localhost:0")
        defer second.Close()
        getDial := func(sock utpSocket, addr string) func() (net.Conn, error) {
                return func() (net.Conn, error) {
                        return sock.DialContext(ctx, network, addr)
                }
        }
-       err := testSimultaneousOpen(
-               c.Cleanup,
-               getDial(first, "localhost:3001"),
-               getDial(second, "localhost:3000"),
-       )
-       c.Assert(err, qt.ErrorIs, errMsgNotReceived)
+       t.Logf("first addr is %v. second addr is %v", first.Addr().String(), second.Addr().String())
+       for range iter.N(10) {
+               err := testSimultaneousOpen(
+                       c.Cleanup,
+                       getDial(first, second.Addr().String()),
+                       getDial(second, first.Addr().String()),
+               )
+               if err == nil {
+                       t.Fatal("expected utp to fail simultaneous open")
+               }
+               if errors.Is(err, errMsgNotReceived) {
+                       return
+               }
+               skipGoUtpDialIssue(t, err)
+               t.Log(err)
+               time.Sleep(time.Second)
+       }
+       t.FailNow()
 }
 
-func testDirectDialMsg(c *qt.C, r, w net.Conn) {
+func writeAndReadMsg(r, w net.Conn) error {
        go writeMsg(w)
-       err := readMsg(r)
-       c.Assert(err, qt.IsNil)
+       return readMsg(r)
+}
+
+func skipGoUtpDialIssue(t *testing.T, err error) {
+       if err.Error() == "timed out waiting for ack" {
+               t.Skip("anacrolix go utp implementation has issues. Use anacrolix/go-libutp by enabling CGO.")
+       }
 }
 
 // Show that dialling one socket and accepting from the other results in them having ends of the
 // same connection.
 func TestUtpDirectDialMsg(t *testing.T) {
+       t.Parallel()
        c := qt.New(t)
-       const network = "udp"
+       const network = "udp4"
        ctx := context.Background()
        newUtpSocket := func(addr string) utpSocket {
                socket, err := NewUtpSocket(network, addr, func(net.Addr) bool {
@@ -354,15 +380,28 @@ func TestUtpDirectDialMsg(t *testing.T) {
                c.Assert(err, qt.IsNil)
                return socket
        }
-       first := newUtpSocket("localhost:0")
-       defer first.Close()
-       second := newUtpSocket("localhost:0")
-       defer second.Close()
-       writer, err := first.DialContext(ctx, network, second.Addr().String())
-       c.Assert(err, qt.IsNil)
-       defer writer.Close()
-       reader, err := second.Accept()
-       defer reader.Close()
-       c.Assert(err, qt.IsNil)
-       testDirectDialMsg(c, reader, writer)
+       for range iter.N(10) {
+               err := func() error {
+                       first := newUtpSocket("localhost:0")
+                       defer first.Close()
+                       second := newUtpSocket("localhost:0")
+                       defer second.Close()
+                       writer, err := first.DialContext(ctx, network, second.Addr().String())
+                       if err != nil {
+                               return err
+                       }
+                       defer writer.Close()
+                       reader, err := second.Accept()
+                       defer reader.Close()
+                       c.Assert(err, qt.IsNil)
+                       return writeAndReadMsg(reader, writer)
+               }()
+               if err == nil {
+                       return
+               }
+               skipGoUtpDialIssue(t, err)
+               t.Log(err)
+               time.Sleep(time.Second)
+       }
+       t.FailNow()
 }