diff --git a/FastGithub.DomainResolve/DomainResolver.cs b/FastGithub.DomainResolve/DomainResolver.cs index 1e80aea..cea0d8f 100644 --- a/FastGithub.DomainResolve/DomainResolver.cs +++ b/FastGithub.DomainResolve/DomainResolver.cs @@ -3,6 +3,7 @@ using DNS.Protocol; using FastGithub.Configuration; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -19,7 +20,9 @@ namespace FastGithub.DomainResolve /// sealed class DomainResolver : IDomainResolver { - private readonly IMemoryCache memoryCache; + private readonly IMemoryCache blackIPAddressCache = new MemoryCache(Options.Create(new MemoryCacheOptions())); + private readonly IMemoryCache domainResolveCache = new MemoryCache(Options.Create(new MemoryCacheOptions())); + private readonly FastGithubConfig fastGithubConfig; private readonly DnscryptProxy dnscryptProxy; private readonly ILogger logger; @@ -33,23 +36,40 @@ namespace FastGithub.DomainResolve /// /// 域名解析器 - /// - /// + /// /// /// /// public DomainResolver( - IMemoryCache memoryCache, FastGithubConfig fastGithubConfig, DnscryptProxy dnscryptProxy, ILogger logger) { - this.memoryCache = memoryCache; this.fastGithubConfig = fastGithubConfig; this.dnscryptProxy = dnscryptProxy; this.logger = logger; } + /// + /// 设置ip黑名单 + /// + /// ip + /// 过期时间 + public void SetBlack(IPAddress address, TimeSpan expiration) + { + this.blackIPAddressCache.Set(address, address, expiration); + this.logger.LogWarning($"已自动将{address}关到黑屋{expiration}"); + } + + /// + /// 刷新域名解析结果 + /// + /// 域名 + public void FlushDomain(DnsEndPoint domain) + { + this.domainResolveCache.Remove(domain); + } + /// /// 解析域名 /// @@ -78,7 +98,7 @@ namespace FastGithub.DomainResolve /// private async Task LookupAsync(DnsEndPoint domain, CancellationToken cancellationToken) { - if (this.memoryCache.TryGetValue(domain, out var address)) + if (this.domainResolveCache.TryGetValue(domain, out var address)) { return address; } @@ -107,7 +127,7 @@ namespace FastGithub.DomainResolve } this.logger.LogInformation($"[{domain.Host}->{address}]"); - this.memoryCache.Set(domain, address, expiration); + this.domainResolveCache.Set(domain, address, expiration); return address; } @@ -170,11 +190,6 @@ namespace FastGithub.DomainResolve return default; } - if (port <= 0) - { - return addresses.FirstOrDefault(); - } - var tasks = addresses.Select(address => this.IsAvailableAsync(address, port, cancellationToken)); var fastTask = await Task.WhenAny(tasks); return await fastTask; @@ -190,6 +205,16 @@ namespace FastGithub.DomainResolve /// private async Task IsAvailableAsync(IPAddress address, int port, CancellationToken cancellationToken) { + if (port <= 0) + { + return address; + } + + if (this.blackIPAddressCache.TryGetValue(address, out _)) + { + return default; + } + try { using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); diff --git a/FastGithub.DomainResolve/IDomainResolver.cs b/FastGithub.DomainResolve/IDomainResolver.cs index 801a187..505a031 100644 --- a/FastGithub.DomainResolve/IDomainResolver.cs +++ b/FastGithub.DomainResolve/IDomainResolver.cs @@ -1,4 +1,5 @@ -using System.Net; +using System; +using System.Net; using System.Threading; using System.Threading.Tasks; @@ -9,10 +10,23 @@ namespace FastGithub.DomainResolve /// public interface IDomainResolver { + /// + /// 设置ip黑名单 + /// + /// ip + /// 过期时间 + void SetBlack(IPAddress address, TimeSpan expiration); + + /// + /// 刷新域名解析结果 + /// + /// 域名 + void FlushDomain(DnsEndPoint domain); + /// /// 解析域名 /// - /// + /// 域名 /// /// Task ResolveAsync(DnsEndPoint domain, CancellationToken cancellationToken = default); diff --git a/FastGithub.DomainResolve/ServiceCollectionExtensions.cs b/FastGithub.DomainResolve/ServiceCollectionExtensions.cs index 8e1cd7f..deccdd2 100644 --- a/FastGithub.DomainResolve/ServiceCollectionExtensions.cs +++ b/FastGithub.DomainResolve/ServiceCollectionExtensions.cs @@ -15,8 +15,7 @@ namespace FastGithub /// /// public static IServiceCollection AddDomainResolve(this IServiceCollection services) - { - services.AddMemoryCache(); + { services.TryAddSingleton(); services.TryAddSingleton(); return services.AddHostedService(); diff --git a/FastGithub.Http/HttpClientHandler.cs b/FastGithub.Http/HttpClientHandler.cs index f036b9c..c3c1585 100644 --- a/FastGithub.Http/HttpClientHandler.cs +++ b/FastGithub.Http/HttpClientHandler.cs @@ -21,6 +21,7 @@ namespace FastGithub.Http { private readonly DomainConfig domainConfig; private readonly IDomainResolver domainResolver; + private readonly TimeSpan blackIPAddressExpiration = TimeSpan.FromMinutes(10d); /// /// HttpClientHandler @@ -42,8 +43,16 @@ namespace FastGithub.Http /// protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - await this.ProcessRequestAsync(request, cancellationToken); - return await this.SendRequestAsync(request, cancellationToken); + try + { + await this.ProcessRequestAsync(request, cancellationToken); + return await this.SendRequestAsync(request, cancellationToken); + } + catch (HttpRequestException ex) + { + this.InterceptRequestException(request, ex); + throw; + } } /// @@ -109,6 +118,32 @@ namespace FastGithub.Http } } + /// + /// 拦截请求异常 + /// 查找TimedOut的ip地址添加到黑名单 + /// + /// + /// + private void InterceptRequestException(HttpRequestMessage request, HttpRequestException exception) + { + if (request.RequestUri == null || + exception.InnerException is not SocketException socketException || + socketException.SocketErrorCode != SocketError.TimedOut) + { + return; + } + + if (IPAddress.TryParse(request.RequestUri.Host, out var address)) + { + this.domainResolver.SetBlack(address, this.blackIPAddressExpiration); + } + + if (request.Headers.Host != null) + { + this.domainResolver.FlushDomain(new DnsEndPoint(request.Headers.Host, request.RequestUri.Port)); + } + } + /// /// 创建转发代理的httpHandler ///