forked from kapetan/dns
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implemented a parallel request resolver.
Working towards resolving kapetan#39
- Loading branch information
1 parent
27cc83a
commit 9949214
Showing
2 changed files
with
98 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using DNS.Protocol; | ||
using DNS.Protocol.Utils; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
|
||
namespace DNS.Client.RequestResolver | ||
{ | ||
/// <summary> | ||
/// Resolve requests using multiple IRequestResolvers, taking the first result. | ||
/// </summary> | ||
public class ParallelRequestResolver : IRequestResolver | ||
{ | ||
private List<IRequestResolver> resolvers; | ||
/// <summary> | ||
/// Create a new instance of ParallelRequestResolver | ||
/// </summary> | ||
/// <param name="innerResolvers"></param> | ||
/// <exception cref="System.ArgumentException">Thrown when <paramref name="innerResolvers">innerResolvers</paramref> does not contain at least 1 resolver.</exception> | ||
public ParallelRequestResolver(IEnumerable<IRequestResolver> innerResolvers) | ||
{ | ||
resolvers = innerResolvers.ToList(); | ||
if (resolvers.Count == 0) throw new ArgumentException("No inner DNS resolvers were provided!", nameof(innerResolvers)); | ||
} | ||
|
||
public async Task<IResponse> Resolve(IRequest request, CancellationToken cancellationToken = default) | ||
{ | ||
CancellationTokenSource requestCompletedCancellationSource = new CancellationTokenSource(); | ||
var linkedSource = CancellationTokenSource.CreateLinkedTokenSource(requestCompletedCancellationSource.Token, cancellationToken); | ||
List<Exception> exceptions = new List<Exception>(); | ||
var tasks = resolvers.Select(i => i.Resolve(request, linkedSource.Token)).ToList(); | ||
bool done = false; | ||
IResponse response = null; | ||
while (response == null) | ||
{ | ||
if (tasks.Count == 0) | ||
break; | ||
var completedTask = await Task.WhenAny(tasks).ConfigureAwait(false); | ||
try | ||
{ | ||
// We could check the task manually, but this way will handle edge cases. | ||
response = await completedTask.ConfigureAwait(false); | ||
} | ||
catch (Exception ex) | ||
{ | ||
exceptions.Add(ex); | ||
} | ||
tasks.Remove(completedTask); | ||
} | ||
|
||
if (tasks.Any()) | ||
{ | ||
tasks = tasks.Select(i => i.SwallowExceptions(null)).ToList(); | ||
} | ||
|
||
requestCompletedCancellationSource.Cancel(); | ||
if (response == null) | ||
{ | ||
throw new AggregateException(exceptions); | ||
} | ||
|
||
// Should response be wrapped with something that exposes exceptions? | ||
// IE: public class ResponseWithExceptions : IResponse | ||
// new ResponseWithExceptions(response, exceptions) | ||
return response; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters