16 "github.com/anacrolix/dht/v2"
17 "github.com/anacrolix/log"
18 "github.com/anacrolix/missinggo/v2"
19 "github.com/anacrolix/missinggo/v2/filecache"
20 "github.com/frankban/quicktest"
21 qt "github.com/frankban/quicktest"
22 "github.com/stretchr/testify/assert"
23 "github.com/stretchr/testify/require"
25 "github.com/anacrolix/torrent/bencode"
26 "github.com/anacrolix/torrent/internal/testutil"
27 "github.com/anacrolix/torrent/iplist"
28 "github.com/anacrolix/torrent/metainfo"
29 "github.com/anacrolix/torrent/storage"
32 func TestClientDefault(t *testing.T) {
33 cl, err := NewClient(TestingConfig(t))
34 require.NoError(t, err)
35 require.Empty(t, cl.Close())
38 func TestClientNilConfig(t *testing.T) {
39 // The default config will put crap in the working directory.
40 origDir, _ := os.Getwd()
41 defer os.Chdir(origDir)
43 cl, err := NewClient(nil)
44 require.NoError(t, err)
45 require.Empty(t, cl.Close())
48 func TestAddDropTorrent(t *testing.T) {
49 cl, err := NewClient(TestingConfig(t))
50 require.NoError(t, err)
52 dir, mi := testutil.GreetingTestTorrent()
53 defer os.RemoveAll(dir)
54 tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
55 require.NoError(t, err)
57 tt.SetMaxEstablishedConns(0)
58 tt.SetMaxEstablishedConns(1)
62 func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
67 func TestAddTorrentNoUsableURLs(t *testing.T) {
72 func TestAddPeersToUnknownTorrent(t *testing.T) {
77 func TestPieceHashSize(t *testing.T) {
78 assert.Equal(t, 20, pieceHash.Size())
81 func TestTorrentInitialState(t *testing.T) {
82 dir, mi := testutil.GreetingTestTorrent()
83 defer os.RemoveAll(dir)
85 cl.init(TestingConfig(t))
89 storage.NewFileWithCompletion(t.TempDir(), storage.NewMapPieceCompletion()),
93 err := tor.setInfoBytesLocked(mi.InfoBytes)
95 require.NoError(t, err)
96 require.Len(t, tor.pieces, 3)
97 tor.pendAllChunkSpecs(0)
99 assert.EqualValues(t, 3, tor.pieceNumPendingChunks(0))
101 assert.EqualValues(t, ChunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
104 func TestReducedDialTimeout(t *testing.T) {
105 cfg := NewDefaultClientConfig()
106 for _, _case := range []struct {
110 ExpectedReduced time.Duration
112 {cfg.NominalDialTimeout, 40, 0, cfg.NominalDialTimeout},
113 {cfg.NominalDialTimeout, 40, 1, cfg.NominalDialTimeout},
114 {cfg.NominalDialTimeout, 40, 39, cfg.NominalDialTimeout},
115 {cfg.NominalDialTimeout, 40, 40, cfg.NominalDialTimeout / 2},
116 {cfg.NominalDialTimeout, 40, 80, cfg.NominalDialTimeout / 3},
117 {cfg.NominalDialTimeout, 40, 4000, cfg.NominalDialTimeout / 101},
119 reduced := reducedDialTimeout(cfg.MinDialTimeout, _case.Max, _case.HalfOpenLimit, _case.PendingPeers)
120 expected := _case.ExpectedReduced
121 if expected < cfg.MinDialTimeout {
122 expected = cfg.MinDialTimeout
124 if reduced != expected {
125 t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
130 func TestAddDropManyTorrents(t *testing.T) {
131 cl, err := NewClient(TestingConfig(t))
132 require.NoError(t, err)
134 for i := 0; i < 1000; i += 1 {
136 binary.PutVarint(spec.InfoHash[:], int64(i))
137 tt, new, err := cl.AddTorrentSpec(&spec)
138 assert.NoError(t, err)
144 func fileCachePieceResourceStorage(fc *filecache.Cache) storage.ClientImpl {
145 return storage.NewResourcePiecesOpts(
146 fc.AsResourceProvider(),
147 storage.ResourcePiecesOpts{
148 LeaveIncompleteChunks: true,
153 func TestMergingTrackersByAddingSpecs(t *testing.T) {
154 cl, err := NewClient(TestingConfig(t))
155 require.NoError(t, err)
157 spec := TorrentSpec{}
158 T, new, _ := cl.AddTorrentSpec(&spec)
162 spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
163 _, new, _ = cl.AddTorrentSpec(&spec)
165 assert.EqualValues(t, [][]string{{"http://a"}, {"udp://b"}}, T.metainfo.AnnounceList)
166 // Because trackers are disabled in TestingConfig.
167 assert.EqualValues(t, 0, len(T.trackerAnnouncers))
170 // We read from a piece which is marked completed, but is missing data.
171 func TestCompletedPieceWrongSize(t *testing.T) {
172 cfg := TestingConfig(t)
173 cfg.DefaultStorage = badStorage{}
174 cl, err := NewClient(cfg)
175 require.NoError(t, err)
177 info := metainfo.Info{
179 Pieces: make([]byte, 20),
180 Files: []metainfo.FileInfo{
181 {Path: []string{"greeting"}, Length: 13},
184 b, err := bencode.Marshal(info)
185 require.NoError(t, err)
186 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
188 InfoHash: metainfo.HashBytes(b),
190 require.NoError(t, err)
195 quicktest.Check(t, iotest.TestReader(r, []byte(testutil.GreetingFileContents)), quicktest.IsNil)
198 func BenchmarkAddLargeTorrent(b *testing.B) {
199 cfg := TestingConfig(b)
200 cfg.DisableTCP = true
201 cfg.DisableUTP = true
202 cl, err := NewClient(cfg)
203 require.NoError(b, err)
206 for i := 0; i < b.N; i += 1 {
207 t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
215 func TestResponsive(t *testing.T) {
216 seederDataDir, mi := testutil.GreetingTestTorrent()
217 defer os.RemoveAll(seederDataDir)
218 cfg := TestingConfig(t)
220 cfg.DataDir = seederDataDir
221 seeder, err := NewClient(cfg)
224 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
225 seederTorrent.VerifyData()
226 leecherDataDir := t.TempDir()
227 cfg = TestingConfig(t)
228 cfg.DataDir = leecherDataDir
229 leecher, err := NewClient(cfg)
231 defer leecher.Close()
232 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
233 ret = TorrentSpecFromMetaInfo(mi)
237 leecherTorrent.AddClientPeer(seeder)
238 reader := leecherTorrent.NewReader()
240 reader.SetReadahead(0)
241 reader.SetResponsive()
243 _, err = reader.Seek(3, io.SeekStart)
244 require.NoError(t, err)
245 _, err = io.ReadFull(reader, b)
247 assert.EqualValues(t, "lo", string(b))
248 _, err = reader.Seek(11, io.SeekStart)
249 require.NoError(t, err)
250 n, err := io.ReadFull(reader, b)
252 assert.EqualValues(t, 2, n)
253 assert.EqualValues(t, "d\n", string(b))
256 // TestResponsive was the first test to fail if uTP is disabled and TCP sockets dial from the
258 func TestResponsiveTcpOnly(t *testing.T) {
259 seederDataDir, mi := testutil.GreetingTestTorrent()
260 defer os.RemoveAll(seederDataDir)
261 cfg := TestingConfig(t)
262 cfg.DisableUTP = true
264 cfg.DataDir = seederDataDir
265 seeder, err := NewClient(cfg)
268 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
269 seederTorrent.VerifyData()
270 leecherDataDir := t.TempDir()
271 cfg = TestingConfig(t)
272 cfg.DataDir = leecherDataDir
273 leecher, err := NewClient(cfg)
275 defer leecher.Close()
276 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
277 ret = TorrentSpecFromMetaInfo(mi)
281 leecherTorrent.AddClientPeer(seeder)
282 reader := leecherTorrent.NewReader()
284 reader.SetReadahead(0)
285 reader.SetResponsive()
287 _, err = reader.Seek(3, io.SeekStart)
288 require.NoError(t, err)
289 _, err = io.ReadFull(reader, b)
291 assert.EqualValues(t, "lo", string(b))
292 _, err = reader.Seek(11, io.SeekStart)
293 require.NoError(t, err)
294 n, err := io.ReadFull(reader, b)
296 assert.EqualValues(t, 2, n)
297 assert.EqualValues(t, "d\n", string(b))
300 func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
301 seederDataDir, mi := testutil.GreetingTestTorrent()
302 defer os.RemoveAll(seederDataDir)
303 cfg := TestingConfig(t)
305 cfg.DataDir = seederDataDir
306 seeder, err := NewClient(cfg)
309 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
310 seederTorrent.VerifyData()
311 leecherDataDir := t.TempDir()
312 cfg = TestingConfig(t)
313 cfg.DataDir = leecherDataDir
314 leecher, err := NewClient(cfg)
316 defer leecher.Close()
317 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
318 ret = TorrentSpecFromMetaInfo(mi)
322 leecherTorrent.AddClientPeer(seeder)
323 reader := leecherTorrent.NewReader()
325 reader.SetReadahead(0)
326 reader.SetResponsive()
328 _, err = reader.Seek(3, io.SeekStart)
329 require.NoError(t, err)
330 _, err = io.ReadFull(reader, b)
332 assert.EqualValues(t, "lo", string(b))
333 _, err = reader.Seek(11, io.SeekStart)
334 require.NoError(t, err)
335 leecherTorrent.Drop()
336 n, err := reader.Read(b)
337 assert.EqualError(t, err, "torrent closed")
338 assert.EqualValues(t, 0, n)
341 func TestDhtInheritBlocklist(t *testing.T) {
342 ipl := iplist.New(nil)
343 require.NotNil(t, ipl)
344 cfg := TestingConfig(t)
345 cfg.IPBlocklist = ipl
347 cl, err := NewClient(cfg)
348 require.NoError(t, err)
351 cl.eachDhtServer(func(s DhtServer) {
353 assert.Equal(t, ipl, s.(AnacrolixDhtServerWrapper).Server.IPBlocklist())
356 assert.EqualValues(t, 2, numServers)
359 // Check that stuff is merged in subsequent AddTorrentSpec for the same
361 func TestAddTorrentSpecMerging(t *testing.T) {
362 cl, err := NewClient(TestingConfig(t))
363 require.NoError(t, err)
365 dir, mi := testutil.GreetingTestTorrent()
366 defer os.RemoveAll(dir)
367 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
368 InfoHash: mi.HashInfoBytes(),
370 require.NoError(t, err)
372 require.Nil(t, tt.Info())
373 _, new, err = cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
374 require.NoError(t, err)
375 require.False(t, new)
376 require.NotNil(t, tt.Info())
379 func TestTorrentDroppedBeforeGotInfo(t *testing.T) {
380 dir, mi := testutil.GreetingTestTorrent()
382 cl, _ := NewClient(TestingConfig(t))
384 tt, _, _ := cl.AddTorrentSpec(&TorrentSpec{
385 InfoHash: mi.HashInfoBytes(),
388 assert.EqualValues(t, 0, len(cl.Torrents()))
396 func writeTorrentData(ts *storage.Torrent, info metainfo.Info, b []byte) {
397 for i := 0; i < info.NumPieces(); i += 1 {
399 ts.Piece(p).WriteAt(b[p.Offset():p.Offset()+p.Length()], 0)
403 func testAddTorrentPriorPieceCompletion(t *testing.T, alreadyCompleted bool, csf func(*filecache.Cache) storage.ClientImpl) {
404 fileCacheDir := t.TempDir()
405 fileCache, err := filecache.NewCache(fileCacheDir)
406 require.NoError(t, err)
407 greetingDataTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
408 defer os.RemoveAll(greetingDataTempDir)
409 filePieceStore := csf(fileCache)
410 info, err := greetingMetainfo.UnmarshalInfo()
411 require.NoError(t, err)
412 ih := greetingMetainfo.HashInfoBytes()
413 greetingData, err := storage.NewClient(filePieceStore).OpenTorrent(&info, ih)
414 require.NoError(t, err)
415 writeTorrentData(greetingData, info, []byte(testutil.GreetingFileContents))
416 // require.Equal(t, len(testutil.GreetingFileContents), written)
417 // require.NoError(t, err)
418 for i := 0; i < info.NumPieces(); i++ {
420 if alreadyCompleted {
421 require.NoError(t, greetingData.Piece(p).MarkComplete())
424 cfg := TestingConfig(t)
425 // TODO: Disable network option?
426 cfg.DisableTCP = true
427 cfg.DisableUTP = true
428 cfg.DefaultStorage = filePieceStore
429 cl, err := NewClient(cfg)
430 require.NoError(t, err)
432 tt, err := cl.AddTorrent(greetingMetainfo)
433 require.NoError(t, err)
434 psrs := tt.PieceStateRuns()
435 assert.Len(t, psrs, 1)
436 assert.EqualValues(t, 3, psrs[0].Length)
437 assert.Equal(t, alreadyCompleted, psrs[0].Complete)
438 if alreadyCompleted {
440 quicktest.Check(t, iotest.TestReader(r, []byte(testutil.GreetingFileContents)), quicktest.IsNil)
444 func TestAddTorrentPiecesAlreadyCompleted(t *testing.T) {
445 testAddTorrentPriorPieceCompletion(t, true, fileCachePieceResourceStorage)
448 func TestAddTorrentPiecesNotAlreadyCompleted(t *testing.T) {
449 testAddTorrentPriorPieceCompletion(t, false, fileCachePieceResourceStorage)
452 func TestAddMetainfoWithNodes(t *testing.T) {
453 cfg := TestingConfig(t)
454 cfg.ListenHost = func(string) string { return "" }
456 cfg.DhtStartingNodes = func(string) dht.StartingNodesGetter { return func() ([]dht.Addr, error) { return nil, nil } }
457 // For now, we want to just jam the nodes into the table, without verifying them first. Also the
458 // DHT code doesn't support mixing secure and insecure nodes if security is enabled (yet).
459 // cfg.DHTConfig.NoSecurity = true
460 cl, err := NewClient(cfg)
461 require.NoError(t, err)
463 sum := func() (ret int64) {
464 cl.eachDhtServer(func(s DhtServer) {
465 ret += s.Stats().(dht.ServerStats).OutboundQueriesAttempted
469 assert.EqualValues(t, 0, sum())
470 tt, err := cl.AddTorrentFromFile("metainfo/testdata/issue_65a.torrent")
471 require.NoError(t, err)
472 // Nodes are not added or exposed in Torrent's metainfo. We just randomly
473 // check if the announce-list is here instead. TODO: Add nodes.
474 assert.Len(t, tt.metainfo.AnnounceList, 5)
475 // There are 6 nodes in the torrent file.
476 for sum() != int64(6*len(cl.dhtServers)) {
477 time.Sleep(time.Millisecond)
481 type testDownloadCancelParams struct {
482 SetLeecherStorageCapacity bool
483 LeecherStorageCapacity int64
487 func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
488 greetingTempDir, mi := testutil.GreetingTestTorrent()
489 defer os.RemoveAll(greetingTempDir)
490 cfg := TestingConfig(t)
492 cfg.DataDir = greetingTempDir
493 seeder, err := NewClient(cfg)
494 require.NoError(t, err)
496 defer testutil.ExportStatusWriter(seeder, "s", t)()
497 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
498 seederTorrent.VerifyData()
499 leecherDataDir := t.TempDir()
500 fc, err := filecache.NewCache(leecherDataDir)
501 require.NoError(t, err)
502 if ps.SetLeecherStorageCapacity {
503 fc.SetCapacity(ps.LeecherStorageCapacity)
505 cfg.DefaultStorage = storage.NewResourcePieces(fc.AsResourceProvider())
506 cfg.DataDir = leecherDataDir
507 leecher, err := NewClient(cfg)
508 require.NoError(t, err)
509 defer leecher.Close()
510 defer testutil.ExportStatusWriter(leecher, "l", t)()
511 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
512 ret = TorrentSpecFromMetaInfo(mi)
516 require.NoError(t, err)
518 psc := leecherGreeting.SubscribePieceStateChanges()
521 leecherGreeting.cl.lock()
522 leecherGreeting.downloadPiecesLocked(0, leecherGreeting.numPieces())
524 leecherGreeting.cancelPiecesLocked(0, leecherGreeting.NumPieces(), "")
526 leecherGreeting.cl.unlock()
527 done := make(chan struct{})
529 go leecherGreeting.AddClientPeer(seeder)
530 completes := make(map[int]bool, 3)
531 expected := func() map[int]bool {
533 return map[int]bool{0: false, 1: false, 2: false}
535 return map[int]bool{0: true, 1: true, 2: true}
538 for !reflect.DeepEqual(completes, expected) {
540 completes[v.Index] = v.Complete
544 func TestTorrentDownloadAll(t *testing.T) {
545 testDownloadCancel(t, testDownloadCancelParams{})
548 func TestTorrentDownloadAllThenCancel(t *testing.T) {
549 testDownloadCancel(t, testDownloadCancelParams{
554 // Ensure that it's an error for a peer to send an invalid have message.
555 func TestPeerInvalidHave(t *testing.T) {
556 cfg := TestingConfig(t)
557 cfg.DropMutuallyCompletePeers = false
558 cl, err := NewClient(cfg)
559 require.NoError(t, err)
561 info := metainfo.Info{
563 Pieces: make([]byte, 20),
564 Files: []metainfo.FileInfo{{Length: 1}},
566 infoBytes, err := bencode.Marshal(info)
567 require.NoError(t, err)
568 tt, _new, err := cl.AddTorrentSpec(&TorrentSpec{
569 InfoBytes: infoBytes,
570 InfoHash: metainfo.HashBytes(infoBytes),
571 Storage: badStorage{},
573 require.NoError(t, err)
576 cn := &PeerConn{Peer: Peer{
578 callbacks: &cfg.Callbacks,
580 tt.conns[cn] = struct{}{}
584 assert.NoError(t, cn.peerSentHave(0))
585 assert.Error(t, cn.peerSentHave(1))
588 func TestPieceCompletedInStorageButNotClient(t *testing.T) {
589 greetingTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
590 defer os.RemoveAll(greetingTempDir)
591 cfg := TestingConfig(t)
592 cfg.DataDir = greetingTempDir
593 seeder, err := NewClient(TestingConfig(t))
594 require.NoError(t, err)
596 seeder.AddTorrentSpec(&TorrentSpec{
597 InfoBytes: greetingMetainfo.InfoBytes,
601 // Check that when the listen port is 0, all the protocols listened on have
602 // the same port, and it isn't zero.
603 func TestClientDynamicListenPortAllProtocols(t *testing.T) {
604 cl, err := NewClient(TestingConfig(t))
605 require.NoError(t, err)
607 port := cl.LocalPort()
608 assert.NotEqual(t, 0, port)
609 cl.eachListener(func(s Listener) bool {
610 assert.Equal(t, port, missinggo.AddrPort(s.Addr()))
615 func TestClientDynamicListenTCPOnly(t *testing.T) {
616 cfg := TestingConfig(t)
617 cfg.DisableUTP = true
618 cfg.DisableTCP = false
619 cl, err := NewClient(cfg)
620 require.NoError(t, err)
622 assert.NotEqual(t, 0, cl.LocalPort())
625 func TestClientDynamicListenUTPOnly(t *testing.T) {
626 cfg := TestingConfig(t)
627 cfg.DisableTCP = true
628 cfg.DisableUTP = false
629 cl, err := NewClient(cfg)
630 require.NoError(t, err)
632 assert.NotEqual(t, 0, cl.LocalPort())
635 func totalConns(tts []*Torrent) (ret int) {
636 for _, tt := range tts {
644 func TestSetMaxEstablishedConn(t *testing.T) {
646 ih := testutil.GreetingMetaInfo().HashInfoBytes()
647 cfg := TestingConfig(t)
648 cfg.DisableAcceptRateLimiting = true
649 cfg.DropDuplicatePeerIds = true
650 for i := 0; i < 3; i += 1 {
651 cl, err := NewClient(cfg)
652 require.NoError(t, err)
654 tt, _ := cl.AddTorrentInfoHash(ih)
655 tt.SetMaxEstablishedConns(2)
656 defer testutil.ExportStatusWriter(cl, fmt.Sprintf("%d", i), t)()
657 tts = append(tts, tt)
660 for _, tt := range tts {
661 for _, _tt := range tts {
663 tt.AddClientPeer(_tt.cl)
668 waitTotalConns := func(num int) {
669 for totalConns(tts) != num {
671 time.Sleep(time.Millisecond)
676 tts[0].SetMaxEstablishedConns(1)
678 tts[0].SetMaxEstablishedConns(0)
680 tts[0].SetMaxEstablishedConns(1)
683 tts[0].SetMaxEstablishedConns(2)
688 // Creates a file containing its own name as data. Make a metainfo from that, adds it to the given
689 // client, and returns a magnet link.
690 func makeMagnet(t *testing.T, cl *Client, dir, name string) string {
691 os.MkdirAll(dir, 0o770)
692 file, err := os.Create(filepath.Join(dir, name))
693 require.NoError(t, err)
694 file.Write([]byte(name))
696 mi := metainfo.MetaInfo{}
698 info := metainfo.Info{PieceLength: 256 * 1024}
699 err = info.BuildFromFilePath(filepath.Join(dir, name))
700 require.NoError(t, err)
701 mi.InfoBytes, err = bencode.Marshal(info)
702 require.NoError(t, err)
703 magnet := mi.Magnet(nil, &info).String()
704 tr, err := cl.AddTorrent(&mi)
705 require.NoError(t, err)
706 require.True(t, tr.Seeding())
711 // https://github.com/anacrolix/torrent/issues/114
712 func TestMultipleTorrentsWithEncryption(t *testing.T) {
713 testSeederLeecherPair(
715 func(cfg *ClientConfig) {
716 cfg.HeaderObfuscationPolicy.Preferred = true
717 cfg.HeaderObfuscationPolicy.RequirePreferred = true
719 func(cfg *ClientConfig) {
720 cfg.HeaderObfuscationPolicy.RequirePreferred = false
725 // Test that the leecher can download a torrent in its entirety from the seeder. Note that the
726 // seeder config is done first.
727 func testSeederLeecherPair(t *testing.T, seeder, leecher func(*ClientConfig)) {
728 cfg := TestingConfig(t)
730 cfg.DataDir = filepath.Join(cfg.DataDir, "server")
731 os.Mkdir(cfg.DataDir, 0o755)
733 server, err := NewClient(cfg)
734 require.NoError(t, err)
736 defer testutil.ExportStatusWriter(server, "s", t)()
737 magnet1 := makeMagnet(t, server, cfg.DataDir, "test1")
738 // Extra torrents are added to test the seeder having to match incoming obfuscated headers
739 // against more than one torrent. See issue #114
740 makeMagnet(t, server, cfg.DataDir, "test2")
741 for i := 0; i < 100; i++ {
742 makeMagnet(t, server, cfg.DataDir, fmt.Sprintf("test%d", i+2))
744 cfg = TestingConfig(t)
745 cfg.DataDir = filepath.Join(cfg.DataDir, "client")
747 client, err := NewClient(cfg)
748 require.NoError(t, err)
750 defer testutil.ExportStatusWriter(client, "c", t)()
751 tr, err := client.AddMagnet(magnet1)
752 require.NoError(t, err)
753 tr.AddClientPeer(server)
759 // This appears to be the situation with the S3 BitTorrent client.
760 func TestObfuscatedHeaderFallbackSeederDisallowsLeecherPrefers(t *testing.T) {
761 // Leecher prefers obfuscation, but the seeder does not allow it.
762 testSeederLeecherPair(
764 func(cfg *ClientConfig) {
765 cfg.HeaderObfuscationPolicy.Preferred = false
766 cfg.HeaderObfuscationPolicy.RequirePreferred = true
768 func(cfg *ClientConfig) {
769 cfg.HeaderObfuscationPolicy.Preferred = true
770 cfg.HeaderObfuscationPolicy.RequirePreferred = false
775 func TestObfuscatedHeaderFallbackSeederRequiresLeecherPrefersNot(t *testing.T) {
776 // Leecher prefers no obfuscation, but the seeder enforces it.
777 testSeederLeecherPair(
779 func(cfg *ClientConfig) {
780 cfg.HeaderObfuscationPolicy.Preferred = true
781 cfg.HeaderObfuscationPolicy.RequirePreferred = true
783 func(cfg *ClientConfig) {
784 cfg.HeaderObfuscationPolicy.Preferred = false
785 cfg.HeaderObfuscationPolicy.RequirePreferred = false
790 func TestClientAddressInUse(t *testing.T) {
791 s, _ := NewUtpSocket("udp", "localhost:50007", nil, log.Default)
795 cfg := TestingConfig(t).SetListenAddr("localhost:50007")
796 cfg.DisableUTP = false
797 cl, err := NewClient(cfg)
799 assert.Nil(t, cl.Close())
801 require.Error(t, err)
805 func TestClientHasDhtServersWhenUtpDisabled(t *testing.T) {
806 cc := TestingConfig(t)
809 cl, err := NewClient(cc)
810 require.NoError(t, err)
812 assert.NotEmpty(t, cl.DhtServers())
815 func TestClientDisabledImplicitNetworksButDhtEnabled(t *testing.T) {
816 cfg := TestingConfig(t)
817 cfg.DisableTCP = true
818 cfg.DisableUTP = true
820 cl, err := NewClient(cfg)
821 require.NoError(t, err)
823 assert.Empty(t, cl.listeners)
824 assert.NotEmpty(t, cl.DhtServers())
827 func TestBadPeerIpPort(t *testing.T) {
828 for _, tc := range []struct {
835 {"empty both", nil, 0, true, func(*Client) {}},
836 {"empty/nil ip", nil, 6666, true, func(*Client) {}},
839 net.ParseIP("127.0.0.1/32"),
844 "in doppleganger addresses",
845 net.ParseIP("127.0.0.1/32"),
849 cl.dopplegangerAddrs["10.0.0.1:2322"] = struct{}{}
854 net.ParseIP("10.0.0.1"),
858 cl.ipBlockList = iplist.New([]iplist.Range{
859 {First: net.ParseIP("10.0.0.1"), Last: net.ParseIP("10.0.0.255")},
865 net.ParseIP("10.0.0.1"),
869 ipAddr, ok := netip.AddrFromSlice(net.ParseIP("10.0.0.1"))
871 cl.badPeerIPs = map[netip.Addr]struct{}{}
872 cl.badPeerIPs[ipAddr] = struct{}{}
877 net.ParseIP("10.0.0.1"),
883 t.Run(tc.title, func(t *testing.T) {
884 cfg := TestingConfig(t)
885 cfg.DisableTCP = true
886 cfg.DisableUTP = true
888 cl, err := NewClient(cfg)
889 require.NoError(t, err)
893 require.Equal(t, tc.expectedOk, cl.badPeerIPPort(tc.ip, tc.port))
898 // https://github.com/anacrolix/torrent/issues/837
899 func TestClientConfigSetHandlerNotIgnored(t *testing.T) {
900 cfg := TestingConfig(t)
901 cfg.Logger.SetHandlers(log.DiscardHandler)
903 cl, err := NewClient(cfg)
904 c.Assert(err, qt.IsNil)
906 c.Assert(cl.logger.Handlers, qt.HasLen, 1)
907 h := cl.logger.Handlers[0].(log.StreamHandler)
908 c.Check(h.W, qt.Equals, io.Discard)