Skip to content

Commit 9e98a44

Browse files
committed
Handle failures of individual dht routers
If a single dht router is offline/unavailable, we should still be able to successfully resolve the remainder and bootstrap. Additionally, tweak how initialisation is handled. The list of initial nodes and the DHT bootstrap nodes must be provided at the start. If both are provided then the initial nodes will be used for bootstrapping prior to attempting to resolve/use the DHT bootstrap nodes. If an empty list of bootstrap nodes is provided, then no lookup will be performed and only the 'initial nodes' will be used for bootstrapping. If an empty list of initial nodes and an empty list of bootstrap nodes are provided, then the engine will fail to initialise.
1 parent 24877e7 commit 9e98a44

File tree

4 files changed

+29
-22
lines changed

4 files changed

+29
-22
lines changed

src/MonoTorrent.Connections/MonoTorrent.Connections/SocketConnector.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,6 @@ static SocketAsyncEventArgs GetSocketAsyncEventArgs ()
5959

6060
static void HandleOperationCompleted (object? sender, SocketAsyncEventArgs e)
6161
{
62-
// Don't retain the TCS forever. Note we do not want to null out the byte[] buffer
63-
// as we *do* want to retain that so that we can avoid the expensive SetBuffer calls.
6462
var tcs = (ReusableTaskCompletionSource<int>) e.UserToken!;
6563
SocketError error = e.SocketError;
6664

src/MonoTorrent.Dht/MonoTorrent.Dht.Tasks/InitialiseTask.cs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,6 @@ namespace MonoTorrent.Dht.Tasks
3838
{
3939
class InitialiseTask
4040
{
41-
static readonly string[] DefaultBootstrapRouters = new[] {
42-
"router.bittorrent.com",
43-
"router.utorrent.com",
44-
"dht.transmissionbt.com"
45-
};
46-
4741
// Choose a completely arbitrary value here. If we have at least this many
4842
// nodes in the routing table we can consider it 'healthy' enough to allow
4943
// the state to change to 'Ready' so torrents can begin searching for peers
@@ -57,7 +51,7 @@ class InitialiseTask
5751
string[] BootstrapRouters { get; set; }
5852

5953
public InitialiseTask (DhtEngine engine)
60-
: this (engine, Enumerable.Empty<Node> (), DefaultBootstrapRouters)
54+
: this (engine, Enumerable.Empty<Node> (), DhtEngine.DefaultBootstrapRouters.ToArray ())
6155
{
6256

6357
}
@@ -66,7 +60,7 @@ public InitialiseTask (DhtEngine engine, IEnumerable<Node> nodes, string[] boots
6660
{
6761
this.engine = engine;
6862
initialNodes = nodes.ToList ();
69-
BootstrapRouters = bootstrapRouters.Length == 0 ? DefaultBootstrapRouters : bootstrapRouters.ToArray ();
63+
BootstrapRouters = bootstrapRouters;
7064
initializationComplete = new TaskCompletionSource<object?> ();
7165
}
7266

@@ -100,13 +94,24 @@ async void BeginAsyncInit ()
10094

10195
async Task<Node[]> GenerateBootstrapNodes ()
10296
{
103-
var addresses = await Task.WhenAll (BootstrapRouters.Select (Dns.GetHostAddressesAsync));
104-
return addresses
105-
.SelectMany (t => t)
106-
.Distinct ()
107-
.Select (t => new IPEndPoint (t, 6881))
108-
.Select (t => new Node (NodeId.Create (), t))
109-
.ToArray ();
97+
// Handle when one, or more, of the bootstrap nodes are offline
98+
var results = new List<Node> ();
99+
100+
var tasks = BootstrapRouters.Select (Dns.GetHostAddressesAsync).ToList ();
101+
while (tasks.Count > 0) {
102+
var completed = await Task.WhenAny (tasks);
103+
tasks.Remove (completed);
104+
105+
try {
106+
var addresses = await completed;
107+
foreach (var v in addresses)
108+
results.Add (new Node (NodeId.Create (), new IPEndPoint (v, 6881)));
109+
} catch {
110+
111+
}
112+
}
113+
114+
return results.ToArray ();
110115
}
111116

112117
async Task SendFindNode (IEnumerable<Node> newNodes)
@@ -139,7 +144,7 @@ async Task SendFindNode (IEnumerable<Node> newNodes)
139144
}
140145
}
141146

142-
if (initialNodes.Count > 0 && engine.RoutingTable.NeedsBootstrap)
147+
if (initialNodes.Count > 0 && engine.RoutingTable.NeedsBootstrap && BootstrapRouters.Length > 0)
143148
await new InitialiseTask (engine).ExecuteAsync ();
144149
}
145150
}

src/MonoTorrent.Dht/MonoTorrent.Dht/DhtEngine.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ class TransferMonitor : ITransferMonitor
6363

6464
public class DhtEngine : IDisposable, IDhtEngine
6565
{
66+
internal static readonly IList<string> DefaultBootstrapRouters = Array.AsReadOnly (new[] {
67+
"router.bittorrent.com",
68+
"router.utorrent.com",
69+
"dht.transmissionbt.com"
70+
});
71+
6672
static readonly TimeSpan DefaultAnnounceInternal = TimeSpan.FromMinutes (10);
6773
static readonly TimeSpan DefaultMinimumAnnounceInterval = TimeSpan.FromMinutes (3);
6874

@@ -272,7 +278,7 @@ public Task StartAsync ()
272278
=> StartAsync (ReadOnlyMemory<byte>.Empty);
273279

274280
public Task StartAsync (ReadOnlyMemory<byte> initialNodes)
275-
=> StartAsync (Node.FromCompactNode (BEncodedString.FromMemory (initialNodes)).Concat (PendingNodes), Array.Empty<string> ());
281+
=> StartAsync (Node.FromCompactNode (BEncodedString.FromMemory (initialNodes)).Concat (PendingNodes), DefaultBootstrapRouters.ToArray ());
276282

277283
public Task StartAsync (params string[] bootstrapRouters)
278284
=> StartAsync (Array.Empty<Node> (), bootstrapRouters);

src/Tests/Tests.MonoTorrent.Dht/MonoTorrent.Dht/TaskTests.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,10 @@ public async Task Setup ()
6262
[Repeat (10)]
6363
public async Task InitialiseFailure ()
6464
{
65-
// FIXME: this flakily fails sometimes... why? DNS timeouts?
66-
// Block until we've checked the status moved to Initializing
6765
var errorSource = new TaskCompletionSource<object> ();
6866
listener.MessageSent += (o, e) => errorSource.Task.GetAwaiter ().GetResult ();
6967

70-
await engine.StartAsync (new byte[26]);
68+
await engine.StartAsync (new byte[26], Array.Empty<string> ());
7169
Assert.AreEqual (DhtState.Initialising, engine.State);
7270

7371
// Then set an error and make sure the engine state moves to 'NotReady'

0 commit comments

Comments
 (0)