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 "github.com/stretchr/testify/assert"
22 "github.com/stretchr/testify/require"
24 "github.com/anacrolix/torrent/bencode"
25 "github.com/anacrolix/torrent/internal/testutil"
26 "github.com/anacrolix/torrent/iplist"
27 "github.com/anacrolix/torrent/metainfo"
28 "github.com/anacrolix/torrent/storage"
31 func TestClientDefault(t *testing.T) {
32 cl, err := NewClient(TestingConfig(t))
33 require.NoError(t, err)
34 require.Empty(t, cl.Close())
37 func TestClientNilConfig(t *testing.T) {
38 // The default config will put crap in the working directory.
39 origDir, _ := os.Getwd()
40 defer os.Chdir(origDir)
42 cl, err := NewClient(nil)
43 require.NoError(t, err)
44 require.Empty(t, cl.Close())
47 func TestAddDropTorrent(t *testing.T) {
48 cl, err := NewClient(TestingConfig(t))
49 require.NoError(t, err)
51 dir, mi := testutil.GreetingTestTorrent()
52 defer os.RemoveAll(dir)
53 tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
54 require.NoError(t, err)
56 tt.SetMaxEstablishedConns(0)
57 tt.SetMaxEstablishedConns(1)
61 func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
66 func TestAddTorrentNoUsableURLs(t *testing.T) {
71 func TestAddPeersToUnknownTorrent(t *testing.T) {
76 func TestPieceHashSize(t *testing.T) {
77 assert.Equal(t, 20, pieceHash.Size())
80 func TestTorrentInitialState(t *testing.T) {
81 dir, mi := testutil.GreetingTestTorrent()
82 defer os.RemoveAll(dir)
84 cl.init(TestingConfig(t))
88 storage.NewFileWithCompletion(t.TempDir(), storage.NewMapPieceCompletion()),
92 err := tor.setInfoBytesLocked(mi.InfoBytes)
94 require.NoError(t, err)
95 require.Len(t, tor.pieces, 3)
96 tor.pendAllChunkSpecs(0)
98 assert.EqualValues(t, 3, tor.pieceNumPendingChunks(0))
100 assert.EqualValues(t, ChunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
103 func TestReducedDialTimeout(t *testing.T) {
104 cfg := NewDefaultClientConfig()
105 for _, _case := range []struct {
109 ExpectedReduced time.Duration
111 {cfg.NominalDialTimeout, 40, 0, cfg.NominalDialTimeout},
112 {cfg.NominalDialTimeout, 40, 1, cfg.NominalDialTimeout},
113 {cfg.NominalDialTimeout, 40, 39, cfg.NominalDialTimeout},
114 {cfg.NominalDialTimeout, 40, 40, cfg.NominalDialTimeout / 2},
115 {cfg.NominalDialTimeout, 40, 80, cfg.NominalDialTimeout / 3},
116 {cfg.NominalDialTimeout, 40, 4000, cfg.NominalDialTimeout / 101},
118 reduced := reducedDialTimeout(cfg.MinDialTimeout, _case.Max, _case.HalfOpenLimit, _case.PendingPeers)
119 expected := _case.ExpectedReduced
120 if expected < cfg.MinDialTimeout {
121 expected = cfg.MinDialTimeout
123 if reduced != expected {
124 t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
129 func TestAddDropManyTorrents(t *testing.T) {
130 cl, err := NewClient(TestingConfig(t))
131 require.NoError(t, err)
133 for i := 0; i < 1000; i += 1 {
135 binary.PutVarint(spec.InfoHash[:], int64(i))
136 tt, new, err := cl.AddTorrentSpec(&spec)
137 assert.NoError(t, err)
143 func fileCachePieceResourceStorage(fc *filecache.Cache) storage.ClientImpl {
144 return storage.NewResourcePiecesOpts(
145 fc.AsResourceProvider(),
146 storage.ResourcePiecesOpts{
147 LeaveIncompleteChunks: true,
152 func TestMergingTrackersByAddingSpecs(t *testing.T) {
153 cl, err := NewClient(TestingConfig(t))
154 require.NoError(t, err)
156 spec := TorrentSpec{}
157 T, new, _ := cl.AddTorrentSpec(&spec)
161 spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
162 _, new, _ = cl.AddTorrentSpec(&spec)
164 assert.EqualValues(t, [][]string{{"http://a"}, {"udp://b"}}, T.metainfo.AnnounceList)
165 // Because trackers are disabled in TestingConfig.
166 assert.EqualValues(t, 0, len(T.trackerAnnouncers))
169 // We read from a piece which is marked completed, but is missing data.
170 func TestCompletedPieceWrongSize(t *testing.T) {
171 cfg := TestingConfig(t)
172 cfg.DefaultStorage = badStorage{}
173 cl, err := NewClient(cfg)
174 require.NoError(t, err)
176 info := metainfo.Info{
178 Pieces: make([]byte, 20),
179 Files: []metainfo.FileInfo{
180 {Path: []string{"greeting"}, Length: 13},
183 b, err := bencode.Marshal(info)
184 require.NoError(t, err)
185 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
187 InfoHash: metainfo.HashBytes(b),
189 require.NoError(t, err)
194 quicktest.Check(t, iotest.TestReader(r, []byte(testutil.GreetingFileContents)), quicktest.IsNil)
197 func BenchmarkAddLargeTorrent(b *testing.B) {
198 cfg := TestingConfig(b)
199 cfg.DisableTCP = true
200 cfg.DisableUTP = true
201 cl, err := NewClient(cfg)
202 require.NoError(b, err)
205 for i := 0; i < b.N; i += 1 {
206 t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
214 func TestResponsive(t *testing.T) {
215 seederDataDir, mi := testutil.GreetingTestTorrent()
216 defer os.RemoveAll(seederDataDir)
217 cfg := TestingConfig(t)
219 cfg.DataDir = seederDataDir
220 seeder, err := NewClient(cfg)
223 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
224 seederTorrent.VerifyData()
225 leecherDataDir := t.TempDir()
226 cfg = TestingConfig(t)
227 cfg.DataDir = leecherDataDir
228 leecher, err := NewClient(cfg)
230 defer leecher.Close()
231 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
232 ret = TorrentSpecFromMetaInfo(mi)
236 leecherTorrent.AddClientPeer(seeder)
237 reader := leecherTorrent.NewReader()
239 reader.SetReadahead(0)
240 reader.SetResponsive()
242 _, err = reader.Seek(3, io.SeekStart)
243 require.NoError(t, err)
244 _, err = io.ReadFull(reader, b)
246 assert.EqualValues(t, "lo", string(b))
247 _, err = reader.Seek(11, io.SeekStart)
248 require.NoError(t, err)
249 n, err := io.ReadFull(reader, b)
251 assert.EqualValues(t, 2, n)
252 assert.EqualValues(t, "d\n", string(b))
255 // TestResponsive was the first test to fail if uTP is disabled and TCP sockets dial from the
257 func TestResponsiveTcpOnly(t *testing.T) {
258 seederDataDir, mi := testutil.GreetingTestTorrent()
259 defer os.RemoveAll(seederDataDir)
260 cfg := TestingConfig(t)
261 cfg.DisableUTP = true
263 cfg.DataDir = seederDataDir
264 seeder, err := NewClient(cfg)
267 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
268 seederTorrent.VerifyData()
269 leecherDataDir := t.TempDir()
270 cfg = TestingConfig(t)
271 cfg.DataDir = leecherDataDir
272 leecher, err := NewClient(cfg)
274 defer leecher.Close()
275 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
276 ret = TorrentSpecFromMetaInfo(mi)
280 leecherTorrent.AddClientPeer(seeder)
281 reader := leecherTorrent.NewReader()
283 reader.SetReadahead(0)
284 reader.SetResponsive()
286 _, err = reader.Seek(3, io.SeekStart)
287 require.NoError(t, err)
288 _, err = io.ReadFull(reader, b)
290 assert.EqualValues(t, "lo", string(b))
291 _, err = reader.Seek(11, io.SeekStart)
292 require.NoError(t, err)
293 n, err := io.ReadFull(reader, b)
295 assert.EqualValues(t, 2, n)
296 assert.EqualValues(t, "d\n", string(b))
299 func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
300 seederDataDir, mi := testutil.GreetingTestTorrent()
301 defer os.RemoveAll(seederDataDir)
302 cfg := TestingConfig(t)
304 cfg.DataDir = seederDataDir
305 seeder, err := NewClient(cfg)
308 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
309 seederTorrent.VerifyData()
310 leecherDataDir := t.TempDir()
311 cfg = TestingConfig(t)
312 cfg.DataDir = leecherDataDir
313 leecher, err := NewClient(cfg)
315 defer leecher.Close()
316 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
317 ret = TorrentSpecFromMetaInfo(mi)
321 leecherTorrent.AddClientPeer(seeder)
322 reader := leecherTorrent.NewReader()
324 reader.SetReadahead(0)
325 reader.SetResponsive()
327 _, err = reader.Seek(3, io.SeekStart)
328 require.NoError(t, err)
329 _, err = io.ReadFull(reader, b)
331 assert.EqualValues(t, "lo", string(b))
332 _, err = reader.Seek(11, io.SeekStart)
333 require.NoError(t, err)
334 leecherTorrent.Drop()
335 n, err := reader.Read(b)
336 assert.EqualError(t, err, "torrent closed")
337 assert.EqualValues(t, 0, n)
340 func TestDhtInheritBlocklist(t *testing.T) {
341 ipl := iplist.New(nil)
342 require.NotNil(t, ipl)
343 cfg := TestingConfig(t)
344 cfg.IPBlocklist = ipl
346 cl, err := NewClient(cfg)
347 require.NoError(t, err)
350 cl.eachDhtServer(func(s DhtServer) {
352 assert.Equal(t, ipl, s.(AnacrolixDhtServerWrapper).Server.IPBlocklist())
355 assert.EqualValues(t, 2, numServers)
358 // Check that stuff is merged in subsequent AddTorrentSpec for the same
360 func TestAddTorrentSpecMerging(t *testing.T) {
361 cl, err := NewClient(TestingConfig(t))
362 require.NoError(t, err)
364 dir, mi := testutil.GreetingTestTorrent()
365 defer os.RemoveAll(dir)
366 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
367 InfoHash: mi.HashInfoBytes(),
369 require.NoError(t, err)
371 require.Nil(t, tt.Info())
372 _, new, err = cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
373 require.NoError(t, err)
374 require.False(t, new)
375 require.NotNil(t, tt.Info())
378 func TestTorrentDroppedBeforeGotInfo(t *testing.T) {
379 dir, mi := testutil.GreetingTestTorrent()
381 cl, _ := NewClient(TestingConfig(t))
383 tt, _, _ := cl.AddTorrentSpec(&TorrentSpec{
384 InfoHash: mi.HashInfoBytes(),
387 assert.EqualValues(t, 0, len(cl.Torrents()))
395 func writeTorrentData(ts *storage.Torrent, info metainfo.Info, b []byte) {
396 for i := 0; i < info.NumPieces(); i += 1 {
398 ts.Piece(p).WriteAt(b[p.Offset():p.Offset()+p.Length()], 0)
402 func testAddTorrentPriorPieceCompletion(t *testing.T, alreadyCompleted bool, csf func(*filecache.Cache) storage.ClientImpl) {
403 fileCacheDir := t.TempDir()
404 fileCache, err := filecache.NewCache(fileCacheDir)
405 require.NoError(t, err)
406 greetingDataTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
407 defer os.RemoveAll(greetingDataTempDir)
408 filePieceStore := csf(fileCache)
409 info, err := greetingMetainfo.UnmarshalInfo()
410 require.NoError(t, err)
411 ih := greetingMetainfo.HashInfoBytes()
412 greetingData, err := storage.NewClient(filePieceStore).OpenTorrent(&info, ih)
413 require.NoError(t, err)
414 writeTorrentData(greetingData, info, []byte(testutil.GreetingFileContents))
415 // require.Equal(t, len(testutil.GreetingFileContents), written)
416 // require.NoError(t, err)
417 for i := 0; i < info.NumPieces(); i++ {
419 if alreadyCompleted {
420 require.NoError(t, greetingData.Piece(p).MarkComplete())
423 cfg := TestingConfig(t)
424 // TODO: Disable network option?
425 cfg.DisableTCP = true
426 cfg.DisableUTP = true
427 cfg.DefaultStorage = filePieceStore
428 cl, err := NewClient(cfg)
429 require.NoError(t, err)
431 tt, err := cl.AddTorrent(greetingMetainfo)
432 require.NoError(t, err)
433 psrs := tt.PieceStateRuns()
434 assert.Len(t, psrs, 1)
435 assert.EqualValues(t, 3, psrs[0].Length)
436 assert.Equal(t, alreadyCompleted, psrs[0].Complete)
437 if alreadyCompleted {
439 quicktest.Check(t, iotest.TestReader(r, []byte(testutil.GreetingFileContents)), quicktest.IsNil)
443 func TestAddTorrentPiecesAlreadyCompleted(t *testing.T) {
444 testAddTorrentPriorPieceCompletion(t, true, fileCachePieceResourceStorage)
447 func TestAddTorrentPiecesNotAlreadyCompleted(t *testing.T) {
448 testAddTorrentPriorPieceCompletion(t, false, fileCachePieceResourceStorage)
451 func TestAddMetainfoWithNodes(t *testing.T) {
452 cfg := TestingConfig(t)
453 cfg.ListenHost = func(string) string { return "" }
455 cfg.DhtStartingNodes = func(string) dht.StartingNodesGetter { return func() ([]dht.Addr, error) { return nil, nil } }
456 // For now, we want to just jam the nodes into the table, without verifying them first. Also the
457 // DHT code doesn't support mixing secure and insecure nodes if security is enabled (yet).
458 // cfg.DHTConfig.NoSecurity = true
459 cl, err := NewClient(cfg)
460 require.NoError(t, err)
462 sum := func() (ret int64) {
463 cl.eachDhtServer(func(s DhtServer) {
464 ret += s.Stats().(dht.ServerStats).OutboundQueriesAttempted
468 assert.EqualValues(t, 0, sum())
469 tt, err := cl.AddTorrentFromFile("metainfo/testdata/issue_65a.torrent")
470 require.NoError(t, err)
471 // Nodes are not added or exposed in Torrent's metainfo. We just randomly
472 // check if the announce-list is here instead. TODO: Add nodes.
473 assert.Len(t, tt.metainfo.AnnounceList, 5)
474 // There are 6 nodes in the torrent file.
475 for sum() != int64(6*len(cl.dhtServers)) {
476 time.Sleep(time.Millisecond)
480 type testDownloadCancelParams struct {
481 SetLeecherStorageCapacity bool
482 LeecherStorageCapacity int64
486 func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
487 greetingTempDir, mi := testutil.GreetingTestTorrent()
488 defer os.RemoveAll(greetingTempDir)
489 cfg := TestingConfig(t)
491 cfg.DataDir = greetingTempDir
492 seeder, err := NewClient(cfg)
493 require.NoError(t, err)
495 defer testutil.ExportStatusWriter(seeder, "s", t)()
496 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
497 seederTorrent.VerifyData()
498 leecherDataDir := t.TempDir()
499 fc, err := filecache.NewCache(leecherDataDir)
500 require.NoError(t, err)
501 if ps.SetLeecherStorageCapacity {
502 fc.SetCapacity(ps.LeecherStorageCapacity)
504 cfg.DefaultStorage = storage.NewResourcePieces(fc.AsResourceProvider())
505 cfg.DataDir = leecherDataDir
506 leecher, err := NewClient(cfg)
507 require.NoError(t, err)
508 defer leecher.Close()
509 defer testutil.ExportStatusWriter(leecher, "l", t)()
510 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
511 ret = TorrentSpecFromMetaInfo(mi)
515 require.NoError(t, err)
517 psc := leecherGreeting.SubscribePieceStateChanges()
520 leecherGreeting.cl.lock()
521 leecherGreeting.downloadPiecesLocked(0, leecherGreeting.numPieces())
523 leecherGreeting.cancelPiecesLocked(0, leecherGreeting.NumPieces(), "")
525 leecherGreeting.cl.unlock()
526 done := make(chan struct{})
528 go leecherGreeting.AddClientPeer(seeder)
529 completes := make(map[int]bool, 3)
530 expected := func() map[int]bool {
532 return map[int]bool{0: false, 1: false, 2: false}
534 return map[int]bool{0: true, 1: true, 2: true}
537 for !reflect.DeepEqual(completes, expected) {
539 completes[v.Index] = v.Complete
543 func TestTorrentDownloadAll(t *testing.T) {
544 testDownloadCancel(t, testDownloadCancelParams{})
547 func TestTorrentDownloadAllThenCancel(t *testing.T) {
548 testDownloadCancel(t, testDownloadCancelParams{
553 // Ensure that it's an error for a peer to send an invalid have message.
554 func TestPeerInvalidHave(t *testing.T) {
555 cfg := TestingConfig(t)
556 cfg.DropMutuallyCompletePeers = false
557 cl, err := NewClient(cfg)
558 require.NoError(t, err)
560 info := metainfo.Info{
562 Pieces: make([]byte, 20),
563 Files: []metainfo.FileInfo{{Length: 1}},
565 infoBytes, err := bencode.Marshal(info)
566 require.NoError(t, err)
567 tt, _new, err := cl.AddTorrentSpec(&TorrentSpec{
568 InfoBytes: infoBytes,
569 InfoHash: metainfo.HashBytes(infoBytes),
570 Storage: badStorage{},
572 require.NoError(t, err)
575 cn := &PeerConn{Peer: Peer{
577 callbacks: &cfg.Callbacks,
579 tt.conns[cn] = struct{}{}
583 assert.NoError(t, cn.peerSentHave(0))
584 assert.Error(t, cn.peerSentHave(1))
587 func TestPieceCompletedInStorageButNotClient(t *testing.T) {
588 greetingTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
589 defer os.RemoveAll(greetingTempDir)
590 cfg := TestingConfig(t)
591 cfg.DataDir = greetingTempDir
592 seeder, err := NewClient(TestingConfig(t))
593 require.NoError(t, err)
594 seeder.AddTorrentSpec(&TorrentSpec{
595 InfoBytes: greetingMetainfo.InfoBytes,
599 // Check that when the listen port is 0, all the protocols listened on have
600 // the same port, and it isn't zero.
601 func TestClientDynamicListenPortAllProtocols(t *testing.T) {
602 cl, err := NewClient(TestingConfig(t))
603 require.NoError(t, err)
605 port := cl.LocalPort()
606 assert.NotEqual(t, 0, port)
607 cl.eachListener(func(s Listener) bool {
608 assert.Equal(t, port, missinggo.AddrPort(s.Addr()))
613 func TestClientDynamicListenTCPOnly(t *testing.T) {
614 cfg := TestingConfig(t)
615 cfg.DisableUTP = true
616 cfg.DisableTCP = false
617 cl, err := NewClient(cfg)
618 require.NoError(t, err)
620 assert.NotEqual(t, 0, cl.LocalPort())
623 func TestClientDynamicListenUTPOnly(t *testing.T) {
624 cfg := TestingConfig(t)
625 cfg.DisableTCP = true
626 cfg.DisableUTP = false
627 cl, err := NewClient(cfg)
628 require.NoError(t, err)
630 assert.NotEqual(t, 0, cl.LocalPort())
633 func totalConns(tts []*Torrent) (ret int) {
634 for _, tt := range tts {
642 func TestSetMaxEstablishedConn(t *testing.T) {
644 ih := testutil.GreetingMetaInfo().HashInfoBytes()
645 cfg := TestingConfig(t)
646 cfg.DisableAcceptRateLimiting = true
647 cfg.DropDuplicatePeerIds = true
648 for i := 0; i < 3; i += 1 {
649 cl, err := NewClient(cfg)
650 require.NoError(t, err)
652 tt, _ := cl.AddTorrentInfoHash(ih)
653 tt.SetMaxEstablishedConns(2)
654 defer testutil.ExportStatusWriter(cl, fmt.Sprintf("%d", i), t)()
655 tts = append(tts, tt)
658 for _, tt := range tts {
659 for _, _tt := range tts {
661 tt.AddClientPeer(_tt.cl)
666 waitTotalConns := func(num int) {
667 for totalConns(tts) != num {
669 time.Sleep(time.Millisecond)
674 tts[0].SetMaxEstablishedConns(1)
676 tts[0].SetMaxEstablishedConns(0)
678 tts[0].SetMaxEstablishedConns(1)
681 tts[0].SetMaxEstablishedConns(2)
686 // Creates a file containing its own name as data. Make a metainfo from that, adds it to the given
687 // client, and returns a magnet link.
688 func makeMagnet(t *testing.T, cl *Client, dir, name string) string {
689 os.MkdirAll(dir, 0o770)
690 file, err := os.Create(filepath.Join(dir, name))
691 require.NoError(t, err)
692 file.Write([]byte(name))
694 mi := metainfo.MetaInfo{}
696 info := metainfo.Info{PieceLength: 256 * 1024}
697 err = info.BuildFromFilePath(filepath.Join(dir, name))
698 require.NoError(t, err)
699 mi.InfoBytes, err = bencode.Marshal(info)
700 require.NoError(t, err)
701 magnet := mi.Magnet(nil, &info).String()
702 tr, err := cl.AddTorrent(&mi)
703 require.NoError(t, err)
704 require.True(t, tr.Seeding())
709 // https://github.com/anacrolix/torrent/issues/114
710 func TestMultipleTorrentsWithEncryption(t *testing.T) {
711 testSeederLeecherPair(
713 func(cfg *ClientConfig) {
714 cfg.HeaderObfuscationPolicy.Preferred = true
715 cfg.HeaderObfuscationPolicy.RequirePreferred = true
717 func(cfg *ClientConfig) {
718 cfg.HeaderObfuscationPolicy.RequirePreferred = false
723 // Test that the leecher can download a torrent in its entirety from the seeder. Note that the
724 // seeder config is done first.
725 func testSeederLeecherPair(t *testing.T, seeder, leecher func(*ClientConfig)) {
726 cfg := TestingConfig(t)
728 cfg.DataDir = filepath.Join(cfg.DataDir, "server")
729 os.Mkdir(cfg.DataDir, 0o755)
731 server, err := NewClient(cfg)
732 require.NoError(t, err)
734 defer testutil.ExportStatusWriter(server, "s", t)()
735 magnet1 := makeMagnet(t, server, cfg.DataDir, "test1")
736 // Extra torrents are added to test the seeder having to match incoming obfuscated headers
737 // against more than one torrent. See issue #114
738 makeMagnet(t, server, cfg.DataDir, "test2")
739 for i := 0; i < 100; i++ {
740 makeMagnet(t, server, cfg.DataDir, fmt.Sprintf("test%d", i+2))
742 cfg = TestingConfig(t)
743 cfg.DataDir = filepath.Join(cfg.DataDir, "client")
745 client, err := NewClient(cfg)
746 require.NoError(t, err)
748 defer testutil.ExportStatusWriter(client, "c", t)()
749 tr, err := client.AddMagnet(magnet1)
750 require.NoError(t, err)
751 tr.AddClientPeer(server)
757 // This appears to be the situation with the S3 BitTorrent client.
758 func TestObfuscatedHeaderFallbackSeederDisallowsLeecherPrefers(t *testing.T) {
759 // Leecher prefers obfuscation, but the seeder does not allow it.
760 testSeederLeecherPair(
762 func(cfg *ClientConfig) {
763 cfg.HeaderObfuscationPolicy.Preferred = false
764 cfg.HeaderObfuscationPolicy.RequirePreferred = true
766 func(cfg *ClientConfig) {
767 cfg.HeaderObfuscationPolicy.Preferred = true
768 cfg.HeaderObfuscationPolicy.RequirePreferred = false
773 func TestObfuscatedHeaderFallbackSeederRequiresLeecherPrefersNot(t *testing.T) {
774 // Leecher prefers no obfuscation, but the seeder enforces it.
775 testSeederLeecherPair(
777 func(cfg *ClientConfig) {
778 cfg.HeaderObfuscationPolicy.Preferred = true
779 cfg.HeaderObfuscationPolicy.RequirePreferred = true
781 func(cfg *ClientConfig) {
782 cfg.HeaderObfuscationPolicy.Preferred = false
783 cfg.HeaderObfuscationPolicy.RequirePreferred = false
788 func TestClientAddressInUse(t *testing.T) {
789 s, _ := NewUtpSocket("udp", ":50007", nil, log.Default)
793 cfg := TestingConfig(t).SetListenAddr(":50007")
794 cfg.DisableUTP = false
795 cl, err := NewClient(cfg)
796 require.Error(t, err)
800 func TestClientHasDhtServersWhenUtpDisabled(t *testing.T) {
801 cc := TestingConfig(t)
804 cl, err := NewClient(cc)
805 require.NoError(t, err)
807 assert.NotEmpty(t, cl.DhtServers())
810 func TestClientDisabledImplicitNetworksButDhtEnabled(t *testing.T) {
811 cfg := TestingConfig(t)
812 cfg.DisableTCP = true
813 cfg.DisableUTP = true
815 cl, err := NewClient(cfg)
816 require.NoError(t, err)
818 assert.Empty(t, cl.listeners)
819 assert.NotEmpty(t, cl.DhtServers())
822 func TestBadPeerIpPort(t *testing.T) {
823 for _, tc := range []struct {
830 {"empty both", nil, 0, true, func(*Client) {}},
831 {"empty/nil ip", nil, 6666, true, func(*Client) {}},
834 net.ParseIP("127.0.0.1/32"),
839 "in doppleganger addresses",
840 net.ParseIP("127.0.0.1/32"),
844 cl.dopplegangerAddrs["10.0.0.1:2322"] = struct{}{}
849 net.ParseIP("10.0.0.1"),
853 cl.ipBlockList = iplist.New([]iplist.Range{
854 {First: net.ParseIP("10.0.0.1"), Last: net.ParseIP("10.0.0.255")},
860 net.ParseIP("10.0.0.1"),
864 ipAddr, ok := netip.AddrFromSlice(net.ParseIP("10.0.0.1"))
866 cl.badPeerIPs = map[netip.Addr]struct{}{}
867 cl.badPeerIPs[ipAddr] = struct{}{}
872 net.ParseIP("10.0.0.1"),
878 t.Run(tc.title, func(t *testing.T) {
879 cfg := TestingConfig(t)
880 cfg.DisableTCP = true
881 cfg.DisableUTP = true
883 cl, err := NewClient(cfg)
884 require.NoError(t, err)
888 require.Equal(t, tc.expectedOk, cl.badPeerIPPort(tc.ip, tc.port))