]> Sergey Matveev's repositories - btrtrc.git/blobdiff - client_test.go
Drop support for go 1.20
[btrtrc.git] / client_test.go
index c626ff890ddedaf0b8346a2abc604c84351cb06d..d2a88e9e7a769abcd1871092c006e37b33573a51 100644 (file)
@@ -4,7 +4,8 @@ import (
        "encoding/binary"
        "fmt"
        "io"
-       "io/ioutil"
+       "net"
+       "net/netip"
        "os"
        "path/filepath"
        "reflect"
@@ -12,14 +13,14 @@ import (
        "testing/iotest"
        "time"
 
-       "github.com/bradfitz/iter"
-       "github.com/frankban/quicktest"
-       "github.com/stretchr/testify/assert"
-       "github.com/stretchr/testify/require"
-
        "github.com/anacrolix/dht/v2"
+       "github.com/anacrolix/log"
        "github.com/anacrolix/missinggo/v2"
        "github.com/anacrolix/missinggo/v2/filecache"
+       "github.com/frankban/quicktest"
+       qt "github.com/frankban/quicktest"
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
 
        "github.com/anacrolix/torrent/bencode"
        "github.com/anacrolix/torrent/internal/testutil"
@@ -31,7 +32,7 @@ import (
 func TestClientDefault(t *testing.T) {
        cl, err := NewClient(TestingConfig(t))
        require.NoError(t, err)
-       cl.Close()
+       require.Empty(t, cl.Close())
 }
 
 func TestClientNilConfig(t *testing.T) {
@@ -41,7 +42,7 @@ func TestClientNilConfig(t *testing.T) {
        os.Chdir(t.TempDir())
        cl, err := NewClient(nil)
        require.NoError(t, err)
-       cl.Close()
+       require.Empty(t, cl.Close())
 }
 
 func TestAddDropTorrent(t *testing.T) {
@@ -80,9 +81,8 @@ func TestPieceHashSize(t *testing.T) {
 func TestTorrentInitialState(t *testing.T) {
        dir, mi := testutil.GreetingTestTorrent()
        defer os.RemoveAll(dir)
-       cl := &Client{
-               config: TestingConfig(t),
-       }
+       var cl Client
+       cl.init(TestingConfig(t))
        cl.initLogger()
        tor := cl.newTorrent(
                mi.HashInfoBytes(),
@@ -131,7 +131,7 @@ func TestAddDropManyTorrents(t *testing.T) {
        cl, err := NewClient(TestingConfig(t))
        require.NoError(t, err)
        defer cl.Close()
-       for i := range iter.N(1000) {
+       for i := 0; i < 1000; i += 1 {
                var spec TorrentSpec
                binary.PutVarint(spec.InfoHash[:], int64(i))
                tt, new, err := cl.AddTorrentSpec(&spec)
@@ -203,7 +203,7 @@ func BenchmarkAddLargeTorrent(b *testing.B) {
        require.NoError(b, err)
        defer cl.Close()
        b.ReportAllocs()
-       for range iter.N(b.N) {
+       for i := 0; i < b.N; i += 1 {
                t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
                if err != nil {
                        b.Fatal(err)
@@ -223,9 +223,51 @@ func TestResponsive(t *testing.T) {
        defer seeder.Close()
        seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
        seederTorrent.VerifyData()
-       leecherDataDir, err := ioutil.TempDir("", "")
+       leecherDataDir := t.TempDir()
+       cfg = TestingConfig(t)
+       cfg.DataDir = leecherDataDir
+       leecher, err := NewClient(cfg)
        require.Nil(t, err)
-       defer os.RemoveAll(leecherDataDir)
+       defer leecher.Close()
+       leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
+               ret = TorrentSpecFromMetaInfo(mi)
+               ret.ChunkSize = 2
+               return
+       }())
+       leecherTorrent.AddClientPeer(seeder)
+       reader := leecherTorrent.NewReader()
+       defer reader.Close()
+       reader.SetReadahead(0)
+       reader.SetResponsive()
+       b := make([]byte, 2)
+       _, err = reader.Seek(3, io.SeekStart)
+       require.NoError(t, err)
+       _, err = io.ReadFull(reader, b)
+       assert.Nil(t, err)
+       assert.EqualValues(t, "lo", string(b))
+       _, err = reader.Seek(11, io.SeekStart)
+       require.NoError(t, err)
+       n, err := io.ReadFull(reader, b)
+       assert.Nil(t, err)
+       assert.EqualValues(t, 2, n)
+       assert.EqualValues(t, "d\n", string(b))
+}
+
+// TestResponsive was the first test to fail if uTP is disabled and TCP sockets dial from the
+// listening port.
+func TestResponsiveTcpOnly(t *testing.T) {
+       seederDataDir, mi := testutil.GreetingTestTorrent()
+       defer os.RemoveAll(seederDataDir)
+       cfg := TestingConfig(t)
+       cfg.DisableUTP = true
+       cfg.Seed = true
+       cfg.DataDir = seederDataDir
+       seeder, err := NewClient(cfg)
+       require.Nil(t, err)
+       defer seeder.Close()
+       seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
+       seederTorrent.VerifyData()
+       leecherDataDir := t.TempDir()
        cfg = TestingConfig(t)
        cfg.DataDir = leecherDataDir
        leecher, err := NewClient(cfg)
@@ -266,9 +308,7 @@ func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
        defer seeder.Close()
        seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
        seederTorrent.VerifyData()
-       leecherDataDir, err := ioutil.TempDir("", "")
-       require.Nil(t, err)
-       defer os.RemoveAll(leecherDataDir)
+       leecherDataDir := t.TempDir()
        cfg = TestingConfig(t)
        cfg.DataDir = leecherDataDir
        leecher, err := NewClient(cfg)
@@ -290,9 +330,9 @@ func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
        _, err = io.ReadFull(reader, b)
        assert.Nil(t, err)
        assert.EqualValues(t, "lo", string(b))
-       go leecherTorrent.Drop()
        _, err = reader.Seek(11, io.SeekStart)
        require.NoError(t, err)
+       leecherTorrent.Drop()
        n, err := reader.Read(b)
        assert.EqualError(t, err, "torrent closed")
        assert.EqualValues(t, 0, n)
@@ -354,16 +394,14 @@ func TestTorrentDroppedBeforeGotInfo(t *testing.T) {
 }
 
 func writeTorrentData(ts *storage.Torrent, info metainfo.Info, b []byte) {
-       for i := range iter.N(info.NumPieces()) {
+       for i := 0; i < info.NumPieces(); i += 1 {
                p := info.Piece(i)
                ts.Piece(p).WriteAt(b[p.Offset():p.Offset()+p.Length()], 0)
        }
 }
 
 func testAddTorrentPriorPieceCompletion(t *testing.T, alreadyCompleted bool, csf func(*filecache.Cache) storage.ClientImpl) {
-       fileCacheDir, err := ioutil.TempDir("", "")
-       require.NoError(t, err)
-       defer os.RemoveAll(fileCacheDir)
+       fileCacheDir := t.TempDir()
        fileCache, err := filecache.NewCache(fileCacheDir)
        require.NoError(t, err)
        greetingDataTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
@@ -458,9 +496,7 @@ func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
        defer testutil.ExportStatusWriter(seeder, "s", t)()
        seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
        seederTorrent.VerifyData()
-       leecherDataDir, err := ioutil.TempDir("", "")
-       require.NoError(t, err)
-       defer os.RemoveAll(leecherDataDir)
+       leecherDataDir := t.TempDir()
        fc, err := filecache.NewCache(leecherDataDir)
        require.NoError(t, err)
        if ps.SetLeecherStorageCapacity {
@@ -485,7 +521,7 @@ func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
        leecherGreeting.cl.lock()
        leecherGreeting.downloadPiecesLocked(0, leecherGreeting.numPieces())
        if ps.Cancel {
-               leecherGreeting.cancelPiecesLocked(0, leecherGreeting.NumPieces())
+               leecherGreeting.cancelPiecesLocked(0, leecherGreeting.NumPieces(), "")
        }
        leecherGreeting.cl.unlock()
        done := make(chan struct{})
@@ -500,8 +536,7 @@ func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
                }
        }()
        for !reflect.DeepEqual(completes, expected) {
-               _v := <-psc.Values
-               v := _v.(PieceStateChange)
+               v := <-psc.Values
                completes[v.Index] = v.Complete
        }
 }
@@ -539,8 +574,10 @@ func TestPeerInvalidHave(t *testing.T) {
        assert.True(t, _new)
        defer tt.Drop()
        cn := &PeerConn{Peer: Peer{
-               t: tt,
+               t:         tt,
+               callbacks: &cfg.Callbacks,
        }}
+       tt.conns[cn] = struct{}{}
        cn.peerImpl = cn
        cl.lock()
        defer cl.unlock()
@@ -555,6 +592,7 @@ func TestPieceCompletedInStorageButNotClient(t *testing.T) {
        cfg.DataDir = greetingTempDir
        seeder, err := NewClient(TestingConfig(t))
        require.NoError(t, err)
+       defer seeder.Close()
        seeder.AddTorrentSpec(&TorrentSpec{
                InfoBytes: greetingMetainfo.InfoBytes,
        })
@@ -609,7 +647,7 @@ func TestSetMaxEstablishedConn(t *testing.T) {
        cfg := TestingConfig(t)
        cfg.DisableAcceptRateLimiting = true
        cfg.DropDuplicatePeerIds = true
-       for i := range iter.N(3) {
+       for i := 0; i < 3; i += 1 {
                cl, err := NewClient(cfg)
                require.NoError(t, err)
                defer cl.Close()
@@ -649,8 +687,8 @@ func TestSetMaxEstablishedConn(t *testing.T) {
 
 // Creates a file containing its own name as data. Make a metainfo from that, adds it to the given
 // client, and returns a magnet link.
-func makeMagnet(t *testing.T, cl *Client, dir string, name string) string {
-       os.MkdirAll(dir, 0770)
+func makeMagnet(t *testing.T, cl *Client, dir, name string) string {
+       os.MkdirAll(dir, 0o770)
        file, err := os.Create(filepath.Join(dir, name))
        require.NoError(t, err)
        file.Write([]byte(name))
@@ -686,11 +724,11 @@ func TestMultipleTorrentsWithEncryption(t *testing.T) {
 
 // Test that the leecher can download a torrent in its entirety from the seeder. Note that the
 // seeder config is done first.
-func testSeederLeecherPair(t *testing.T, seeder func(*ClientConfig), leecher func(*ClientConfig)) {
+func testSeederLeecherPair(t *testing.T, seeder, leecher func(*ClientConfig)) {
        cfg := TestingConfig(t)
        cfg.Seed = true
        cfg.DataDir = filepath.Join(cfg.DataDir, "server")
-       os.Mkdir(cfg.DataDir, 0755)
+       os.Mkdir(cfg.DataDir, 0o755)
        seeder(cfg)
        server, err := NewClient(cfg)
        require.NoError(t, err)
@@ -750,12 +788,16 @@ func TestObfuscatedHeaderFallbackSeederRequiresLeecherPrefersNot(t *testing.T) {
 }
 
 func TestClientAddressInUse(t *testing.T) {
-       s, _ := NewUtpSocket("udp", ":50007", nil)
+       s, _ := NewUtpSocket("udp", "localhost:50007", nil, log.Default)
        if s != nil {
                defer s.Close()
        }
-       cfg := TestingConfig(t).SetListenAddr(":50007")
+       cfg := TestingConfig(t).SetListenAddr("localhost:50007")
+       cfg.DisableUTP = false
        cl, err := NewClient(cfg)
+       if err == nil {
+               assert.Nil(t, cl.Close())
+       }
        require.Error(t, err)
        require.Nil(t, cl)
 }
@@ -781,3 +823,87 @@ func TestClientDisabledImplicitNetworksButDhtEnabled(t *testing.T) {
        assert.Empty(t, cl.listeners)
        assert.NotEmpty(t, cl.DhtServers())
 }
+
+func TestBadPeerIpPort(t *testing.T) {
+       for _, tc := range []struct {
+               title      string
+               ip         net.IP
+               port       int
+               expectedOk bool
+               setup      func(*Client)
+       }{
+               {"empty both", nil, 0, true, func(*Client) {}},
+               {"empty/nil ip", nil, 6666, true, func(*Client) {}},
+               {
+                       "empty port",
+                       net.ParseIP("127.0.0.1/32"),
+                       0, true,
+                       func(*Client) {},
+               },
+               {
+                       "in doppleganger addresses",
+                       net.ParseIP("127.0.0.1/32"),
+                       2322,
+                       true,
+                       func(cl *Client) {
+                               cl.dopplegangerAddrs["10.0.0.1:2322"] = struct{}{}
+                       },
+               },
+               {
+                       "in IP block list",
+                       net.ParseIP("10.0.0.1"),
+                       2322,
+                       true,
+                       func(cl *Client) {
+                               cl.ipBlockList = iplist.New([]iplist.Range{
+                                       {First: net.ParseIP("10.0.0.1"), Last: net.ParseIP("10.0.0.255")},
+                               })
+                       },
+               },
+               {
+                       "in bad peer IPs",
+                       net.ParseIP("10.0.0.1"),
+                       2322,
+                       true,
+                       func(cl *Client) {
+                               ipAddr, ok := netip.AddrFromSlice(net.ParseIP("10.0.0.1"))
+                               require.True(t, ok)
+                               cl.badPeerIPs = map[netip.Addr]struct{}{}
+                               cl.badPeerIPs[ipAddr] = struct{}{}
+                       },
+               },
+               {
+                       "good",
+                       net.ParseIP("10.0.0.1"),
+                       2322,
+                       false,
+                       func(cl *Client) {},
+               },
+       } {
+               t.Run(tc.title, func(t *testing.T) {
+                       cfg := TestingConfig(t)
+                       cfg.DisableTCP = true
+                       cfg.DisableUTP = true
+                       cfg.NoDHT = false
+                       cl, err := NewClient(cfg)
+                       require.NoError(t, err)
+                       defer cl.Close()
+
+                       tc.setup(cl)
+                       require.Equal(t, tc.expectedOk, cl.badPeerIPPort(tc.ip, tc.port))
+               })
+       }
+}
+
+// https://github.com/anacrolix/torrent/issues/837
+func TestClientConfigSetHandlerNotIgnored(t *testing.T) {
+       cfg := TestingConfig(t)
+       cfg.Logger.SetHandlers(log.DiscardHandler)
+       c := qt.New(t)
+       cl, err := NewClient(cfg)
+       c.Assert(err, qt.IsNil)
+       defer cl.Close()
+       c.Assert(cl.logger.Handlers, qt.HasLen, 1)
+       h := cl.logger.Handlers[0].(log.StreamHandler)
+       c.Check(h.W, qt.Equals, io.Discard)
+}