2014-03-17 14:44:22 +00:00
|
|
|
package torrent
|
|
|
|
|
|
|
|
import (
|
2015-02-26 14:46:02 +00:00
|
|
|
"encoding/binary"
|
2014-12-04 01:57:43 +00:00
|
|
|
"fmt"
|
2015-02-26 14:46:02 +00:00
|
|
|
"io/ioutil"
|
2014-12-04 01:57:43 +00:00
|
|
|
"log"
|
|
|
|
"net"
|
2014-03-20 05:58:09 +00:00
|
|
|
"os"
|
2014-03-17 14:44:22 +00:00
|
|
|
"testing"
|
2014-11-18 00:04:09 +00:00
|
|
|
"time"
|
2014-08-21 08:24:19 +00:00
|
|
|
|
2015-05-20 12:20:11 +00:00
|
|
|
_ "github.com/anacrolix/envpprof"
|
2015-03-26 06:18:08 +00:00
|
|
|
"github.com/anacrolix/utp"
|
2015-02-26 14:46:02 +00:00
|
|
|
"github.com/bradfitz/iter"
|
2015-03-27 15:50:55 +00:00
|
|
|
"gopkg.in/check.v1"
|
2015-02-26 14:46:02 +00:00
|
|
|
|
2015-04-29 14:31:34 +00:00
|
|
|
"github.com/anacrolix/torrent/bencode"
|
2015-03-26 06:18:08 +00:00
|
|
|
"github.com/anacrolix/torrent/data/blob"
|
2015-03-20 05:37:44 +00:00
|
|
|
"github.com/anacrolix/torrent/internal/testutil"
|
|
|
|
"github.com/anacrolix/torrent/util"
|
2014-03-17 14:44:22 +00:00
|
|
|
)
|
|
|
|
|
2015-03-18 07:32:31 +00:00
|
|
|
func init() {
|
|
|
|
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
|
|
|
}
|
|
|
|
|
2015-02-26 14:46:02 +00:00
|
|
|
var TestingConfig = Config{
|
2015-03-18 07:32:31 +00:00
|
|
|
ListenAddr: "localhost:0",
|
2015-02-26 14:46:02 +00:00
|
|
|
NoDHT: true,
|
|
|
|
DisableTrackers: true,
|
|
|
|
NoDefaultBlocklist: true,
|
|
|
|
DisableMetainfoCache: true,
|
|
|
|
}
|
|
|
|
|
2014-08-21 08:07:06 +00:00
|
|
|
func TestClientDefault(t *testing.T) {
|
2015-03-18 07:32:31 +00:00
|
|
|
cl, err := NewClient(&TestingConfig)
|
2014-08-21 08:07:06 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-03-08 06:28:14 +00:00
|
|
|
cl.Close()
|
2014-08-21 08:07:06 +00:00
|
|
|
}
|
|
|
|
|
2015-02-06 03:54:59 +00:00
|
|
|
func TestAddDropTorrent(t *testing.T) {
|
2015-03-10 17:22:56 +00:00
|
|
|
cl, err := NewClient(&TestingConfig)
|
2015-02-06 03:54:59 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-03-08 06:28:14 +00:00
|
|
|
defer cl.Close()
|
2015-02-06 03:54:59 +00:00
|
|
|
dir, mi := testutil.GreetingTestTorrent()
|
|
|
|
defer os.RemoveAll(dir)
|
2015-03-18 07:32:31 +00:00
|
|
|
tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
|
2015-02-06 03:54:59 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-03-18 07:32:31 +00:00
|
|
|
if !new {
|
|
|
|
t.FailNow()
|
|
|
|
}
|
2015-02-06 03:54:59 +00:00
|
|
|
tt.Drop()
|
|
|
|
}
|
|
|
|
|
2014-03-17 14:44:22 +00:00
|
|
|
func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
|
|
|
|
t.SkipNow()
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAddTorrentNoUsableURLs(t *testing.T) {
|
|
|
|
t.SkipNow()
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAddPeersToUnknownTorrent(t *testing.T) {
|
|
|
|
t.SkipNow()
|
|
|
|
}
|
2014-03-20 05:58:09 +00:00
|
|
|
|
|
|
|
func TestPieceHashSize(t *testing.T) {
|
2014-04-08 16:36:05 +00:00
|
|
|
if pieceHash.Size() != 20 {
|
2014-03-20 05:58:09 +00:00
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestTorrentInitialState(t *testing.T) {
|
|
|
|
dir, mi := testutil.GreetingTestTorrent()
|
|
|
|
defer os.RemoveAll(dir)
|
2014-08-21 08:24:19 +00:00
|
|
|
tor, err := newTorrent(func() (ih InfoHash) {
|
|
|
|
util.CopyExact(ih[:], mi.Info.Hash)
|
|
|
|
return
|
2015-03-18 07:32:31 +00:00
|
|
|
}())
|
2014-06-28 09:38:31 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-03-18 07:32:31 +00:00
|
|
|
err = tor.setMetadata(&mi.Info.Info, mi.Info.Bytes, nil)
|
2014-03-20 05:58:09 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-02-26 14:46:02 +00:00
|
|
|
if len(tor.Pieces) != 3 {
|
2014-03-20 05:58:09 +00:00
|
|
|
t.Fatal("wrong number of pieces")
|
|
|
|
}
|
|
|
|
p := tor.Pieces[0]
|
2014-12-09 03:59:01 +00:00
|
|
|
tor.pendAllChunkSpecs(0)
|
2015-05-16 00:51:48 +00:00
|
|
|
if p.numPendingChunks() != 1 {
|
2014-07-09 16:59:37 +00:00
|
|
|
t.Fatalf("should only be 1 chunk: %v", p.PendingChunkSpecs)
|
2014-03-20 05:58:09 +00:00
|
|
|
}
|
2015-02-26 14:46:02 +00:00
|
|
|
// TODO: Set chunkSize to 2, to test odd/even silliness.
|
2015-05-16 00:51:48 +00:00
|
|
|
if chunkIndexSpec(0, tor.pieceLength(0)).Length != 5 {
|
|
|
|
t.Fatal("pending chunk spec is incorrect")
|
2014-03-20 05:58:09 +00:00
|
|
|
}
|
|
|
|
}
|
2014-06-29 09:07:43 +00:00
|
|
|
|
|
|
|
func TestUnmarshalPEXMsg(t *testing.T) {
|
|
|
|
var m peerExchangeMessage
|
|
|
|
if err := bencode.Unmarshal([]byte("d5:added12:\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0ce"), &m); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if len(m.Added) != 2 {
|
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
if m.Added[0].Port != 0x506 {
|
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
}
|
2014-11-18 00:04:09 +00:00
|
|
|
|
|
|
|
func TestReducedDialTimeout(t *testing.T) {
|
|
|
|
for _, _case := range []struct {
|
|
|
|
Max time.Duration
|
|
|
|
HalfOpenLimit int
|
|
|
|
PendingPeers int
|
|
|
|
ExpectedReduced time.Duration
|
|
|
|
}{
|
2014-11-19 03:53:00 +00:00
|
|
|
{nominalDialTimeout, 40, 0, nominalDialTimeout},
|
|
|
|
{nominalDialTimeout, 40, 1, nominalDialTimeout},
|
|
|
|
{nominalDialTimeout, 40, 39, nominalDialTimeout},
|
|
|
|
{nominalDialTimeout, 40, 40, nominalDialTimeout / 2},
|
|
|
|
{nominalDialTimeout, 40, 80, nominalDialTimeout / 3},
|
|
|
|
{nominalDialTimeout, 40, 4000, nominalDialTimeout / 101},
|
2014-11-18 00:04:09 +00:00
|
|
|
} {
|
|
|
|
reduced := reducedDialTimeout(_case.Max, _case.HalfOpenLimit, _case.PendingPeers)
|
2014-11-19 03:53:00 +00:00
|
|
|
expected := _case.ExpectedReduced
|
|
|
|
if expected < minDialTimeout {
|
|
|
|
expected = minDialTimeout
|
|
|
|
}
|
|
|
|
if reduced != expected {
|
2014-11-18 00:04:09 +00:00
|
|
|
t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-12-04 01:57:43 +00:00
|
|
|
|
|
|
|
func TestUTPRawConn(t *testing.T) {
|
2015-01-08 12:26:39 +00:00
|
|
|
l, err := utp.NewSocket("")
|
2014-12-04 01:57:43 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer l.Close()
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
_, err := l.Accept()
|
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
// Connect a UTP peer to see if the RawConn will still work.
|
2015-01-08 12:26:39 +00:00
|
|
|
utpPeer, err := func() *utp.Socket {
|
|
|
|
s, _ := utp.NewSocket("")
|
|
|
|
return s
|
|
|
|
}().Dial(fmt.Sprintf("localhost:%d", util.AddrPort(l.Addr())))
|
2014-12-04 01:57:43 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error dialing utp listener: %s", err)
|
|
|
|
}
|
|
|
|
defer utpPeer.Close()
|
|
|
|
peer, err := net.ListenPacket("udp", ":0")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer peer.Close()
|
|
|
|
|
|
|
|
msgsReceived := 0
|
2015-05-24 11:37:14 +00:00
|
|
|
// How many messages to send. I've set this to double the channel buffer
|
|
|
|
// size in the raw packetConn.
|
|
|
|
const N = 200
|
2014-12-04 01:57:43 +00:00
|
|
|
readerStopped := make(chan struct{})
|
|
|
|
// The reader goroutine.
|
|
|
|
go func() {
|
|
|
|
defer close(readerStopped)
|
|
|
|
b := make([]byte, 500)
|
|
|
|
for i := 0; i < N; i++ {
|
2015-02-09 13:17:04 +00:00
|
|
|
n, _, err := l.PacketConn().ReadFrom(b)
|
2014-12-04 01:57:43 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error reading from raw conn: %s", err)
|
|
|
|
}
|
|
|
|
msgsReceived++
|
|
|
|
var d int
|
|
|
|
fmt.Sscan(string(b[:n]), &d)
|
|
|
|
if d != i {
|
|
|
|
log.Printf("got wrong number: expected %d, got %d", i, d)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2014-12-26 06:19:01 +00:00
|
|
|
udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", util.AddrPort(l.Addr())))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2014-12-04 01:57:43 +00:00
|
|
|
for i := 0; i < N; i++ {
|
2014-12-26 06:19:01 +00:00
|
|
|
_, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr)
|
2014-12-04 01:57:43 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
time.Sleep(time.Microsecond)
|
|
|
|
}
|
|
|
|
select {
|
|
|
|
case <-readerStopped:
|
|
|
|
case <-time.After(time.Second):
|
|
|
|
t.Fatal("reader timed out")
|
|
|
|
}
|
|
|
|
if msgsReceived != N {
|
|
|
|
t.Fatalf("messages received: %d", msgsReceived)
|
|
|
|
}
|
|
|
|
}
|
2015-01-11 10:42:57 +00:00
|
|
|
|
|
|
|
func TestTwoClientsArbitraryPorts(t *testing.T) {
|
|
|
|
for i := 0; i < 2; i++ {
|
2015-03-18 07:32:31 +00:00
|
|
|
cl, err := NewClient(&TestingConfig)
|
2015-01-11 10:42:57 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-03-08 06:28:14 +00:00
|
|
|
defer cl.Close()
|
2015-01-11 10:42:57 +00:00
|
|
|
}
|
|
|
|
}
|
2015-02-26 14:46:02 +00:00
|
|
|
|
|
|
|
func TestAddDropManyTorrents(t *testing.T) {
|
|
|
|
cl, _ := NewClient(&TestingConfig)
|
2015-03-08 06:28:14 +00:00
|
|
|
defer cl.Close()
|
2015-02-26 14:46:02 +00:00
|
|
|
for i := range iter.N(1000) {
|
2015-03-18 07:32:31 +00:00
|
|
|
var spec TorrentSpec
|
|
|
|
binary.PutVarint(spec.InfoHash[:], int64(i))
|
|
|
|
tt, new, err := cl.AddTorrentSpec(&spec)
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
if !new {
|
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
defer tt.Drop()
|
2015-02-26 14:46:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestClientTransfer(t *testing.T) {
|
|
|
|
greetingTempDir, mi := testutil.GreetingTestTorrent()
|
|
|
|
defer os.RemoveAll(greetingTempDir)
|
|
|
|
cfg := TestingConfig
|
|
|
|
cfg.DataDir = greetingTempDir
|
|
|
|
seeder, err := NewClient(&cfg)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-03-08 06:28:14 +00:00
|
|
|
defer seeder.Close()
|
2015-03-18 07:32:31 +00:00
|
|
|
seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
|
2015-02-26 14:46:02 +00:00
|
|
|
leecherDataDir, err := ioutil.TempDir("", "")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(leecherDataDir)
|
2015-03-07 06:11:45 +00:00
|
|
|
// cfg.TorrentDataOpener = func(info *metainfo.Info) (data.Data, error) {
|
|
|
|
// return blob.TorrentData(info, leecherDataDir), nil
|
|
|
|
// }
|
|
|
|
cfg.TorrentDataOpener = blob.NewStore(leecherDataDir).OpenTorrent
|
2015-02-26 14:46:02 +00:00
|
|
|
leecher, _ := NewClient(&cfg)
|
2015-03-08 06:28:14 +00:00
|
|
|
defer leecher.Close()
|
2015-03-18 07:32:31 +00:00
|
|
|
leecherGreeting, _, _ := leecher.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
|
2015-02-26 14:46:02 +00:00
|
|
|
leecherGreeting.AddPeers([]Peer{
|
|
|
|
Peer{
|
|
|
|
IP: util.AddrIP(seeder.ListenAddr()),
|
|
|
|
Port: util.AddrPort(seeder.ListenAddr()),
|
|
|
|
},
|
|
|
|
})
|
2015-04-14 13:59:41 +00:00
|
|
|
r := leecherGreeting.NewReader()
|
|
|
|
defer r.Close()
|
|
|
|
_greeting, err := ioutil.ReadAll(r)
|
2015-02-26 14:46:02 +00:00
|
|
|
if err != nil {
|
2015-04-14 13:59:41 +00:00
|
|
|
t.Fatalf("%q %s", string(_greeting), err)
|
2015-02-26 14:46:02 +00:00
|
|
|
}
|
|
|
|
greeting := string(_greeting)
|
|
|
|
if greeting != testutil.GreetingFileContents {
|
|
|
|
t.Fatal(":(")
|
|
|
|
}
|
|
|
|
}
|
2015-03-04 02:07:11 +00:00
|
|
|
|
|
|
|
func TestReadaheadPieces(t *testing.T) {
|
|
|
|
for _, case_ := range []struct {
|
|
|
|
readaheadBytes, pieceLength int64
|
|
|
|
readaheadPieces int
|
|
|
|
}{
|
|
|
|
{5 * 1024 * 1024, 256 * 1024, 19},
|
|
|
|
{5 * 1024 * 1024, 5 * 1024 * 1024, 0},
|
|
|
|
{5*1024*1024 - 1, 5 * 1024 * 1024, 0},
|
|
|
|
{5 * 1024 * 1024, 5*1024*1024 - 1, 1},
|
|
|
|
{0, 5 * 1024 * 1024, -1},
|
|
|
|
{5 * 1024 * 1024, 1048576, 4},
|
|
|
|
} {
|
|
|
|
if readaheadPieces(case_.readaheadBytes, case_.pieceLength) != case_.readaheadPieces {
|
2015-03-12 09:06:23 +00:00
|
|
|
t.Fatalf("case failed: %v", case_)
|
2015-03-04 02:07:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-03-27 15:50:55 +00:00
|
|
|
|
|
|
|
func (suite) TestMergingTrackersByAddingSpecs(c *check.C) {
|
|
|
|
cl, _ := NewClient(&TestingConfig)
|
|
|
|
defer cl.Close()
|
|
|
|
spec := TorrentSpec{}
|
|
|
|
T, new, _ := cl.AddTorrentSpec(&spec)
|
|
|
|
if !new {
|
|
|
|
c.FailNow()
|
|
|
|
}
|
|
|
|
spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
|
|
|
|
_, new, _ = cl.AddTorrentSpec(&spec)
|
|
|
|
if new {
|
|
|
|
c.FailNow()
|
|
|
|
}
|
|
|
|
c.Assert(T.Trackers[0][0].URL(), check.Equals, "http://a")
|
|
|
|
c.Assert(T.Trackers[1][0].URL(), check.Equals, "udp://b")
|
|
|
|
}
|