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) {
343 ipl := iplist.New(nil)
344 require.NotNil(t, ipl)
345 cfg := TestingConfig(t)
346 cfg.IPBlocklist = ipl
348 cl, err := NewClient(cfg)
349 require.NoError(t, err)
352 cl.eachDhtServer(func(s DhtServer) {
354 assert.Equal(t, ipl, s.(AnacrolixDhtServerWrapper).Server.IPBlocklist())
357 c.Assert(numServers, qt.Not(qt.Equals), 0)
360 // Check that stuff is merged in subsequent AddTorrentSpec for the same
362 func TestAddTorrentSpecMerging(t *testing.T) {
363 cl, err := NewClient(TestingConfig(t))
364 require.NoError(t, err)
366 dir, mi := testutil.GreetingTestTorrent()
367 defer os.RemoveAll(dir)
368 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
369 InfoHash: mi.HashInfoBytes(),
371 require.NoError(t, err)
373 require.Nil(t, tt.Info())
374 _, new, err = cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
375 require.NoError(t, err)
376 require.False(t, new)
377 require.NotNil(t, tt.Info())
380 func TestTorrentDroppedBeforeGotInfo(t *testing.T) {
381 dir, mi := testutil.GreetingTestTorrent()
383 cl, _ := NewClient(TestingConfig(t))
385 tt, _, _ := cl.AddTorrentSpec(&TorrentSpec{
386 InfoHash: mi.HashInfoBytes(),
389 assert.EqualValues(t, 0, len(cl.Torrents()))
397 func writeTorrentData(ts *storage.Torrent, info metainfo.Info, b []byte) {
398 for i := 0; i < info.NumPieces(); i += 1 {
400 ts.Piece(p).WriteAt(b[p.Offset():p.Offset()+p.Length()], 0)
404 func testAddTorrentPriorPieceCompletion(t *testing.T, alreadyCompleted bool, csf func(*filecache.Cache) storage.ClientImpl) {
405 fileCacheDir := t.TempDir()
406 fileCache, err := filecache.NewCache(fileCacheDir)
407 require.NoError(t, err)
408 greetingDataTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
409 defer os.RemoveAll(greetingDataTempDir)
410 filePieceStore := csf(fileCache)
411 info, err := greetingMetainfo.UnmarshalInfo()
412 require.NoError(t, err)
413 ih := greetingMetainfo.HashInfoBytes()
414 greetingData, err := storage.NewClient(filePieceStore).OpenTorrent(&info, ih)
415 require.NoError(t, err)
416 writeTorrentData(greetingData, info, []byte(testutil.GreetingFileContents))
417 // require.Equal(t, len(testutil.GreetingFileContents), written)
418 // require.NoError(t, err)
419 for i := 0; i < info.NumPieces(); i++ {
421 if alreadyCompleted {
422 require.NoError(t, greetingData.Piece(p).MarkComplete())
425 cfg := TestingConfig(t)
426 // TODO: Disable network option?
427 cfg.DisableTCP = true
428 cfg.DisableUTP = true
429 cfg.DefaultStorage = filePieceStore
430 cl, err := NewClient(cfg)
431 require.NoError(t, err)
433 tt, err := cl.AddTorrent(greetingMetainfo)
434 require.NoError(t, err)
435 psrs := tt.PieceStateRuns()
436 assert.Len(t, psrs, 1)
437 assert.EqualValues(t, 3, psrs[0].Length)
438 assert.Equal(t, alreadyCompleted, psrs[0].Complete)
439 if alreadyCompleted {
441 quicktest.Check(t, iotest.TestReader(r, []byte(testutil.GreetingFileContents)), quicktest.IsNil)
445 func TestAddTorrentPiecesAlreadyCompleted(t *testing.T) {
446 testAddTorrentPriorPieceCompletion(t, true, fileCachePieceResourceStorage)
449 func TestAddTorrentPiecesNotAlreadyCompleted(t *testing.T) {
450 testAddTorrentPriorPieceCompletion(t, false, fileCachePieceResourceStorage)
453 func TestAddMetainfoWithNodes(t *testing.T) {
454 cfg := TestingConfig(t)
455 cfg.ListenHost = func(string) string { return "" }
457 cfg.DhtStartingNodes = func(string) dht.StartingNodesGetter { return func() ([]dht.Addr, error) { return nil, nil } }
458 // For now, we want to just jam the nodes into the table, without verifying them first. Also the
459 // DHT code doesn't support mixing secure and insecure nodes if security is enabled (yet).
460 // cfg.DHTConfig.NoSecurity = true
461 cl, err := NewClient(cfg)
462 require.NoError(t, err)
464 sum := func() (ret int64) {
465 cl.eachDhtServer(func(s DhtServer) {
466 ret += s.Stats().(dht.ServerStats).OutboundQueriesAttempted
470 assert.EqualValues(t, 0, sum())
471 tt, err := cl.AddTorrentFromFile("metainfo/testdata/issue_65a.torrent")
472 require.NoError(t, err)
473 // Nodes are not added or exposed in Torrent's metainfo. We just randomly
474 // check if the announce-list is here instead. TODO: Add nodes.
475 assert.Len(t, tt.metainfo.AnnounceList, 5)
476 // There are 6 nodes in the torrent file.
477 for sum() != int64(6*len(cl.dhtServers)) {
478 time.Sleep(time.Millisecond)
482 type testDownloadCancelParams struct {
483 SetLeecherStorageCapacity bool
484 LeecherStorageCapacity int64
488 func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
489 greetingTempDir, mi := testutil.GreetingTestTorrent()
490 defer os.RemoveAll(greetingTempDir)
491 cfg := TestingConfig(t)
493 cfg.DataDir = greetingTempDir
494 seeder, err := NewClient(cfg)
495 require.NoError(t, err)
497 defer testutil.ExportStatusWriter(seeder, "s", t)()
498 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
499 seederTorrent.VerifyData()
500 leecherDataDir := t.TempDir()
501 fc, err := filecache.NewCache(leecherDataDir)
502 require.NoError(t, err)
503 if ps.SetLeecherStorageCapacity {
504 fc.SetCapacity(ps.LeecherStorageCapacity)
506 cfg.DefaultStorage = storage.NewResourcePieces(fc.AsResourceProvider())
507 cfg.DataDir = leecherDataDir
508 leecher, err := NewClient(cfg)
509 require.NoError(t, err)
510 defer leecher.Close()
511 defer testutil.ExportStatusWriter(leecher, "l", t)()
512 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
513 ret = TorrentSpecFromMetaInfo(mi)
517 require.NoError(t, err)
519 psc := leecherGreeting.SubscribePieceStateChanges()
522 leecherGreeting.cl.lock()
523 leecherGreeting.downloadPiecesLocked(0, leecherGreeting.numPieces())
525 leecherGreeting.cancelPiecesLocked(0, leecherGreeting.NumPieces(), "")
527 leecherGreeting.cl.unlock()
528 done := make(chan struct{})
530 go leecherGreeting.AddClientPeer(seeder)
531 completes := make(map[int]bool, 3)
532 expected := func() map[int]bool {
534 return map[int]bool{0: false, 1: false, 2: false}
536 return map[int]bool{0: true, 1: true, 2: true}
539 for !reflect.DeepEqual(completes, expected) {
541 completes[v.Index] = v.Complete
545 func TestTorrentDownloadAll(t *testing.T) {
546 testDownloadCancel(t, testDownloadCancelParams{})
549 func TestTorrentDownloadAllThenCancel(t *testing.T) {
550 testDownloadCancel(t, testDownloadCancelParams{
555 // Ensure that it's an error for a peer to send an invalid have message.
556 func TestPeerInvalidHave(t *testing.T) {
557 cfg := TestingConfig(t)
558 cfg.DropMutuallyCompletePeers = false
559 cl, err := NewClient(cfg)
560 require.NoError(t, err)
562 info := metainfo.Info{
564 Pieces: make([]byte, 20),
565 Files: []metainfo.FileInfo{{Length: 1}},
567 infoBytes, err := bencode.Marshal(info)
568 require.NoError(t, err)
569 tt, _new, err := cl.AddTorrentSpec(&TorrentSpec{
570 InfoBytes: infoBytes,
571 InfoHash: metainfo.HashBytes(infoBytes),
572 Storage: badStorage{},
574 require.NoError(t, err)
577 cn := &PeerConn{Peer: Peer{
579 callbacks: &cfg.Callbacks,
581 tt.conns[cn] = struct{}{}
585 assert.NoError(t, cn.peerSentHave(0))
586 assert.Error(t, cn.peerSentHave(1))
589 func TestPieceCompletedInStorageButNotClient(t *testing.T) {
590 greetingTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
591 defer os.RemoveAll(greetingTempDir)
592 cfg := TestingConfig(t)
593 cfg.DataDir = greetingTempDir
594 seeder, err := NewClient(TestingConfig(t))
595 require.NoError(t, err)
597 seeder.AddTorrentSpec(&TorrentSpec{
598 InfoBytes: greetingMetainfo.InfoBytes,
602 // Check that when the listen port is 0, all the protocols listened on have
603 // the same port, and it isn't zero.
604 func TestClientDynamicListenPortAllProtocols(t *testing.T) {
605 cl, err := NewClient(TestingConfig(t))
606 require.NoError(t, err)
608 port := cl.LocalPort()
609 assert.NotEqual(t, 0, port)
610 cl.eachListener(func(s Listener) bool {
611 assert.Equal(t, port, missinggo.AddrPort(s.Addr()))
616 func TestClientDynamicListenTCPOnly(t *testing.T) {
617 cfg := TestingConfig(t)
618 cfg.DisableUTP = true
619 cfg.DisableTCP = false
620 cl, err := NewClient(cfg)
621 require.NoError(t, err)
623 assert.NotEqual(t, 0, cl.LocalPort())
626 func TestClientDynamicListenUTPOnly(t *testing.T) {
627 cfg := TestingConfig(t)
628 cfg.DisableTCP = true
629 cfg.DisableUTP = false
630 cl, err := NewClient(cfg)
631 require.NoError(t, err)
633 assert.NotEqual(t, 0, cl.LocalPort())
636 func totalConns(tts []*Torrent) (ret int) {
637 for _, tt := range tts {
645 func TestSetMaxEstablishedConn(t *testing.T) {
647 ih := testutil.GreetingMetaInfo().HashInfoBytes()
648 cfg := TestingConfig(t)
649 cfg.DisableAcceptRateLimiting = true
650 cfg.DropDuplicatePeerIds = true
651 for i := 0; i < 3; i += 1 {
652 cl, err := NewClient(cfg)
653 require.NoError(t, err)
655 tt, _ := cl.AddTorrentInfoHash(ih)
656 tt.SetMaxEstablishedConns(2)
657 defer testutil.ExportStatusWriter(cl, fmt.Sprintf("%d", i), t)()
658 tts = append(tts, tt)
661 for _, tt := range tts {
662 for _, _tt := range tts {
664 tt.AddClientPeer(_tt.cl)
669 waitTotalConns := func(num int) {
670 for totalConns(tts) != num {
672 time.Sleep(time.Millisecond)
677 tts[0].SetMaxEstablishedConns(1)
679 tts[0].SetMaxEstablishedConns(0)
681 tts[0].SetMaxEstablishedConns(1)
684 tts[0].SetMaxEstablishedConns(2)
689 // Creates a file containing its own name as data. Make a metainfo from that, adds it to the given
690 // client, and returns a magnet link.
691 func makeMagnet(t *testing.T, cl *Client, dir, name string) string {
692 os.MkdirAll(dir, 0o770)
693 file, err := os.Create(filepath.Join(dir, name))
694 require.NoError(t, err)
695 file.Write([]byte(name))
697 mi := metainfo.MetaInfo{}
699 info := metainfo.Info{PieceLength: 256 * 1024}
700 err = info.BuildFromFilePath(filepath.Join(dir, name))
701 require.NoError(t, err)
702 mi.InfoBytes, err = bencode.Marshal(info)
703 require.NoError(t, err)
704 magnet := mi.Magnet(nil, &info).String()
705 tr, err := cl.AddTorrent(&mi)
706 require.NoError(t, err)
707 require.True(t, tr.Seeding())
712 // https://github.com/anacrolix/torrent/issues/114
713 func TestMultipleTorrentsWithEncryption(t *testing.T) {
714 testSeederLeecherPair(
716 func(cfg *ClientConfig) {
717 cfg.HeaderObfuscationPolicy.Preferred = true
718 cfg.HeaderObfuscationPolicy.RequirePreferred = true
720 func(cfg *ClientConfig) {
721 cfg.HeaderObfuscationPolicy.RequirePreferred = false
726 // Test that the leecher can download a torrent in its entirety from the seeder. Note that the
727 // seeder config is done first.
728 func testSeederLeecherPair(t *testing.T, seeder, leecher func(*ClientConfig)) {
729 cfg := TestingConfig(t)
731 cfg.DataDir = filepath.Join(cfg.DataDir, "server")
732 os.Mkdir(cfg.DataDir, 0o755)
734 server, err := NewClient(cfg)
735 require.NoError(t, err)
737 defer testutil.ExportStatusWriter(server, "s", t)()
738 magnet1 := makeMagnet(t, server, cfg.DataDir, "test1")
739 // Extra torrents are added to test the seeder having to match incoming obfuscated headers
740 // against more than one torrent. See issue #114
741 makeMagnet(t, server, cfg.DataDir, "test2")
742 for i := 0; i < 100; i++ {
743 makeMagnet(t, server, cfg.DataDir, fmt.Sprintf("test%d", i+2))
745 cfg = TestingConfig(t)
746 cfg.DataDir = filepath.Join(cfg.DataDir, "client")
748 client, err := NewClient(cfg)
749 require.NoError(t, err)
751 defer testutil.ExportStatusWriter(client, "c", t)()
752 tr, err := client.AddMagnet(magnet1)
753 require.NoError(t, err)
754 tr.AddClientPeer(server)
760 // This appears to be the situation with the S3 BitTorrent client.
761 func TestObfuscatedHeaderFallbackSeederDisallowsLeecherPrefers(t *testing.T) {
762 // Leecher prefers obfuscation, but the seeder does not allow it.
763 testSeederLeecherPair(
765 func(cfg *ClientConfig) {
766 cfg.HeaderObfuscationPolicy.Preferred = false
767 cfg.HeaderObfuscationPolicy.RequirePreferred = true
769 func(cfg *ClientConfig) {
770 cfg.HeaderObfuscationPolicy.Preferred = true
771 cfg.HeaderObfuscationPolicy.RequirePreferred = false
776 func TestObfuscatedHeaderFallbackSeederRequiresLeecherPrefersNot(t *testing.T) {
777 // Leecher prefers no obfuscation, but the seeder enforces it.
778 testSeederLeecherPair(
780 func(cfg *ClientConfig) {
781 cfg.HeaderObfuscationPolicy.Preferred = true
782 cfg.HeaderObfuscationPolicy.RequirePreferred = true
784 func(cfg *ClientConfig) {
785 cfg.HeaderObfuscationPolicy.Preferred = false
786 cfg.HeaderObfuscationPolicy.RequirePreferred = false
791 func TestClientAddressInUse(t *testing.T) {
792 s, _ := NewUtpSocket("udp", "localhost:50007", nil, log.Default)
796 cfg := TestingConfig(t).SetListenAddr("localhost:50007")
797 cfg.DisableUTP = false
798 cl, err := NewClient(cfg)
800 assert.Nil(t, cl.Close())
802 require.Error(t, err)
806 func TestClientHasDhtServersWhenUtpDisabled(t *testing.T) {
807 cc := TestingConfig(t)
810 cl, err := NewClient(cc)
811 require.NoError(t, err)
813 assert.NotEmpty(t, cl.DhtServers())
816 func TestClientDisabledImplicitNetworksButDhtEnabled(t *testing.T) {
817 cfg := TestingConfig(t)
818 cfg.DisableTCP = true
819 cfg.DisableUTP = true
821 cl, err := NewClient(cfg)
822 require.NoError(t, err)
824 assert.Empty(t, cl.listeners)
825 assert.NotEmpty(t, cl.DhtServers())
828 func TestBadPeerIpPort(t *testing.T) {
829 for _, tc := range []struct {
836 {"empty both", nil, 0, true, func(*Client) {}},
837 {"empty/nil ip", nil, 6666, true, func(*Client) {}},
840 net.ParseIP("127.0.0.1/32"),
845 "in doppleganger addresses",
846 net.ParseIP("127.0.0.1/32"),
850 cl.dopplegangerAddrs["10.0.0.1:2322"] = struct{}{}
855 net.ParseIP("10.0.0.1"),
859 cl.ipBlockList = iplist.New([]iplist.Range{
860 {First: net.ParseIP("10.0.0.1"), Last: net.ParseIP("10.0.0.255")},
866 net.ParseIP("10.0.0.1"),
870 ipAddr, ok := netip.AddrFromSlice(net.ParseIP("10.0.0.1"))
872 cl.badPeerIPs = map[netip.Addr]struct{}{}
873 cl.badPeerIPs[ipAddr] = struct{}{}
878 net.ParseIP("10.0.0.1"),
884 t.Run(tc.title, func(t *testing.T) {
885 cfg := TestingConfig(t)
886 cfg.DisableTCP = true
887 cfg.DisableUTP = true
889 cl, err := NewClient(cfg)
890 require.NoError(t, err)
894 require.Equal(t, tc.expectedOk, cl.badPeerIPPort(tc.ip, tc.port))
899 // https://github.com/anacrolix/torrent/issues/837
900 func TestClientConfigSetHandlerNotIgnored(t *testing.T) {
901 cfg := TestingConfig(t)
902 cfg.Logger.SetHandlers(log.DiscardHandler)
904 cl, err := NewClient(cfg)
905 c.Assert(err, qt.IsNil)
907 c.Assert(cl.logger.Handlers, qt.HasLen, 1)
908 h := cl.logger.Handlers[0].(log.StreamHandler)
909 c.Check(h.W, qt.Equals, io.Discard)