Beispiel #1
0
        TestRig(string savePath, int piecelength, IPieceWriter writer, string[][] trackers, TorrentFile[] files, bool metadataMode)
        {
            this.files       = files;
            this.savePath    = savePath;
            this.piecelength = piecelength;
            this.tier        = trackers;
            MetadataMode     = metadataMode;
            var cacheDir = Path.Combine(Path.GetDirectoryName(typeof(TestRig).Assembly.Location), "test_cache_dir");

            PeerListenerFactory.Creator              = endpoint => new CustomListener();
            LocalPeerDiscoveryFactory.Creator        = port => new ManualLocalPeerListener();
            Dht.Listeners.DhtListenerFactory.Creator = endpoint => new Dht.Listeners.NullDhtListener();
            Engine = new ClientEngine(EngineSettingsBuilder.CreateForTests(
                                          allowLocalPeerDiscovery: true,
                                          dhtPort: 12345,
                                          cacheDirectory: cacheDir,
                                          listenPort: 12345
                                          ));
            if (Directory.Exists(Engine.Settings.MetadataCacheDirectory))
            {
                Directory.Delete(Engine.Settings.MetadataCacheDirectory, true);
            }
            Engine.DiskManager.ChangePieceWriter(writer);
            Writer = writer;

            RecreateManager().Wait();
            MetadataPath = Path.Combine(Engine.Settings.MetadataCacheDirectory, $"{Engine.Torrents.Single ().InfoHash.ToHex ()}.torrent");
        }
Beispiel #2
0
        internal BEncodedDictionary Create(string name, List <TorrentFile> files, CancellationToken token)
        {
            if (PieceLength == 0)
            {
                PieceLength = RecommendedPieceSize(files);
            }

            BEncodedDictionary torrent = BEncodedValue.Clone(Metadata);
            BEncodedDictionary info    = (BEncodedDictionary)torrent ["info"];

            info ["name"] = (BEncodedString)name;
            AddCommonStuff(torrent);

            using (IPieceWriter reader = CreateReader()) {
                info ["pieces"] = (BEncodedString)CalcPiecesHash(files, reader, token);

                if (files.Count == 1 && files [0].Path == name)
                {
                    CreateSingleFileTorrent(torrent, files, reader, name);
                }
                else
                {
                    CreateMultiFileTorrent(torrent, files, reader, name);
                }
            }

            return(torrent);
        }
Beispiel #3
0
        TestRig(string savePath, int piecelength, IPieceWriter writer, string[][] trackers, TorrentFile[] files, bool metadataMode)
        {
            this.files       = files;
            this.savePath    = savePath;
            this.piecelength = piecelength;
            this.tier        = trackers;
            MetadataMode     = metadataMode;
            var cacheDir  = Path.Combine(Path.GetDirectoryName(typeof(TestRig).Assembly.Location), "test_cache_dir");
            var factories = Factories.Default
                            .WithDhtCreator(() => new ManualDhtEngine())
                            .WithDhtListenerCreator(port => new NullDhtListener())
                            .WithLocalPeerDiscoveryCreator(() => new ManualLocalPeerListener())
                            .WithPeerConnectionListenerCreator(endpoint => new CustomListener())
                            .WithTrackerCreator("custom", uri => new Tracker(new CustomTracker(uri)))
            ;

            Engine = new ClientEngine(EngineSettingsBuilder.CreateForTests(
                                          allowLocalPeerDiscovery: true,
                                          dhtEndPoint: new IPEndPoint(IPAddress.Any, 12345),
                                          cacheDirectory: cacheDir,
                                          listenEndPoint: new IPEndPoint(IPAddress.Any, 12345)
                                          ), factories);
            if (Directory.Exists(Engine.Settings.MetadataCacheDirectory))
            {
                Directory.Delete(Engine.Settings.MetadataCacheDirectory, true);
            }
            Engine.DiskManager.SetWriterAsync(writer).GetAwaiter().GetResult();
            Writer = writer;

            RecreateManager().Wait();
            MetadataPath = Path.Combine(Engine.Settings.MetadataCacheDirectory, $"{Engine.Torrents.Single ().InfoHash.ToHex ()}.torrent");
        }
Beispiel #4
0
        public static async ReusableTask WriteToFilesAsync(this IPieceWriter writer, ITorrentData manager, BlockInfo request, byte[] buffer)
        {
            var count         = request.RequestLength;
            var torrentOffset = request.ToByteOffset(manager.PieceLength);

            if (torrentOffset < 0 || torrentOffset + count > manager.Size)
            {
                throw new ArgumentOutOfRangeException(nameof(request));
            }

            int totalWritten = 0;
            var files        = manager.Files;
            int i            = files.FindFileByOffset(torrentOffset);
            var offset       = torrentOffset - files[i].OffsetInTorrent;

            while (totalWritten < count)
            {
                int fileToWrite = (int)Math.Min(files[i].Length - offset, count - totalWritten);
                fileToWrite = Math.Min(fileToWrite, Piece.BlockSize);

                await writer.WriteAsync(files[i], offset, buffer, totalWritten, fileToWrite);

                offset       += fileToWrite;
                totalWritten += fileToWrite;
                if (offset >= files[i].Length)
                {
                    offset = 0;
                    i++;
                }
            }
        }
Beispiel #5
0
        internal DiskManager(EngineSettings settings, IPieceWriter writer = null)
        {
            ReadLimiter = new RateLimiter();
            ReadMonitor = new SpeedMonitor();
            ReadQueue   = new Queue <BufferedIO> ();

            WriteLimiter = new RateLimiter();
            WriteMonitor = new SpeedMonitor();
            WriteQueue   = new Queue <BufferedIO> ();

            UpdateTimer = ValueStopwatch.StartNew();

            Settings = settings ?? throw new ArgumentNullException(nameof(settings));

            // If we pass in an IPieceWriter it should be used  *instead* of these.
            // However we still create these so the properties are non-null.
            DiskWriter = new DiskWriter(settings.MaximumOpenFiles)
            {
                ReadMonitor  = ReadMonitor,
                WriteMonitor = WriteMonitor,
            };
            MemoryWriter = new MemoryWriter(DiskWriter, settings.DiskCacheBytes);

            Writer = writer ?? MemoryWriter;
        }
Beispiel #6
0
        public MemoryWriter (IPieceWriter writer, int capacity)
        {
            if (capacity < 0)
                throw new ArgumentOutOfRangeException (nameof (capacity));

            CachedBlocks = new List<CachedBlock> ();
            Capacity = capacity;
            Writer = writer ?? throw new ArgumentNullException (nameof (writer));
        }
        async Task ChangePieceWriterAsync (IPieceWriter writer)
        {
            writer = writer ?? throw new ArgumentNullException (nameof (writer));

            await MainLoop;
            if (IsRunning)
                throw new InvalidOperationException ("You must stop all active downloads before changing the piece writer used to write data to disk.");
            await DiskManager.SetWriterAsync (writer);
        }
Beispiel #8
0
        public MemoryCache(MemoryPool bufferPool, long capacity, IPieceWriter writer)
        {
            if (capacity < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(capacity));
            }

            BufferPool = bufferPool;
            Capacity   = capacity;
            Writer     = writer ?? throw new ArgumentNullException(nameof(writer));

            CachedBlocks = new Dictionary <ITorrentData, List <CachedBlock> > ();
        }
Beispiel #9
0
        internal MemoryCache(int capacity, IPieceWriter writer)
        {
            if (capacity < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(capacity));
            }

            Capacity = capacity;
            Writer   = writer ?? throw new ArgumentNullException(nameof(writer));

            CachedBlocks = new Dictionary <ITorrentData, List <CachedBlock> > ();
            ReadMonitor  = new SpeedMonitor();
            WriteMonitor = new SpeedMonitor();
        }
Beispiel #10
0
        public ClientEngine(EngineSettings settings, IPeerListener listener, IPieceWriter writer)
        {
            Check.Settings(settings);
            Check.Listener(listener);
            Check.Writer(writer);

            // This is just a sanity check to make sure the ReusableTasks.dll assembly is
            // loadable.
            GC.KeepAlive(ReusableTasks.ReusableTask.CompletedTask);

            PeerId   = GeneratePeerId();
            Listener = listener ?? throw new ArgumentNullException(nameof(listener));
            Settings = settings ?? throw new ArgumentNullException(nameof(settings));

            allTorrents    = new List <TorrentManager> ();
            publicTorrents = new List <TorrentManager> ();
            Torrents       = new ReadOnlyCollection <TorrentManager> (publicTorrents);

            DiskManager       = new DiskManager(Settings, writer);
            ConnectionManager = new ConnectionManager(PeerId, Settings, DiskManager);
            DhtEngine         = new NullDhtEngine();
            listenManager     = new ListenManager(this);
            PortForwarder     = new MonoNatPortForwarder();

            MainLoop.QueueTimeout(TimeSpan.FromMilliseconds(TickLength), delegate {
                if (IsRunning && !Disposed)
                {
                    LogicTick();
                }
                return(!Disposed);
            });

            downloadLimiter  = new RateLimiter();
            downloadLimiters = new RateLimiterGroup {
                new DiskWriterLimiter(DiskManager),
                downloadLimiter,
            };

            uploadLimiter  = new RateLimiter();
            uploadLimiters = new RateLimiterGroup {
                uploadLimiter
            };

            listenManager.Register(listener);

            if (SupportsLocalPeerDiscovery)
            {
                RegisterLocalPeerDiscovery(new LocalPeerDiscovery(Settings));
            }
        }
Beispiel #11
0
        public ClientEngine(EngineSettings settings, IPieceWriter writer)
        {
            Check.Settings(settings);

            // This is just a sanity check to make sure the ReusableTasks.dll assembly is
            // loadable.
            GC.KeepAlive(ReusableTasks.ReusableTask.CompletedTask);

            PeerId   = GeneratePeerId();
            Settings = settings ?? throw new ArgumentNullException(nameof(settings));

            allTorrents    = new List <TorrentManager> ();
            publicTorrents = new List <TorrentManager> ();
            Torrents       = new ReadOnlyCollection <TorrentManager> (publicTorrents);

            DiskManager       = new DiskManager(Settings, writer);
            ConnectionManager = new ConnectionManager(PeerId, Settings, DiskManager);
            listenManager     = new ListenManager(this);
            PortForwarder     = new MonoNatPortForwarder();

            MainLoop.QueueTimeout(TimeSpan.FromMilliseconds(TickLength), delegate {
                if (IsRunning && !Disposed)
                {
                    LogicTick();
                }
                return(!Disposed);
            });

            downloadLimiter  = new RateLimiter();
            downloadLimiters = new RateLimiterGroup {
                new DiskWriterLimiter(DiskManager),
                downloadLimiter,
            };

            uploadLimiter  = new RateLimiter();
            uploadLimiters = new RateLimiterGroup {
                uploadLimiter
            };

            Listener = PeerListenerFactory.CreateTcp(settings.ListenPort);
            listenManager.SetListener(Listener);

            DhtListener             = DhtListenerFactory.CreateUdp(settings.DhtPort);
            DhtEngine               = settings.DhtPort == -1 ? new NullDhtEngine() : DhtEngineFactory.Create(DhtListener);
            DhtEngine.StateChanged += DhtEngineStateChanged;
            DhtEngine.PeersFound   += DhtEnginePeersFound;

            RegisterLocalPeerDiscovery(settings.AllowLocalPeerDiscovery && settings.ListenPort > 0 ? LocalPeerDiscoveryFactory.Create(settings.ListenPort) : null);
        }
Beispiel #12
0
        internal DiskManager(EngineSettings settings, IPieceWriter writer)
        {
            ReadLimiter = new RateLimiter();
            ReadMonitor = new SpeedMonitor();
            ReadQueue   = new Queue <BufferedIO> ();

            WriteLimiter = new RateLimiter();
            WriteMonitor = new SpeedMonitor();
            WriteQueue   = new Queue <BufferedIO> ();

            UpdateTimer = ValueStopwatch.StartNew();

            Settings = settings ?? throw new ArgumentNullException(nameof(settings));
            Writer   = writer ?? throw new ArgumentNullException(nameof(writer));
        }
Beispiel #13
0
        internal DiskManager(EngineSettings settings, IPieceWriter writer = null)
        {
            ReadLimiter = new RateLimiter();
            ReadQueue   = new Queue <BufferedIO> ();

            WriteLimiter = new RateLimiter();
            WriteQueue   = new Queue <BufferedIO> ();

            UpdateTimer = ValueStopwatch.StartNew();

            Settings = settings ?? throw new ArgumentNullException(nameof(settings));

            writer ??= new DiskWriter(settings.MaximumOpenFiles);
            Cache = new MemoryCache(settings.DiskCacheBytes, writer);
        }
Beispiel #14
0
        TestRig(string savePath, int piecelength, IPieceWriter writer, string[][] trackers, TorrentFile[] files, bool metadataMode)
        {
            this.files                               = files;
            this.savePath                            = savePath;
            this.piecelength                         = piecelength;
            this.tier                                = trackers;
            MetadataMode                             = metadataMode;
            MetadataPath                             = "metadataSave.torrent";
            PeerListenerFactory.Creator              = endpoint => new CustomListener();
            LocalPeerDiscoveryFactory.Creator        = port => new ManualLocalPeerListener();
            Dht.Listeners.DhtListenerFactory.Creator = endpoint => new Dht.Listeners.NullDhtListener();
            Engine = new ClientEngine(new EngineSettingsBuilder {
                ListenPort = 12345
            }.ToSettings());
            Engine.DiskManager.ChangePieceWriter(writer);
            Writer = writer;

            RecreateManager().Wait();
        }
Beispiel #15
0
        internal DiskManager(EngineSettings settings, Factories factories, IPieceWriter writer = null)
        {
            ReadLimiter = new RateLimiter();
            ReadQueue   = new Queue <BufferedIO> ();

            WriteLimiter = new RateLimiter();
            WriteQueue   = new Queue <BufferedIO> ();

            UpdateTimer = ValueStopwatch.StartNew();

            Factories = factories ?? throw new ArgumentNullException(nameof(factories));
            Settings  = settings ?? throw new ArgumentNullException(nameof(settings));

            writer ??= factories.CreatePieceWriter(settings.MaximumOpenFiles);
            Cache = factories.CreateBlockCache(writer, settings.DiskCacheBytes, BufferPool);
            Cache.ReadThroughCache    += (o, e) => WriterReadMonitor.AddDelta(e.RequestLength);
            Cache.WrittenThroughCache += (o, e) => WriterWriteMonitor.AddDelta(e.RequestLength);
            IncrementalHashCache       = new Cache <IncrementalHashData> (() => new IncrementalHashData(IncrementalHash.CreateHash(HashAlgorithmName.SHA1)));
        }
Beispiel #16
0
        public ClientEngine(EngineSettings settings, IPeerListener listener, IPieceWriter writer)
        {
            Check.Settings(settings);
            Check.Listener(listener);
            Check.Writer(writer);

            PeerId   = GeneratePeerId();
            Listener = listener ?? throw new ArgumentNullException(nameof(listener));
            Settings = settings ?? throw new ArgumentNullException(nameof(settings));

            torrents = new List <TorrentManager>();
            Torrents = new ReadOnlyCollection <TorrentManager> (torrents);

            DiskManager       = new DiskManager(Settings, writer);
            ConnectionManager = new ConnectionManager(PeerId, Settings, DiskManager);
            DhtEngine         = new NullDhtEngine();
            listenManager     = new ListenManager(this);
            MainLoop.QueueTimeout(TimeSpan.FromMilliseconds(TickLength), delegate {
                if (IsRunning && !Disposed)
                {
                    LogicTick();
                }
                return(!Disposed);
            });

            downloadLimiter  = new RateLimiter();
            downloadLimiters = new RateLimiterGroup {
                new DiskWriterLimiter(DiskManager),
                downloadLimiter,
            };

            uploadLimiter  = new RateLimiter();
            uploadLimiters = new RateLimiterGroup {
                uploadLimiter
            };

            listenManager.Register(listener);

            if (SupportsLocalPeerDiscovery)
            {
                RegisterLocalPeerDiscovery(new LocalPeerDiscovery(Settings));
            }
        }
Beispiel #17
0
        public static async ReusableTask <int> ReadFromFilesAsync(this IPieceWriter writer, ITorrentData manager, BlockInfo request, byte[] buffer)
        {
            var count  = request.RequestLength;
            var offset = request.ToByteOffset(manager.PieceLength);

            if (count < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(count), $"Count must be greater than zero, but was {count}.");
            }

            if (offset < 0 || offset + count > manager.Size)
            {
                throw new ArgumentOutOfRangeException(nameof(offset));
            }

            int totalRead = 0;
            var files     = manager.Files;
            int i         = manager.Files.FindFileByOffset(offset);

            offset -= files[i].OffsetInTorrent;

            while (totalRead < count)
            {
                int fileToRead = (int)Math.Min(files[i].Length - offset, count - totalRead);
                fileToRead = Math.Min(fileToRead, Piece.BlockSize);

                if (fileToRead != await writer.ReadAsync(files[i], offset, buffer, totalRead, fileToRead))
                {
                    return(totalRead);
                }

                offset    += fileToRead;
                totalRead += fileToRead;
                if (offset >= files[i].Length)
                {
                    offset = 0;
                    i++;
                }
            }

            return(totalRead);
        }
 public MonotorrentDataAccessor(IPieceWriter writer)
 {
     _writer = writer;
 }
Beispiel #19
0
 internal static TorrentManager CreateMultiFileManager(TorrentFile[] files, int pieceLength, IPieceWriter writer = null)
 {
     return(CreateMultiFile(files, pieceLength, writer: writer).Manager);
 }
Beispiel #20
0
        internal static TorrentManager CreateMultiFileManager(long[] fileSizes, int pieceLength, IPieceWriter writer = null)
        {
            var files = TorrentFile.Create(pieceLength, fileSizes).ToArray();

            return(CreateMultiFileManager(files, pieceLength, writer));
        }
Beispiel #21
0
        async Task <byte[]> CalcPiecesHash(int startPiece, long totalBytesToRead, Synchronizer synchronizer, List <TorrentFile> files, CancellationToken token)
        {
            // One buffer will be filled and will be passed to the hashing method.
            // One buffer will be filled and will be waiting to be hashed.
            // One buffer will be empty and will be filled from the disk.
            // Aaaannd one extra buffer for good luck!
            var emptyBuffers = new AsyncProducerConsumerQueue <byte[]> (4);

            // Make this buffer one element larger so it can fit the placeholder which indicates a file has been completely read.
            var filledBuffers = new AsyncProducerConsumerQueue <(byte[], int, TorrentFile)> (emptyBuffers.Capacity + 1);

            // This is the IPieceWriter which we'll use to get our filestream. Each thread gets it's own writer.
            using IPieceWriter writer = CreateReader();

            // Read from the disk in 256kB chunks, instead of 16kB, as a performance optimisation.
            // As the capacity is set to 4, this means we'll have 1 megabyte of buffers to handle.
            for (int i = 0; i < emptyBuffers.Capacity; i++)
            {
                await emptyBuffers.EnqueueAsync(new byte[256 * 1024], token);
            }
            token.ThrowIfCancellationRequested();

            using CancellationTokenRegistration cancellation = token.Register(() => {
                emptyBuffers.CompleteAdding();
                filledBuffers.CompleteAdding();
            });

            // We're going to do single-threaded reading from disk, which (unfortunately) means we're (more or less) restricted
            // to single threaded hashing too as it's unlikely we'll have sufficient data in our buffers to do any better.
            Task readAllTask = ReadAllDataAsync(startPiece * PieceLength, totalBytesToRead, synchronizer, files, writer, emptyBuffers, filledBuffers, token);

            Task <byte[]> hashAllTask = HashAllDataAsync(totalBytesToRead, emptyBuffers, filledBuffers, token);

            Task firstCompleted = null;

            try {
                // We first call 'WhenAny' so that if an exception is thrown in one of the tasks, execution will continue
                // and we can kill the producer/consumer queues.
                firstCompleted = await Task.WhenAny(readAllTask, hashAllTask);

                // If the first completed task has faulted, force the exception to be thrown.
                await firstCompleted;
            } catch {
                // We got an exception from the first or second task, so bail out now!
                emptyBuffers.CompleteAdding();
                filledBuffers.CompleteAdding();
            }

            try {
                // If there is no exception from the first completed task, just wait for the second one.
                await Task.WhenAll(readAllTask, hashAllTask);
            } catch {
                token.ThrowIfCancellationRequested();
                if (firstCompleted != null)
                {
                    await firstCompleted;
                }
                throw;
            }
            return(await hashAllTask);
        }
Beispiel #22
0
 TestRig(string savePath, int piecelength, IPieceWriter writer, string[][] trackers, TorrentFile[] files)
     : this(savePath, piecelength, writer, trackers, files, false)
 {
 }
Beispiel #23
0
        void CreateMultiFileTorrent(BEncodedDictionary dictionary, List <TorrentFile> mappings, IPieceWriter writer, string name)
        {
            BEncodedDictionary   info  = (BEncodedDictionary)dictionary ["info"];
            List <BEncodedValue> files = mappings.ConvertAll <BEncodedValue> (ToFileInfoDict);

            info.Add("files", new BEncodedList(files));
        }
Beispiel #24
0
 public MemoryWriter(IPieceWriter writer)
     : this(writer, 2 * 1024 * 1024)
 {
 }
Beispiel #25
0
 async Task ReadAllDataAsync(long startOffset, long totalBytesToRead, Synchronizer synchronizer, List <TorrentFile> files, IPieceWriter writer, AsyncProducerConsumerQueue <byte[]> emptyBuffers, AsyncProducerConsumerQueue <(byte[], int, TorrentFile)> filledBuffers, CancellationToken token)
Beispiel #26
0
        public ClientEngine(EngineSettings settings, IPieceWriter writer)
            : this(settings, new PeerListener(new IPEndPoint(IPAddress.Any, settings.ListenPort)), writer)

        {
        }
Beispiel #27
0
 internal static TestRig CreateMultiFile(TorrentFile[] files, int pieceLength, IPieceWriter writer = null)
 {
     return(new TestRig("", pieceLength, writer ?? StandardWriter(), StandardTrackers(), files));
 }
Beispiel #28
0
        void CreateSingleFileTorrent(BEncodedDictionary dictionary, List <TorrentFile> mappings, IPieceWriter writer, string name)
        {
            BEncodedDictionary infoDict = (BEncodedDictionary)dictionary ["info"];

            infoDict.Add("length", new BEncodedNumber(mappings [0].Length));
            if (mappings [0].MD5 != null)
            {
                infoDict ["md5sum"] = (BEncodedString)mappings [0].MD5;
            }
        }
Beispiel #29
0
 public ReusableTask SetWriterAsync(IPieceWriter writer)
 {
     Writer = writer;
     return(ReusableTask.CompletedTask);
 }
 internal async ReusableTask SetWriterAsync(IPieceWriter writer)
 => await Cache.SetWriterAsync(writer);