using FastGithub.Configuration; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net; using System.Net.Sockets; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; namespace FastGithub.DomainResolve { /// /// 域名解析器 /// sealed class DomainResolver : IDomainResolver { private readonly DnsClient dnsClient; private readonly ConcurrentDictionary dnsEndPointAddressTestResult = new(); /// /// 域名解析器 /// /// public DomainResolver(DnsClient dnsClient) { this.dnsClient = dnsClient; } /// /// 预加载 /// /// 域名 public void Prefetch(string domain) { var endPoint = new DnsEndPoint(domain, 443); this.dnsEndPointAddressTestResult.TryAdd(endPoint, IPAddressTestResult.Empty); } /// /// 对所有节点进行测速 /// /// /// public async Task TestAllEndPointsAsync(CancellationToken cancellationToken) { foreach (var keyValue in this.dnsEndPointAddressTestResult) { if (keyValue.Value.IsEmpty || keyValue.Value.IsExpired) { var dnsEndPoint = keyValue.Key; var addresses = new List(); await foreach (var adddress in this.dnsClient.ResolveAsync(dnsEndPoint.Host, cancellationToken)) { addresses.Add(adddress); } var addressTestResult = IPAddressTestResult.Empty; if (addresses.Count == 1) { var addressElapseds = new[] { new IPAddressElapsed(addresses[0], TimeSpan.Zero) }; addressTestResult = new IPAddressTestResult(addressElapseds); } else if (addresses.Count > 1) { var tasks = addresses.Select(item => GetIPAddressElapsedAsync(item, dnsEndPoint.Port, cancellationToken)); var addressElapseds = await Task.WhenAll(tasks); addressTestResult = new IPAddressTestResult(addressElapseds); } this.dnsEndPointAddressTestResult[dnsEndPoint] = addressTestResult; } } } /// /// 获取连接耗时 /// /// /// /// /// private static async Task GetIPAddressElapsedAsync(IPAddress address, int port, CancellationToken cancellationToken) { var stopWatch = Stopwatch.StartNew(); try { using var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(10d)); using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token); using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); await socket.ConnectAsync(address, port, linkedTokenSource.Token); return new IPAddressElapsed(address, stopWatch.Elapsed); } catch (Exception) { cancellationToken.ThrowIfCancellationRequested(); return new IPAddressElapsed(address, TimeSpan.MaxValue); } finally { stopWatch.Stop(); } } /// /// 解析ip /// /// 节点 /// /// public async Task ResolveAnyAsync(DnsEndPoint endPoint, CancellationToken cancellationToken = default) { await foreach (var address in this.ResolveAllAsync(endPoint, cancellationToken)) { return address; } throw new FastGithubException($"解析不到{endPoint.Host}的IP"); } /// /// 解析域名 /// /// 节点 /// /// public async IAsyncEnumerable ResolveAllAsync(DnsEndPoint endPoint, [EnumeratorCancellation] CancellationToken cancellationToken) { if (this.dnsEndPointAddressTestResult.TryGetValue(endPoint, out var speedTestResult) && speedTestResult.IsEmpty == false) { foreach (var addressElapsed in speedTestResult.AddressElapseds) { yield return addressElapsed.Adddress; } } else { this.dnsEndPointAddressTestResult.TryAdd(endPoint, IPAddressTestResult.Empty); await foreach (var adddress in this.dnsClient.ResolveAsync(endPoint.Host, cancellationToken)) { yield return adddress; } } } } }