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)
595 seeder.AddTorrentSpec(&TorrentSpec{
596 InfoBytes: greetingMetainfo.InfoBytes,
600 // Check that when the listen port is 0, all the protocols listened on have
601 // the same port, and it isn't zero.
602 func TestClientDynamicListenPortAllProtocols(t *testing.T) {
603 cl, err := NewClient(TestingConfig(t))
604 require.NoError(t, err)
606 port := cl.LocalPort()
607 assert.NotEqual(t, 0, port)
608 cl.eachListener(func(s Listener) bool {
609 assert.Equal(t, port, missinggo.AddrPort(s.Addr()))
614 func TestClientDynamicListenTCPOnly(t *testing.T) {
615 cfg := TestingConfig(t)
616 cfg.DisableUTP = true
617 cfg.DisableTCP = false
618 cl, err := NewClient(cfg)
619 require.NoError(t, err)
621 assert.NotEqual(t, 0, cl.LocalPort())
624 func TestClientDynamicListenUTPOnly(t *testing.T) {
625 cfg := TestingConfig(t)
626 cfg.DisableTCP = true
627 cfg.DisableUTP = false
628 cl, err := NewClient(cfg)
629 require.NoError(t, err)
631 assert.NotEqual(t, 0, cl.LocalPort())
634 func totalConns(tts []*Torrent) (ret int) {
635 for _, tt := range tts {
643 func TestSetMaxEstablishedConn(t *testing.T) {
645 ih := testutil.GreetingMetaInfo().HashInfoBytes()
646 cfg := TestingConfig(t)
647 cfg.DisableAcceptRateLimiting = true
648 cfg.DropDuplicatePeerIds = true
649 for i := 0; i < 3; i += 1 {
650 cl, err := NewClient(cfg)
651 require.NoError(t, err)
653 tt, _ := cl.AddTorrentInfoHash(ih)
654 tt.SetMaxEstablishedConns(2)
655 defer testutil.ExportStatusWriter(cl, fmt.Sprintf("%d", i), t)()
656 tts = append(tts, tt)
659 for _, tt := range tts {
660 for _, _tt := range tts {
662 tt.AddClientPeer(_tt.cl)
667 waitTotalConns := func(num int) {
668 for totalConns(tts) != num {
670 time.Sleep(time.Millisecond)
675 tts[0].SetMaxEstablishedConns(1)
677 tts[0].SetMaxEstablishedConns(0)
679 tts[0].SetMaxEstablishedConns(1)
682 tts[0].SetMaxEstablishedConns(2)
687 // Creates a file containing its own name as data. Make a metainfo from that, adds it to the given
688 // client, and returns a magnet link.
689 func makeMagnet(t *testing.T, cl *Client, dir, name string) string {
690 os.MkdirAll(dir, 0o770)
691 file, err := os.Create(filepath.Join(dir, name))
692 require.NoError(t, err)
693 file.Write([]byte(name))
695 mi := metainfo.MetaInfo{}
697 info := metainfo.Info{PieceLength: 256 * 1024}
698 err = info.BuildFromFilePath(filepath.Join(dir, name))
699 require.NoError(t, err)
700 mi.InfoBytes, err = bencode.Marshal(info)
701 require.NoError(t, err)
702 magnet := mi.Magnet(nil, &info).String()
703 tr, err := cl.AddTorrent(&mi)
704 require.NoError(t, err)
705 require.True(t, tr.Seeding())
710 // https://github.com/anacrolix/torrent/issues/114
711 func TestMultipleTorrentsWithEncryption(t *testing.T) {
712 testSeederLeecherPair(
714 func(cfg *ClientConfig) {
715 cfg.HeaderObfuscationPolicy.Preferred = true
716 cfg.HeaderObfuscationPolicy.RequirePreferred = true
718 func(cfg *ClientConfig) {
719 cfg.HeaderObfuscationPolicy.RequirePreferred = false
724 // Test that the leecher can download a torrent in its entirety from the seeder. Note that the
725 // seeder config is done first.
726 func testSeederLeecherPair(t *testing.T, seeder, leecher func(*ClientConfig)) {
727 cfg := TestingConfig(t)
729 cfg.DataDir = filepath.Join(cfg.DataDir, "server")
730 os.Mkdir(cfg.DataDir, 0o755)
732 server, err := NewClient(cfg)
733 require.NoError(t, err)
735 defer testutil.ExportStatusWriter(server, "s", t)()
736 magnet1 := makeMagnet(t, server, cfg.DataDir, "test1")
737 // Extra torrents are added to test the seeder having to match incoming obfuscated headers
738 // against more than one torrent. See issue #114
739 makeMagnet(t, server, cfg.DataDir, "test2")
740 for i := 0; i < 100; i++ {
741 makeMagnet(t, server, cfg.DataDir, fmt.Sprintf("test%d", i+2))
743 cfg = TestingConfig(t)
744 cfg.DataDir = filepath.Join(cfg.DataDir, "client")
746 client, err := NewClient(cfg)
747 require.NoError(t, err)
749 defer testutil.ExportStatusWriter(client, "c", t)()
750 tr, err := client.AddMagnet(magnet1)
751 require.NoError(t, err)
752 tr.AddClientPeer(server)
758 // This appears to be the situation with the S3 BitTorrent client.
759 func TestObfuscatedHeaderFallbackSeederDisallowsLeecherPrefers(t *testing.T) {
760 // Leecher prefers obfuscation, but the seeder does not allow it.
761 testSeederLeecherPair(
763 func(cfg *ClientConfig) {
764 cfg.HeaderObfuscationPolicy.Preferred = false
765 cfg.HeaderObfuscationPolicy.RequirePreferred = true
767 func(cfg *ClientConfig) {
768 cfg.HeaderObfuscationPolicy.Preferred = true
769 cfg.HeaderObfuscationPolicy.RequirePreferred = false
774 func TestObfuscatedHeaderFallbackSeederRequiresLeecherPrefersNot(t *testing.T) {
775 // Leecher prefers no obfuscation, but the seeder enforces it.
776 testSeederLeecherPair(
778 func(cfg *ClientConfig) {
779 cfg.HeaderObfuscationPolicy.Preferred = true
780 cfg.HeaderObfuscationPolicy.RequirePreferred = true
782 func(cfg *ClientConfig) {
783 cfg.HeaderObfuscationPolicy.Preferred = false
784 cfg.HeaderObfuscationPolicy.RequirePreferred = false
789 func TestClientAddressInUse(t *testing.T) {
790 s, _ := NewUtpSocket("udp", ":50007", nil, log.Default)
794 cfg := TestingConfig(t).SetListenAddr(":50007")
795 cfg.DisableUTP = false
796 cl, err := NewClient(cfg)
797 require.Error(t, err)
801 func TestClientHasDhtServersWhenUtpDisabled(t *testing.T) {
802 cc := TestingConfig(t)
805 cl, err := NewClient(cc)
806 require.NoError(t, err)
808 assert.NotEmpty(t, cl.DhtServers())
811 func TestClientDisabledImplicitNetworksButDhtEnabled(t *testing.T) {
812 cfg := TestingConfig(t)
813 cfg.DisableTCP = true
814 cfg.DisableUTP = true
816 cl, err := NewClient(cfg)
817 require.NoError(t, err)
819 assert.Empty(t, cl.listeners)
820 assert.NotEmpty(t, cl.DhtServers())
823 func TestBadPeerIpPort(t *testing.T) {
824 for _, tc := range []struct {
831 {"empty both", nil, 0, true, func(*Client) {}},
832 {"empty/nil ip", nil, 6666, true, func(*Client) {}},
835 net.ParseIP("127.0.0.1/32"),
840 "in doppleganger addresses",
841 net.ParseIP("127.0.0.1/32"),
845 cl.dopplegangerAddrs["10.0.0.1:2322"] = struct{}{}
850 net.ParseIP("10.0.0.1"),
854 cl.ipBlockList = iplist.New([]iplist.Range{
855 {First: net.ParseIP("10.0.0.1"), Last: net.ParseIP("10.0.0.255")},
861 net.ParseIP("10.0.0.1"),
865 ipAddr, ok := netip.AddrFromSlice(net.ParseIP("10.0.0.1"))
867 cl.badPeerIPs = map[netip.Addr]struct{}{}
868 cl.badPeerIPs[ipAddr] = struct{}{}
873 net.ParseIP("10.0.0.1"),
879 t.Run(tc.title, func(t *testing.T) {
880 cfg := TestingConfig(t)
881 cfg.DisableTCP = true
882 cfg.DisableUTP = true
884 cl, err := NewClient(cfg)
885 require.NoError(t, err)
889 require.Equal(t, tc.expectedOk, cl.badPeerIPPort(tc.ip, tc.port))
894 // https://github.com/anacrolix/torrent/issues/837
895 func TestClientConfigSetHandlerNotIgnored(t *testing.T) {
896 cfg := NewDefaultClientConfig()
897 cfg.Logger.SetHandlers(log.DiscardHandler)
899 cl, err := NewClient(cfg)
900 c.Assert(err, qt.IsNil)
902 c.Assert(cl.logger.Handlers, qt.HasLen, 1)
903 h := cl.logger.Handlers[0].(log.StreamHandler)
904 c.Check(h.W, qt.Equals, io.Discard)