Skip to content

Commit

Permalink
[core] Use an immutable approach to overriding core functionality
Browse files Browse the repository at this point in the history
Require the user of the library to create the 'Factories' object
before initializing the engine, and ensure 'Factories' is immutable.

If someone wants to enable/disable something core, like proxy support,
then it's far easier to do that safely/reliably if they also instantiate
a new ClientEngine object.

These are things which should not change for the lifetime of a torrent,
unlike the more mutable EngineSettings or ClientSettings.
  • Loading branch information
alanmcgovern committed Oct 30, 2021
1 parent 1858b63 commit 3e93877
Show file tree
Hide file tree
Showing 35 changed files with 265 additions and 517 deletions.
1 change: 1 addition & 0 deletions src/DotNetCoreSample/DotNetCoreSample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<ItemGroup>
<ProjectReference Include="..\MonoTorrent.BEncoding\MonoTorrent.BEncoding.csproj" />
<ProjectReference Include="..\MonoTorrent.Client\MonoTorrent.Client.csproj" />
<ProjectReference Include="..\MonoTorrent.Factories\MonoTorrent.Factories.csproj" />
<ProjectReference Include="..\MonoTorrent.PieceWriter\MonoTorrent.PieceWriter.csproj" />
<ProjectReference Include="..\MonoTorrent\MonoTorrent.csproj" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ public HttpRequestData (RequestMessage request)

public bool Disposed { get; set; }

Factories.HttpRequestCreator RequestCreator { get; }

IHttpRequest Requester { get; set; }

public TimeSpan ConnectionTimeout {
Expand Down Expand Up @@ -103,9 +105,10 @@ public TimeSpan ConnectionTimeout {

#region Constructors

public HttpPeerConnection (ITorrentData torrentData, Uri uri)
public HttpPeerConnection (ITorrentData torrentData, Factories.HttpRequestCreator requestCreator, Uri uri)
{
ConnectionTimeout = TimeSpan.FromSeconds (10);
RequestCreator = requestCreator;
TorrentData = torrentData ?? throw new ArgumentNullException (nameof (torrentData));
Uri = uri;
}
Expand Down Expand Up @@ -212,7 +215,7 @@ public async ReusableTask<int> ReceiveAsync (ByteBuffer buffer, int offset, int
var rr = WebRequests.Dequeue ();

Requester?.Dispose ();
Requester = HttpRequestFactory.Create ();
Requester = RequestCreator ();
Requester.ConnectionTimeout = ConnectionTimeout;
DataStream = await Requester.GetStreamAsync (rr.fileUri, rr.startOffset, rr.count);
DataStreamCount = rr.count;
Expand Down
28 changes: 19 additions & 9 deletions src/MonoTorrent.Client/MonoTorrent.Client/ClientEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -202,6 +203,8 @@ public async Task SaveStateAsync (string pathToStateFile)

public bool Disposed { get; private set; }

internal Factories Factories { get; }

internal IPeerConnectionListener Listener { get; set; }

internal ILocalPeerDiscovery LocalPeerDiscovery { get; private set; }
Expand Down Expand Up @@ -252,8 +255,15 @@ public ClientEngine ()
}

public ClientEngine (EngineSettings settings)
: this (settings, Factories.Default)
{

}

public ClientEngine (EngineSettings settings, Factories factories)
{
settings = settings ?? throw new ArgumentNullException (nameof (settings));
Factories = factories ?? throw new ArgumentNullException (nameof (factories));

// This is just a sanity check to make sure the ReusableTasks.dll assembly is
// loadable.
Expand All @@ -267,9 +277,9 @@ public ClientEngine (EngineSettings settings)
publicTorrents = new List<TorrentManager> ();
Torrents = new ReadOnlyCollection<TorrentManager> (publicTorrents);

DiskManager = new DiskManager (Settings);
DiskManager = new DiskManager (Settings, Factories);

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

Expand All @@ -290,15 +300,15 @@ public ClientEngine (EngineSettings settings)
uploadLimiter
};

Listener = PeerListenerFactory.CreateTcp (settings.ListenPort) ?? new NullPeerListener ();
Listener = (settings.ListenPort == -1 ? null : Factories.CreatePeerConnectionListener (new IPEndPoint (IPAddress.Any, settings.ListenPort))) ?? new NullPeerListener ();
listenManager.SetListener (Listener);

DhtListener = DhtListenerFactory.CreateUdp (settings.DhtPort) ?? new NullDhtListener ();
DhtListener = (settings.DhtPort == -1 ? null : Factories.CreateDhtListener (new IPEndPoint (IPAddress.Any, settings.DhtPort))) ?? new NullDhtListener ();
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);
RegisterLocalPeerDiscovery (settings.AllowLocalPeerDiscovery && settings.ListenPort >= 0 ? Factories.CreateLocalPeerDiscovery (settings.ListenPort) : null);
}

#endregion
Expand Down Expand Up @@ -400,7 +410,7 @@ public async Task<TorrentManager> AddStreamingAsync (Torrent torrent, string sav

async Task<TorrentManager> MakeStreamingAsync (TorrentManager manager)
{
await manager.ChangePickerAsync (PieceRequesterFactory.CreateStreamingPieceRequester (manager));
await manager.ChangePickerAsync (Factories.CreateStreamingPieceRequester (manager));
return manager;
}

Expand Down Expand Up @@ -879,7 +889,7 @@ async Task UpdateSettingsAsync (EngineSettings oldSettings, EngineSettings newSe
else if (oldSettings.DhtPort > 0)
await PortForwarder.UnregisterMappingAsync (new Mapping (Protocol.Udp, oldSettings.DhtPort), CancellationToken.None);

DhtListener = DhtListenerFactory.CreateUdp (newSettings.DhtPort) ?? new NullDhtListener ();
DhtListener = (newSettings.DhtPort == -1 ? null : Factories.CreateDhtListener (new IPEndPoint (IPAddress.Any, newSettings.DhtPort))) ?? new NullDhtListener ();
if (oldSettings.DhtPort == -1)
await RegisterDht (DhtEngineFactory.Create (DhtListener));
else if (newSettings.DhtPort == -1)
Expand All @@ -903,7 +913,7 @@ async Task UpdateSettingsAsync (EngineSettings oldSettings, EngineSettings newSe
await PortForwarder.UnregisterMappingAsync (new Mapping (Protocol.Tcp, oldSettings.ListenPort), CancellationToken.None);

Listener.Stop ();
Listener = PeerListenerFactory.CreateTcp (newSettings.ListenPort) ?? new NullPeerListener ();
Listener = (newSettings.ListenPort == -1 ? null : Factories.CreatePeerConnectionListener (new IPEndPoint (IPAddress.Any, newSettings.ListenPort))) ?? new NullPeerListener ();
listenManager.SetListener (Listener);

if (IsRunning) {
Expand All @@ -923,7 +933,7 @@ async Task UpdateSettingsAsync (EngineSettings oldSettings, EngineSettings newSe

if ((oldSettings.AllowLocalPeerDiscovery != newSettings.AllowLocalPeerDiscovery) ||
(oldSettings.ListenPort != newSettings.ListenPort)) {
RegisterLocalPeerDiscovery (newSettings.AllowLocalPeerDiscovery && localPort > 0 ? LocalPeerDiscoveryFactory.Create (localPort) : null);
RegisterLocalPeerDiscovery (!newSettings.AllowLocalPeerDiscovery || localPort <= 0 ? null : Factories.CreateLocalPeerDiscovery(localPort));
}
}

Expand Down
20 changes: 20 additions & 0 deletions src/MonoTorrent.Client/MonoTorrent.Client/FactoriesExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;

using MonoTorrent.Client.Connections;

namespace MonoTorrent.Client
{
static class FactoriesExtensions
{
public static IPeerConnection CreatePeerConnection(this Factories factories, Uri uri)
{
try {
if (factories.PeerConnectionCreators.TryGetValue (uri.Scheme, out var creator))
return creator (uri);
} catch {

}
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ public AsyncConnectState (TorrentManager manager, IPeerConnection connection, Va

internal DiskManager DiskManager { get; }

Factories Factories { get; }

internal BEncodedString LocalPeerId { get; }

/// <summary>
Expand All @@ -97,14 +99,14 @@ public AsyncConnectState (TorrentManager manager, IPeerConnection connection, Va
List<AsyncConnectState> PendingConnects { get; }

internal EngineSettings Settings { get; set; }

internal List<TorrentManager> Torrents { get; set; }

internal ConnectionManager (BEncodedString localPeerId, EngineSettings settings, DiskManager diskManager)
internal ConnectionManager (BEncodedString localPeerId, EngineSettings settings, Factories factories, DiskManager diskManager)
{
DiskManager = diskManager ?? throw new ArgumentNullException (nameof (diskManager));
LocalPeerId = localPeerId ?? throw new ArgumentNullException (nameof (localPeerId));
Settings = settings ?? throw new ArgumentNullException (nameof (settings));
Factories = factories ?? throw new ArgumentNullException (nameof (factories));

PendingConnects = new List<AsyncConnectState> ();
Torrents = new List<TorrentManager> ();
Expand All @@ -123,7 +125,7 @@ internal void Remove (TorrentManager manager)
async void ConnectToPeer (TorrentManager manager, Peer peer)
{
// Connect to the peer.
var connection = PeerConnectionFactory.Create (peer.ConnectionUri);
var connection = Factories.CreatePeerConnection (peer.ConnectionUri);
if (connection == null || peer.AllowedEncryption.Count == 0)
return;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ public BufferedIO (ITorrentData manager, BlockInfo request, byte[] buffer, bool
/// </summary>
IBlockCache Cache { get; }

internal DiskManager (EngineSettings settings, IPieceWriter writer = null)
internal DiskManager (EngineSettings settings, Factories factories, IPieceWriter writer = null)
{
ReadLimiter = new RateLimiter ();
ReadQueue = new Queue<BufferedIO> ();
Expand All @@ -186,8 +186,8 @@ internal DiskManager (EngineSettings settings, IPieceWriter writer = null)

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

writer ??= PieceWriterFactory.Create (settings.MaximumOpenFiles);
Cache = BlockCacheFactory.Create (writer, settings.DiskCacheBytes, BufferPool);
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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ internal PieceManager (TorrentManager manager)
{
Manager = manager;
PendingHashCheckPieces = new MutableBitField (1);
Requester = PieceRequesterFactory.CreateStandardPieceRequester (manager);
Requester = manager.Engine.Factories.CreatePieceRequester (manager);
}

internal bool PieceDataReceived (PeerId id, PieceMessage message, out bool pieceComplete, out IList<IPeer> peersInvolved)
Expand Down
2 changes: 1 addition & 1 deletion src/MonoTorrent.Client/MonoTorrent.Client/Modes/Mode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ void DownloadLogic (int counter)

var peer = new Peer (peerId, uri);

var connection = new HttpPeerConnection (Manager, uri);
var connection = new HttpPeerConnection (Manager, Manager.Engine.Factories.CreateHttpRequest, uri);
// Unsupported connection type.
if (connection == null)
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,4 +346,4 @@ static int CheckZeroOrPositive (int value)
return value;
}
}
}
}
9 changes: 0 additions & 9 deletions src/MonoTorrent.Client/MonoTorrent.Dht/DhtEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,6 @@ public class DhtEngine : IDisposable, IDhtEngine

#region Constructors

/// <summary>
/// Creates a new DhtEngine which listens for connections on the given endpoint
/// </summary>
/// <param name="listenerEndpoint">The IPAddresss/port which the engine should listen on</param>
public DhtEngine (IPEndPoint listenerEndpoint)
: this (DhtListenerFactory.CreateUdp (listenerEndpoint))
{
}

public DhtEngine (IDhtListener listener)
{
if (listener == null)
Expand Down
12 changes: 5 additions & 7 deletions src/MonoTorrent.Client/MonoTorrent/TorrentCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,14 @@ public static int RecommendedPieceSize (IEnumerable<FileMapping> files)

internal TimeSpan CreationTime { get; set; }

public TorrentCreator ()
Factories.PieceWriterCreator PieceWriterCreator { get; }

public TorrentCreator (Factories.PieceWriterCreator pieceWriterCreator)
{
GetrightHttpSeeds = new List<string> ();
CanEditSecureMetadata = true;
CreatedBy = $"MonoTorrent {VersionInfo.Version}";
PieceWriterCreator = pieceWriterCreator;
}

public BEncodedDictionary Create (ITorrentFileSource fileSource)
Expand Down Expand Up @@ -302,7 +305,7 @@ async Task<byte[]> CalcPiecesHash (int startPiece, long totalBytesToRead, Synchr
var filledBuffers = new AsyncProducerConsumerQueue<(byte[], int, InputFile)> (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 ();
using IPieceWriter writer = PieceWriterCreator (3);

// 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.
Expand Down Expand Up @@ -480,11 +483,6 @@ void CreateMultiFileTorrent (BEncodedDictionary dictionary, List<InputFile> mapp
info.Add ("files", new BEncodedList (files));
}

protected virtual IPieceWriter CreateReader ()
{
return PieceWriterFactory.Create (10);
}

void CreateSingleFileTorrent (BEncodedDictionary dictionary, IList<InputFile> mappings)
{
var infoDict = (BEncodedDictionary) dictionary["info"];
Expand Down
51 changes: 0 additions & 51 deletions src/MonoTorrent.Factories/BlockCacheFactory.cs

This file was deleted.

Loading

0 comments on commit 3e93877

Please sign in to comment.