16 "github.com/frankban/quicktest"
17 "github.com/stretchr/testify/assert"
18 "github.com/stretchr/testify/require"
20 "github.com/anacrolix/log"
22 "github.com/anacrolix/dht/v2"
23 "github.com/anacrolix/missinggo/v2"
24 "github.com/anacrolix/missinggo/v2/filecache"
26 "github.com/anacrolix/torrent/bencode"
27 "github.com/anacrolix/torrent/internal/testutil"
28 "github.com/anacrolix/torrent/iplist"
29 "github.com/anacrolix/torrent/metainfo"
30 "github.com/anacrolix/torrent/storage"
33 func TestClientDefault(t *testing.T) {
34 cl, err := NewClient(TestingConfig(t))
35 require.NoError(t, err)
36 require.Empty(t, cl.Close())
39 func TestClientNilConfig(t *testing.T) {
40 // The default config will put crap in the working directory.
41 origDir, _ := os.Getwd()
42 defer os.Chdir(origDir)
44 cl, err := NewClient(nil)
45 require.NoError(t, err)
46 require.Empty(t, cl.Close())
49 func TestAddDropTorrent(t *testing.T) {
50 cl, err := NewClient(TestingConfig(t))
51 require.NoError(t, err)
53 dir, mi := testutil.GreetingTestTorrent()
54 defer os.RemoveAll(dir)
55 tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
56 require.NoError(t, err)
58 tt.SetMaxEstablishedConns(0)
59 tt.SetMaxEstablishedConns(1)
63 func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
68 func TestAddTorrentNoUsableURLs(t *testing.T) {
73 func TestAddPeersToUnknownTorrent(t *testing.T) {
78 func TestPieceHashSize(t *testing.T) {
79 assert.Equal(t, 20, pieceHash.Size())
82 func TestTorrentInitialState(t *testing.T) {
83 dir, mi := testutil.GreetingTestTorrent()
84 defer os.RemoveAll(dir)
86 cl.init(TestingConfig(t))
90 storage.NewFileWithCompletion(t.TempDir(), storage.NewMapPieceCompletion()),
94 err := tor.setInfoBytesLocked(mi.InfoBytes)
96 require.NoError(t, err)
97 require.Len(t, tor.pieces, 3)
98 tor.pendAllChunkSpecs(0)
100 assert.EqualValues(t, 3, tor.pieceNumPendingChunks(0))
102 assert.EqualValues(t, ChunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
105 func TestReducedDialTimeout(t *testing.T) {
106 cfg := NewDefaultClientConfig()
107 for _, _case := range []struct {
111 ExpectedReduced time.Duration
113 {cfg.NominalDialTimeout, 40, 0, cfg.NominalDialTimeout},
114 {cfg.NominalDialTimeout, 40, 1, cfg.NominalDialTimeout},
115 {cfg.NominalDialTimeout, 40, 39, cfg.NominalDialTimeout},
116 {cfg.NominalDialTimeout, 40, 40, cfg.NominalDialTimeout / 2},
117 {cfg.NominalDialTimeout, 40, 80, cfg.NominalDialTimeout / 3},
118 {cfg.NominalDialTimeout, 40, 4000, cfg.NominalDialTimeout / 101},
120 reduced := reducedDialTimeout(cfg.MinDialTimeout, _case.Max, _case.HalfOpenLimit, _case.PendingPeers)
121 expected := _case.ExpectedReduced
122 if expected < cfg.MinDialTimeout {
123 expected = cfg.MinDialTimeout
125 if reduced != expected {
126 t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
131 func TestAddDropManyTorrents(t *testing.T) {
132 cl, err := NewClient(TestingConfig(t))
133 require.NoError(t, err)
135 for i := 0; i < 1000; i += 1 {
137 binary.PutVarint(spec.InfoHash[:], int64(i))
138 tt, new, err := cl.AddTorrentSpec(&spec)
139 assert.NoError(t, err)
145 func fileCachePieceResourceStorage(fc *filecache.Cache) storage.ClientImpl {
146 return storage.NewResourcePiecesOpts(
147 fc.AsResourceProvider(),
148 storage.ResourcePiecesOpts{
149 LeaveIncompleteChunks: true,
154 func TestMergingTrackersByAddingSpecs(t *testing.T) {
155 cl, err := NewClient(TestingConfig(t))
156 require.NoError(t, err)
158 spec := TorrentSpec{}
159 T, new, _ := cl.AddTorrentSpec(&spec)
163 spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
164 _, new, _ = cl.AddTorrentSpec(&spec)
166 assert.EqualValues(t, [][]string{{"http://a"}, {"udp://b"}}, T.metainfo.AnnounceList)
167 // Because trackers are disabled in TestingConfig.
168 assert.EqualValues(t, 0, len(T.trackerAnnouncers))
171 // We read from a piece which is marked completed, but is missing data.
172 func TestCompletedPieceWrongSize(t *testing.T) {
173 cfg := TestingConfig(t)
174 cfg.DefaultStorage = badStorage{}
175 cl, err := NewClient(cfg)
176 require.NoError(t, err)
178 info := metainfo.Info{
180 Pieces: make([]byte, 20),
181 Files: []metainfo.FileInfo{
182 {Path: []string{"greeting"}, Length: 13},
185 b, err := bencode.Marshal(info)
186 require.NoError(t, err)
187 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
189 InfoHash: metainfo.HashBytes(b),
191 require.NoError(t, err)
196 quicktest.Check(t, iotest.TestReader(r, []byte(testutil.GreetingFileContents)), quicktest.IsNil)
199 func BenchmarkAddLargeTorrent(b *testing.B) {
200 cfg := TestingConfig(b)
201 cfg.DisableTCP = true
202 cfg.DisableUTP = true
203 cl, err := NewClient(cfg)
204 require.NoError(b, err)
207 for i := 0; i < b.N; i += 1 {
208 t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
216 func TestResponsive(t *testing.T) {
217 seederDataDir, mi := testutil.GreetingTestTorrent()
218 defer os.RemoveAll(seederDataDir)
219 cfg := TestingConfig(t)
221 cfg.DataDir = seederDataDir
222 seeder, err := NewClient(cfg)
225 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
226 seederTorrent.VerifyData()
227 leecherDataDir := t.TempDir()
228 cfg = TestingConfig(t)
229 cfg.DataDir = leecherDataDir
230 leecher, err := NewClient(cfg)
232 defer leecher.Close()
233 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
234 ret = TorrentSpecFromMetaInfo(mi)
238 leecherTorrent.AddClientPeer(seeder)
239 reader := leecherTorrent.NewReader()
241 reader.SetReadahead(0)
242 reader.SetResponsive()
244 _, err = reader.Seek(3, io.SeekStart)
245 require.NoError(t, err)
246 _, err = io.ReadFull(reader, b)
248 assert.EqualValues(t, "lo", string(b))
249 _, err = reader.Seek(11, io.SeekStart)
250 require.NoError(t, err)
251 n, err := io.ReadFull(reader, b)
253 assert.EqualValues(t, 2, n)
254 assert.EqualValues(t, "d\n", string(b))
257 // TestResponsive was the first test to fail if uTP is disabled and TCP sockets dial from the
259 func TestResponsiveTcpOnly(t *testing.T) {
260 seederDataDir, mi := testutil.GreetingTestTorrent()
261 defer os.RemoveAll(seederDataDir)
262 cfg := TestingConfig(t)
263 cfg.DisableUTP = true
265 cfg.DataDir = seederDataDir
266 seeder, err := NewClient(cfg)
269 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
270 seederTorrent.VerifyData()
271 leecherDataDir := t.TempDir()
272 cfg = TestingConfig(t)
273 cfg.DataDir = leecherDataDir
274 leecher, err := NewClient(cfg)
276 defer leecher.Close()
277 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
278 ret = TorrentSpecFromMetaInfo(mi)
282 leecherTorrent.AddClientPeer(seeder)
283 reader := leecherTorrent.NewReader()
285 reader.SetReadahead(0)
286 reader.SetResponsive()
288 _, err = reader.Seek(3, io.SeekStart)
289 require.NoError(t, err)
290 _, err = io.ReadFull(reader, b)
292 assert.EqualValues(t, "lo", string(b))
293 _, err = reader.Seek(11, io.SeekStart)
294 require.NoError(t, err)
295 n, err := io.ReadFull(reader, b)
297 assert.EqualValues(t, 2, n)
298 assert.EqualValues(t, "d\n", string(b))
301 func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
302 seederDataDir, mi := testutil.GreetingTestTorrent()
303 defer os.RemoveAll(seederDataDir)
304 cfg := TestingConfig(t)
306 cfg.DataDir = seederDataDir
307 seeder, err := NewClient(cfg)
310 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
311 seederTorrent.VerifyData()
312 leecherDataDir := t.TempDir()
313 cfg = TestingConfig(t)
314 cfg.DataDir = leecherDataDir
315 leecher, err := NewClient(cfg)
317 defer leecher.Close()
318 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
319 ret = TorrentSpecFromMetaInfo(mi)
323 leecherTorrent.AddClientPeer(seeder)
324 reader := leecherTorrent.NewReader()
326 reader.SetReadahead(0)
327 reader.SetResponsive()
329 _, err = reader.Seek(3, io.SeekStart)
330 require.NoError(t, err)
331 _, err = io.ReadFull(reader, b)
333 assert.EqualValues(t, "lo", string(b))
334 _, err = reader.Seek(11, io.SeekStart)
335 require.NoError(t, err)
336 leecherTorrent.Drop()
337 n, err := reader.Read(b)
338 assert.EqualError(t, err, "torrent closed")
339 assert.EqualValues(t, 0, n)
342 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 assert.EqualValues(t, 2, numServers)
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)
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", ":50007", nil, log.Default)
795 cfg := TestingConfig(t).SetListenAddr(":50007")
796 cfg.DisableUTP = false
797 cl, err := NewClient(cfg)
798 require.Error(t, err)
802 func TestClientHasDhtServersWhenUtpDisabled(t *testing.T) {
803 cc := TestingConfig(t)
806 cl, err := NewClient(cc)
807 require.NoError(t, err)
809 assert.NotEmpty(t, cl.DhtServers())
812 func TestClientDisabledImplicitNetworksButDhtEnabled(t *testing.T) {
813 cfg := TestingConfig(t)
814 cfg.DisableTCP = true
815 cfg.DisableUTP = true
817 cl, err := NewClient(cfg)
818 require.NoError(t, err)
820 assert.Empty(t, cl.listeners)
821 assert.NotEmpty(t, cl.DhtServers())
824 func TestBadPeerIpPort(t *testing.T) {
825 for _, tc := range []struct {
832 {"empty both", nil, 0, true, func(*Client) {}},
833 {"empty/nil ip", nil, 6666, true, func(*Client) {}},
836 net.ParseIP("127.0.0.1/32"),
841 "in doppleganger addresses",
842 net.ParseIP("127.0.0.1/32"),
846 cl.dopplegangerAddrs["10.0.0.1:2322"] = struct{}{}
851 net.ParseIP("10.0.0.1"),
855 cl.ipBlockList = iplist.New([]iplist.Range{
856 {First: net.ParseIP("10.0.0.1"), Last: net.ParseIP("10.0.0.255")},
862 net.ParseIP("10.0.0.1"),
866 ipAddr, ok := netip.AddrFromSlice(net.ParseIP("10.0.0.1"))
868 cl.badPeerIPs = map[netip.Addr]struct{}{}
869 cl.badPeerIPs[ipAddr] = struct{}{}
874 net.ParseIP("10.0.0.1"),
880 t.Run(tc.title, func(t *testing.T) {
881 cfg := TestingConfig(t)
882 cfg.DisableTCP = true
883 cfg.DisableUTP = true
885 cl, err := NewClient(cfg)
886 require.NoError(t, err)
890 require.Equal(t, tc.expectedOk, cl.badPeerIPPort(tc.ip, tc.port))