Example #1
0
// Creates a seeder and a leecher, and ensures the data transfers when a read
// is attempted on the leecher.
func testClientTransfer(t *testing.T, ps testClientTransferParams) {
	greetingTempDir, mi := testutil.GreetingTestTorrent()
	defer os.RemoveAll(greetingTempDir)
	cfg := TestingConfig
	cfg.Seed = true
	if ps.SeederStorage != nil {
		cfg.DefaultStorage = ps.SeederStorage(greetingTempDir)
	} else {
		cfg.DataDir = greetingTempDir
	}
	seeder, err := NewClient(&cfg)
	require.NoError(t, err)
	defer seeder.Close()
	if ps.ExportClientStatus {
		testutil.ExportStatusWriter(seeder, "s")
	}
	seederTorrent, new, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
	require.NoError(t, err)
	assert.True(t, new)
	leecherDataDir, err := ioutil.TempDir("", "")
	require.NoError(t, err)
	defer os.RemoveAll(leecherDataDir)
	cfg.DefaultStorage = ps.LeecherStorage(leecherDataDir)
	leecher, err := NewClient(&cfg)
	require.NoError(t, err)
	defer leecher.Close()
	if ps.ExportClientStatus {
		testutil.ExportStatusWriter(leecher, "l")
	}
	leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
		ret = TorrentSpecFromMetaInfo(mi)
		ret.ChunkSize = 2
		ret.Storage = storage.NewFile(leecherDataDir)
		return
	}())
	require.NoError(t, err)
	assert.True(t, new)
	addClientPeer(leecherGreeting, seeder)
	r := leecherGreeting.NewReader()
	defer r.Close()
	if ps.Responsive {
		r.SetResponsive()
	}
	if ps.SetReadahead {
		r.SetReadahead(ps.Readahead)
	}
	assertReadAllGreeting(t, r)
	// After one read through, we can assume certain torrent statistics.
	// These are not a strict requirement. It is however interesting to
	// follow.
	t.Logf("%#v", seederTorrent.Stats())
	assert.EqualValues(t, 13, seederTorrent.Stats().DataBytesWritten)
	assert.EqualValues(t, 8, seederTorrent.Stats().ChunksWritten)
	assert.EqualValues(t, 13, leecherGreeting.Stats().DataBytesRead)
	assert.EqualValues(t, 8, leecherGreeting.Stats().ChunksRead)
	// Read through again for the cases where the torrent data size exceeds
	// the size of the cache.
	assertReadAllGreeting(t, r)
}
Example #2
0
func TestHashPieceAfterStorageClosed(t *testing.T) {
	td, err := ioutil.TempDir("", "")
	require.NoError(t, err)
	defer os.RemoveAll(td)
	cs := storage.NewFile(td)
	tt := &Torrent{}
	tt.info = &testutil.GreetingMetaInfo().Info
	tt.makePieces()
	tt.storage, err = cs.OpenTorrent(tt.info)
	require.NoError(t, err)
	require.NoError(t, tt.storage.Close())
	tt.hashPiece(0)
}
Example #3
0
func TestTorrentInitialState(t *testing.T) {
	dir, mi := testutil.GreetingTestTorrent()
	defer os.RemoveAll(dir)
	tor := newTorrent(func() (ih metainfo.Hash) {
		missinggo.CopyExact(ih[:], mi.Info.Hash)
		return
	}())
	tor.chunkSize = 2
	tor.storageOpener = storage.NewFile(dir)
	// Needed to lock for asynchronous piece verification.
	tor.cl = new(Client)
	err := tor.setMetadata(&mi.Info.Info, mi.Info.Bytes)
	require.NoError(t, err)
	require.Len(t, tor.pieces, 3)
	tor.pendAllChunkSpecs(0)
	assert.EqualValues(t, 3, tor.pieceNumPendingChunks(0))
	assert.EqualValues(t, chunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
}
Example #4
0
func TestTorrentInitialState(t *testing.T) {
	dir, mi := testutil.GreetingTestTorrent()
	defer os.RemoveAll(dir)
	tor := &Torrent{
		infoHash:          mi.Info.Hash(),
		pieceStateChanges: pubsub.NewPubSub(),
	}
	tor.chunkSize = 2
	tor.storageOpener = storage.NewFile("/dev/null")
	// Needed to lock for asynchronous piece verification.
	tor.cl = new(Client)
	err := tor.setInfoBytes(mi.Info.Bytes)
	require.NoError(t, err)
	require.Len(t, tor.pieces, 3)
	tor.pendAllChunkSpecs(0)
	tor.cl.mu.Lock()
	assert.EqualValues(t, 3, tor.pieceNumPendingChunks(0))
	tor.cl.mu.Unlock()
	assert.EqualValues(t, chunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
}
Example #5
0
func testClientTransfer(t *testing.T, ps testClientTransferParams) {
	greetingTempDir, mi := testutil.GreetingTestTorrent()
	defer os.RemoveAll(greetingTempDir)
	cfg := TestingConfig
	cfg.Seed = true
	if ps.SeederStorage != nil {
		cfg.DefaultStorage = ps.SeederStorage(greetingTempDir)
	} else {
		cfg.DataDir = greetingTempDir
	}
	seeder, err := NewClient(&cfg)
	require.NoError(t, err)
	defer seeder.Close()
	if ps.ExportClientStatus {
		testutil.ExportStatusWriter(seeder, "s")
	}
	_, new, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
	require.NoError(t, err)
	assert.True(t, new)
	leecherDataDir, err := ioutil.TempDir("", "")
	require.NoError(t, err)
	defer os.RemoveAll(leecherDataDir)
	fc, err := filecache.NewCache(leecherDataDir)
	require.NoError(t, err)
	if ps.SetLeecherStorageCapacity {
		fc.SetCapacity(ps.LeecherStorageCapacity)
	}
	cfg.DefaultStorage = ps.LeecherFileCachePieceStorageFactory(fc)
	leecher, err := NewClient(&cfg)
	require.NoError(t, err)
	defer leecher.Close()
	if ps.ExportClientStatus {
		testutil.ExportStatusWriter(leecher, "l")
	}
	leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
		ret = TorrentSpecFromMetaInfo(mi)
		ret.ChunkSize = 2
		ret.Storage = storage.NewFile(leecherDataDir)
		return
	}())
	require.NoError(t, err)
	assert.True(t, new)
	leecherGreeting.AddPeers([]Peer{
		Peer{
			IP:   missinggo.AddrIP(seeder.ListenAddr()),
			Port: missinggo.AddrPort(seeder.ListenAddr()),
		},
	})
	r := leecherGreeting.NewReader()
	defer r.Close()
	if ps.Responsive {
		r.SetResponsive()
	}
	if ps.SetReadahead {
		r.SetReadahead(ps.Readahead)
	}
	for range iter.N(2) {
		pos, err := r.Seek(0, os.SEEK_SET)
		assert.NoError(t, err)
		assert.EqualValues(t, 0, pos)
		_greeting, err := ioutil.ReadAll(r)
		assert.NoError(t, err)
		assert.EqualValues(t, testutil.GreetingFileContents, _greeting)
	}
}
Example #6
0
// Creates a new client.
func NewClient(cfg *Config) (cl *Client, err error) {
	if cfg == nil {
		cfg = &Config{}
	}

	defer func() {
		if err != nil {
			cl = nil
		}
	}()
	cl = &Client{
		halfOpenLimit:     socketsPerTorrent,
		config:            *cfg,
		defaultStorage:    cfg.DefaultStorage,
		dopplegangerAddrs: make(map[string]struct{}),
		torrents:          make(map[metainfo.Hash]*Torrent),
	}
	missinggo.CopyExact(&cl.extensionBytes, defaultExtensionBytes)
	cl.event.L = &cl.mu
	if cl.defaultStorage == nil {
		cl.defaultStorage = storage.NewFile(cfg.DataDir)
	}
	if cfg.IPBlocklist != nil {
		cl.ipBlockList = cfg.IPBlocklist
	}

	if cfg.PeerID != "" {
		missinggo.CopyExact(&cl.peerID, cfg.PeerID)
	} else {
		o := copy(cl.peerID[:], bep20)
		_, err = rand.Read(cl.peerID[o:])
		if err != nil {
			panic("error generating peer id")
		}
	}

	cl.tcpListener, cl.utpSock, cl.listenAddr, err = listen(
		!cl.config.DisableTCP,
		!cl.config.DisableUTP,
		func() string {
			if cl.config.DisableIPv6 {
				return "4"
			} else {
				return ""
			}
		}(),
		cl.config.ListenAddr)
	if err != nil {
		return
	}
	if cl.tcpListener != nil {
		go cl.acceptConnections(cl.tcpListener, false)
	}
	if cl.utpSock != nil {
		go cl.acceptConnections(cl.utpSock, true)
	}
	if !cfg.NoDHT {
		dhtCfg := cfg.DHTConfig
		if dhtCfg.IPBlocklist == nil {
			dhtCfg.IPBlocklist = cl.ipBlockList
		}
		dhtCfg.Addr = firstNonEmptyString(dhtCfg.Addr, cl.listenAddr, cl.config.ListenAddr)
		if dhtCfg.Conn == nil && cl.utpSock != nil {
			dhtCfg.Conn = cl.utpSock
		}
		cl.dHT, err = dht.NewServer(&dhtCfg)
		if err != nil {
			return
		}
	}

	return
}