DomainResolver.cs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. using FastGithub.Configuration;
  2. using System;
  3. using System.Collections.Concurrent;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Runtime.CompilerServices;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. namespace FastGithub.DomainResolve
  11. {
  12. /// <summary>
  13. /// 域名解析器
  14. /// </summary>
  15. sealed class DomainResolver : IDomainResolver
  16. {
  17. private readonly DnsClient dnsClient;
  18. private readonly ConcurrentDictionary<DnsEndPoint, IPAddressElapsedCollection> dnsEndPointAddressElapseds = new();
  19. /// <summary>
  20. /// 域名解析器
  21. /// </summary>
  22. /// <param name="dnsClient"></param>
  23. public DomainResolver(DnsClient dnsClient)
  24. {
  25. this.dnsClient = dnsClient;
  26. }
  27. /// <summary>
  28. /// 解析ip
  29. /// </summary>
  30. /// <param name="endPoint">节点</param>
  31. /// <param name="cancellationToken"></param>
  32. /// <returns></returns>
  33. public async Task<IPAddress> ResolveAnyAsync(DnsEndPoint endPoint, CancellationToken cancellationToken = default)
  34. {
  35. await foreach (var address in this.ResolveAllAsync(endPoint, cancellationToken))
  36. {
  37. return address;
  38. }
  39. throw new FastGithubException($"解析不到{endPoint.Host}的IP");
  40. }
  41. /// <summary>
  42. /// 解析域名
  43. /// </summary>
  44. /// <param name="endPoint">节点</param>
  45. /// <param name="cancellationToken"></param>
  46. /// <returns></returns>
  47. public async IAsyncEnumerable<IPAddress> ResolveAllAsync(DnsEndPoint endPoint, [EnumeratorCancellation] CancellationToken cancellationToken)
  48. {
  49. if (this.dnsEndPointAddressElapseds.TryGetValue(endPoint, out var addressElapseds) && addressElapseds.IsEmpty == false)
  50. {
  51. foreach (var addressElapsed in addressElapseds)
  52. {
  53. yield return addressElapsed.Adddress;
  54. }
  55. }
  56. else
  57. {
  58. this.dnsEndPointAddressElapseds.TryAdd(endPoint, IPAddressElapsedCollection.Empty);
  59. await foreach (var adddress in this.dnsClient.ResolveAsync(endPoint, cancellationToken))
  60. {
  61. yield return adddress;
  62. }
  63. }
  64. }
  65. /// <summary>
  66. /// 对所有节点进行测速
  67. /// </summary>
  68. /// <param name="cancellationToken"></param>
  69. /// <returns></returns>
  70. public async Task TestAllEndPointsAsync(CancellationToken cancellationToken)
  71. {
  72. foreach (var keyValue in this.dnsEndPointAddressElapseds)
  73. {
  74. if (keyValue.Value.IsEmpty || keyValue.Value.IsExpired)
  75. {
  76. var dnsEndPoint = keyValue.Key;
  77. var addresses = new List<IPAddress>();
  78. await foreach (var adddress in this.dnsClient.ResolveAsync(dnsEndPoint, cancellationToken))
  79. {
  80. addresses.Add(adddress);
  81. }
  82. var addressElapseds = IPAddressElapsedCollection.Empty;
  83. if (addresses.Count == 1)
  84. {
  85. var addressElapsed = new IPAddressElapsed(addresses[0], TimeSpan.Zero);
  86. addressElapseds = new IPAddressElapsedCollection(addressElapsed);
  87. }
  88. else if (addresses.Count > 1)
  89. {
  90. var tasks = addresses.Select(address => IPAddressElapsed.ParseAsync(address, dnsEndPoint.Port, cancellationToken));
  91. addressElapseds = new IPAddressElapsedCollection(await Task.WhenAll(tasks));
  92. }
  93. this.dnsEndPointAddressElapseds[dnsEndPoint] = addressElapseds;
  94. }
  95. }
  96. }
  97. }
  98. }