From dee494b754283369a30dd01311173e13465c8b8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=9B=BD=E4=BC=9F?= <366193849@qq.com> Date: Sun, 26 Sep 2021 15:02:24 +0800 Subject: [PATCH] =?UTF-8?q?HttpClientHandler=E5=A4=9Aip=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E5=B0=9D=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FastGithub.DomainResolve/DnsClient.cs | 9 +- FastGithub.DomainResolve/DomainResolver.cs | 265 ++++++------------ FastGithub.DomainResolve/IDomainResolver.cs | 19 +- FastGithub.Http/HttpClientHandler.cs | 248 +++++++--------- FastGithub.Http/RequestContext.cs | 20 +- .../RequestLoggingMilldeware.cs | 23 +- FastGithub/appsettings.json | 2 +- 7 files changed, 236 insertions(+), 350 deletions(-) diff --git a/FastGithub.DomainResolve/DnsClient.cs b/FastGithub.DomainResolve/DnsClient.cs index 0f994eb..305987d 100644 --- a/FastGithub.DomainResolve/DnsClient.cs +++ b/FastGithub.DomainResolve/DnsClient.cs @@ -17,7 +17,7 @@ namespace FastGithub.DomainResolve { private readonly IPEndPoint dns; private readonly IRequestResolver resolver; - private readonly TimeSpan timeout = TimeSpan.FromSeconds(5d); + private readonly int timeout = (int)TimeSpan.FromSeconds(2d).TotalMilliseconds; /// /// DNS客户端 @@ -28,7 +28,7 @@ namespace FastGithub.DomainResolve this.dns = dns; this.resolver = dns.Port == 53 ? new TcpRequestResolver(dns) - : new UdpRequestResolver(dns, new TcpRequestResolver(dns)); + : new UdpRequestResolver(dns, new TcpRequestResolver(dns), this.timeout); } /// @@ -46,10 +46,7 @@ namespace FastGithub.DomainResolve }; request.Questions.Add(new Question(new Domain(domain), RecordType.A)); var clientRequest = new ClientRequest(this.resolver, request); - - using var timeoutTokenSource = new CancellationTokenSource(this.timeout); - using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token); - var response = await clientRequest.Resolve(linkedTokenSource.Token); + var response = await clientRequest.Resolve(cancellationToken); return response.AnswerRecords.OfType().Select(item => item.IPAddress).ToArray(); } diff --git a/FastGithub.DomainResolve/DomainResolver.cs b/FastGithub.DomainResolve/DomainResolver.cs index 444ae89..6d376d8 100644 --- a/FastGithub.DomainResolve/DomainResolver.cs +++ b/FastGithub.DomainResolve/DomainResolver.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -18,21 +19,14 @@ namespace FastGithub.DomainResolve /// sealed class DomainResolver : IDomainResolver { - private readonly IMemoryCache domainResolveCache = new MemoryCache(Options.Create(new MemoryCacheOptions())); - private readonly IMemoryCache disableIPAddressCache = new MemoryCache(Options.Create(new MemoryCacheOptions())); - private readonly DnscryptProxy dnscryptProxy; private readonly FastGithubConfig fastGithubConfig; private readonly ILogger logger; - private readonly TimeSpan connectTimeout = TimeSpan.FromSeconds(5d); - private readonly TimeSpan disableIPExpiration = TimeSpan.FromMinutes(2d); - - private readonly TimeSpan dnscryptExpiration = TimeSpan.FromMinutes(10d); - private readonly TimeSpan fallbackExpiration = TimeSpan.FromMinutes(2d); - private readonly TimeSpan loopbackExpiration = TimeSpan.FromSeconds(5d); - - private readonly ConcurrentDictionary semaphoreSlims = new(); + private readonly ConcurrentDictionary semaphoreSlims = new(); + private readonly IMemoryCache ipEndPointAvailableCache = new MemoryCache(Options.Create(new MemoryCacheOptions())); + private readonly TimeSpan ipEndPointExpiration = TimeSpan.FromMinutes(2d); + private readonly TimeSpan ipEndPointConnectTimeout = TimeSpan.FromSeconds(5d); /// /// 域名解析器 @@ -41,7 +35,6 @@ namespace FastGithub.DomainResolve /// /// public DomainResolver( - DnscryptProxy dnscryptProxy, FastGithubConfig fastGithubConfig, ILogger logger) @@ -51,39 +44,58 @@ namespace FastGithub.DomainResolve this.logger = logger; } - /// - /// 设置ip不可用 - /// - /// ip - public void SetDisabled(IPAddress address) - { - this.disableIPAddressCache.Set(address, address, this.disableIPExpiration); - } - - /// - /// 刷新域名解析结果 - /// - /// 域名 - public void FlushDomain(DnsEndPoint domain) - { - this.domainResolveCache.Remove(domain); - } - /// /// 解析域名 /// /// /// - /// - /// /// - public async Task ResolveAsync(DnsEndPoint domain, CancellationToken cancellationToken = default) + public async Task ResolveAsync(DnsEndPoint domain, CancellationToken cancellationToken) { - var semaphore = this.semaphoreSlims.GetOrAdd(domain, _ => new SemaphoreSlim(1, 1)); + await foreach (var address in this.ResolveAsync(domain.Host, cancellationToken)) + { + if (await this.IsAvailableAsync(new IPEndPoint(address, domain.Port), cancellationToken)) + { + return address; + } + } + throw new FastGithubException($"解析不到{domain.Host}可用的IP"); + } + + /// + /// 验证远程节点是否可连接 + /// + /// + /// + /// + /// + private async Task IsAvailableAsync(IPEndPoint ipEndPoint, CancellationToken cancellationToken) + { + var semaphore = this.semaphoreSlims.GetOrAdd(ipEndPoint, _ => new SemaphoreSlim(1, 1)); try { await semaphore.WaitAsync(CancellationToken.None); - return await this.ResolveCoreAsync(domain, cancellationToken); + if (this.ipEndPointAvailableCache.TryGetValue(ipEndPoint, out var available)) + { + return available; + } + + try + { + using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); + using var timeoutTokenSource = new CancellationTokenSource(this.ipEndPointConnectTimeout); + using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token); + await socket.ConnectAsync(ipEndPoint, linkedTokenSource.Token); + available = true; + } + catch (Exception) + { + cancellationToken.ThrowIfCancellationRequested(); + available = false; + } + + this.ipEndPointAvailableCache.Set(ipEndPoint, available, ipEndPointExpiration); + return available; } finally { @@ -91,89 +103,46 @@ namespace FastGithub.DomainResolve } } + /// /// 解析域名 /// - /// - /// - /// - /// - /// - private async Task ResolveCoreAsync(DnsEndPoint domain, CancellationToken cancellationToken) - { - if (domain.Host == "localhost") - { - return IPAddress.Loopback; - } - - if (this.domainResolveCache.TryGetValue(domain, out var address) && address != null) - { - return address; - } - - var expiration = this.dnscryptExpiration; - address = await this.LookupByDnscryptAsync(domain, cancellationToken); - - if (address == null) - { - expiration = this.fallbackExpiration; - address = await this.LookupByFallbackAsync(domain, cancellationToken); - } - - if (address == null) - { - throw new FastGithubException($"当前解析不到{domain.Host}可用的ip,请刷新重试"); - } - - // 往往是被污染的dns - if (address.Equals(IPAddress.Loopback) == true) - { - expiration = this.loopbackExpiration; - } - - this.domainResolveCache.Set(domain, address, expiration); - return address; - } - - - /// - /// Dnscrypt查找ip - /// - /// + /// 域名 /// /// - private async Task LookupByDnscryptAsync(DnsEndPoint domain, CancellationToken cancellationToken) + public async IAsyncEnumerable ResolveAsync(string domain, [EnumeratorCancellation] CancellationToken cancellationToken) { - var dns = this.dnscryptProxy.LocalEndPoint; - if (dns == null) + if (domain == "localhost") { - return null; + yield return IPAddress.Loopback; + yield break; } - var dnsClient = new DnsClient(dns); - var address = await this.LookupAsync(dnsClient, domain, cancellationToken); - return address ?? await this.LookupAsync(dnsClient, domain, cancellationToken); - } - - /// - /// 回退查找ip - /// - /// - /// - /// - /// - private async Task LookupByFallbackAsync(DnsEndPoint domain, CancellationToken cancellationToken) - { - foreach (var dns in this.fastGithubConfig.FallbackDns) + var hashSet = new HashSet(); + var cryptDns = this.dnscryptProxy.LocalEndPoint; + if (cryptDns != null) { - var dnsClient = new DnsClient(dns); - var address = await this.LookupAsync(dnsClient, domain, cancellationToken); - if (address != null) + var dnsClient = new DnsClient(cryptDns); + foreach (var address in await this.LookupAsync(dnsClient, domain, cancellationToken)) { - return address; + if (hashSet.Add(address) == true) + { + yield return address; + } + } + } + + foreach (var fallbackDns in this.fastGithubConfig.FallbackDns) + { + var dnsClient = new DnsClient(fallbackDns); + foreach (var address in await this.LookupAsync(dnsClient, domain, cancellationToken)) + { + if (hashSet.Add(address) == true) + { + yield return address; + } } } - return default; } /// @@ -183,92 +152,20 @@ namespace FastGithub.DomainResolve /// /// /// - private async Task LookupAsync(DnsClient dnsClient, DnsEndPoint domain, CancellationToken cancellationToken) + private async Task LookupAsync(DnsClient dnsClient, string domain, CancellationToken cancellationToken) { try { - var addresses = await dnsClient.LookupAsync(domain.Host, cancellationToken); - var address = await this.FindFastValueAsync(addresses, domain.Port, cancellationToken); - - if (address == null) - { - this.logger.LogWarning($"{dnsClient}解析不到{domain.Host}可用的ip解析"); - } - else - { - this.logger.LogInformation($"{dnsClient}: {domain.Host}->{address}"); - } - return address; + var addresses = await dnsClient.LookupAsync(domain, cancellationToken); + var items = string.Join(", ", addresses.Select(item => item.ToString())); + this.logger.LogInformation($"{dnsClient}:{domain}->[{items}]"); + return addresses; } catch (Exception ex) { cancellationToken.ThrowIfCancellationRequested(); - this.logger.LogWarning($"{dnsClient}无法解析{domain.Host}:{ex.Message}"); - return default; - } - } - - /// - /// 获取最快的ip - /// - /// - /// - /// - /// - /// - private async Task FindFastValueAsync(IEnumerable addresses, int port, CancellationToken cancellationToken) - { - addresses = addresses.Where(IsEnableIPAddress).ToArray(); - if (addresses.Any() == false) - { - 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; - - bool IsEnableIPAddress(IPAddress address) - { - return this.disableIPAddressCache.TryGetValue(address, out _) == false; - } - } - - - /// - /// 验证远程节点是否可连接 - /// - /// - /// - /// - /// - /// - private async Task IsAvailableAsync(IPAddress address, int port, CancellationToken cancellationToken) - { - try - { - using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); - using var timeoutTokenSource = new CancellationTokenSource(this.connectTimeout); - using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token); - await socket.ConnectAsync(address, port, linkedTokenSource.Token); - return address; - } - catch (OperationCanceledException) - { - cancellationToken.ThrowIfCancellationRequested(); - this.SetDisabled(address); - return default; - } - catch (Exception) - { - this.SetDisabled(address); - await Task.Delay(this.connectTimeout, cancellationToken); - return default; + this.logger.LogWarning($"{dnsClient}无法解析{domain}:{ex.Message}"); + return Array.Empty(); } } } diff --git a/FastGithub.DomainResolve/IDomainResolver.cs b/FastGithub.DomainResolve/IDomainResolver.cs index c744aca..fd063ed 100644 --- a/FastGithub.DomainResolve/IDomainResolver.cs +++ b/FastGithub.DomainResolve/IDomainResolver.cs @@ -1,4 +1,5 @@ -using System.Net; +using System.Collections.Generic; +using System.Net; using System.Threading; using System.Threading.Tasks; @@ -10,16 +11,12 @@ namespace FastGithub.DomainResolve public interface IDomainResolver { /// - /// 设置ip不可用 + /// 解析域名 /// - /// ip - void SetDisabled(IPAddress address); - - /// - /// 刷新域名解析结果 - /// - /// 域名 - void FlushDomain(DnsEndPoint domain); + /// + /// + /// + Task ResolveAsync(DnsEndPoint domain, CancellationToken cancellationToken = default); /// /// 解析域名 @@ -27,6 +24,6 @@ namespace FastGithub.DomainResolve /// 域名 /// /// - Task ResolveAsync(DnsEndPoint domain, CancellationToken cancellationToken = default); + IAsyncEnumerable ResolveAsync(string domain, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/FastGithub.Http/HttpClientHandler.cs b/FastGithub.Http/HttpClientHandler.cs index d2c1447..f49ba2a 100644 --- a/FastGithub.Http/HttpClientHandler.cs +++ b/FastGithub.Http/HttpClientHandler.cs @@ -3,12 +3,13 @@ using FastGithub.DomainResolve; using System; using System.Collections; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Security; using System.Net.Sockets; -using System.Security.Authentication; +using System.Runtime.CompilerServices; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; @@ -27,7 +28,7 @@ namespace FastGithub.Http /// HttpClientHandler /// /// - /// + /// public HttpClientHandler(DomainConfig domainConfig, IDomainResolver domainResolver) { this.domainResolver = domainResolver; @@ -41,27 +42,7 @@ namespace FastGithub.Http /// /// /// - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - try - { - await this.ProcessRequestAsync(request, cancellationToken); - return await this.SendRequestAsync(request, cancellationToken); - } - catch (HttpRequestException ex) - { - this.InterceptRequestException(request, ex); - throw; - } - } - - /// - /// 处理请求 - /// - /// - /// - /// - private async Task ProcessRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken) + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var uri = request.RequestUri; if (uri == null) @@ -70,91 +51,22 @@ namespace FastGithub.Http } // 请求上下文信息 - var context = new RequestContext - { - Domain = uri.Host, - IsHttps = uri.Scheme == Uri.UriSchemeHttps, - TlsSniValue = this.domainConfig.GetTlsSniPattern().WithDomain(uri.Host).WithRandom() - }; - request.SetRequestContext(context); + var isHttps = uri.Scheme == Uri.UriSchemeHttps; + var tlsSniValue = this.domainConfig.GetTlsSniPattern().WithDomain(uri.Host).WithRandom(); + request.SetRequestContext(new RequestContext(isHttps, tlsSniValue)); - // 解析ip,替换https为http - var uriBuilder = new UriBuilder(uri) - { - Scheme = Uri.UriSchemeHttp - }; + // 设置请求host,修改协议为http + request.Headers.Host = uri.Host; + request.RequestUri = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttp }.Uri; - if (uri.HostNameType == UriHostNameType.Dns) - { - if (IPAddress.TryParse(this.domainConfig.IPAddress, out var address) == false) - { - var endPoint = new DnsEndPoint(uri.Host, uri.Port); - address = await this.domainResolver.ResolveAsync(endPoint, cancellationToken); - } - uriBuilder.Host = address.ToString(); - request.Headers.Host = context.Domain; - context.TlsSniValue = context.TlsSniValue.WithIPAddress(address); - } - request.RequestUri = uriBuilder.Uri; - } - - /// - /// 发送请求 - /// - /// - /// - /// - private async Task SendRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { if (this.domainConfig.Timeout != null) { using var timeoutTokenSource = new CancellationTokenSource(this.domainConfig.Timeout.Value); using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token); - return await base.SendAsync(request, linkedTokenSource.Token); - } - else - { - return await base.SendAsync(request, cancellationToken); - } - } - - /// - /// 拦截请求异常 - /// 查找TimedOut的ip地址添加到黑名单 - /// - /// - /// - private void InterceptRequestException(HttpRequestMessage request, HttpRequestException exception) - { - if (request.RequestUri == null || IsTimedOutSocketError(exception) == false) - { - return; + return base.SendAsync(request, linkedTokenSource.Token); } - if (IPAddress.TryParse(request.RequestUri.Host, out var address)) - { - this.domainResolver.SetDisabled(address); - } - - if (request.Headers.Host != null) - { - this.domainResolver.FlushDomain(new DnsEndPoint(request.Headers.Host, request.RequestUri.Port)); - } - - - static bool IsTimedOutSocketError(HttpRequestException exception) - { - var inner = exception.InnerException; - while (inner != null) - { - if (inner is SocketException socketException && socketException.SocketErrorCode == SocketError.TimedOut) - { - return true; - } - inner = inner.InnerException; - } - return false; - } + return base.SendAsync(request, cancellationToken); } /// @@ -170,48 +82,106 @@ namespace FastGithub.Http UseCookies = false, AllowAutoRedirect = false, AutomaticDecompression = DecompressionMethods.None, - ConnectCallback = async (context, cancellationToken) => - { - var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); - await socket.ConnectAsync(context.DnsEndPoint, cancellationToken); - var stream = new NetworkStream(socket, ownsSocket: true); - - var requestContext = context.InitialRequestMessage.GetRequestContext(); - if (requestContext.IsHttps == false) - { - return stream; - } - - var sslStream = new SslStream(stream, leaveInnerStreamOpen: false); - await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions - { - EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13, - TargetHost = requestContext.TlsSniValue.Value, - RemoteCertificateValidationCallback = ValidateServerCertificate - }, cancellationToken); - return sslStream; - - - bool ValidateServerCertificate(object sender, X509Certificate? cert, X509Chain? chain, SslPolicyErrors errors) - { - if (errors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch)) - { - if (this.domainConfig.TlsIgnoreNameMismatch == true) - { - return true; - } - - var domain = requestContext.Domain; - var dnsNames = ReadDnsNames(cert); - return dnsNames.Any(dns => IsMatch(dns, domain)); - } - - return errors == SslPolicyErrors.None; - } - } + ConnectCallback = this.ConnectCallback }; } + /// + /// 连接回调 + /// + /// + /// + /// + private async ValueTask ConnectCallback(SocketsHttpConnectionContext context, CancellationToken cancellationToken) + { + var innerExceptions = new List(); + var ipEndPoints = this.GetIPEndPointsAsync(context.DnsEndPoint, cancellationToken); + + await foreach (var ipEndPoint in ipEndPoints) + { + try + { + return await this.ConnectAsync(context, ipEndPoint, cancellationToken); + } + catch (Exception ex) + { + innerExceptions.Add(ex); + } + } + + throw new AggregateException("没有可连接成功的IP", innerExceptions); + } + + /// + /// 建立连接 + /// + /// + /// + /// + /// + private async ValueTask ConnectAsync(SocketsHttpConnectionContext context, IPEndPoint ipEndPoint, CancellationToken cancellationToken) + { + var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); + await socket.ConnectAsync(ipEndPoint, cancellationToken); + var stream = new NetworkStream(socket, ownsSocket: true); + + var requestContext = context.InitialRequestMessage.GetRequestContext(); + if (requestContext.IsHttps == false) + { + return stream; + } + + var tlsSniValue = requestContext.TlsSniValue.WithIPAddress(ipEndPoint.Address); + var sslStream = new SslStream(stream, leaveInnerStreamOpen: false); + await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions + { + TargetHost = tlsSniValue.Value, + RemoteCertificateValidationCallback = ValidateServerCertificate + }, cancellationToken); + + return sslStream; + + // 验证证书有效性 + bool ValidateServerCertificate(object sender, X509Certificate? cert, X509Chain? chain, SslPolicyErrors errors) + { + if (errors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch)) + { + if (this.domainConfig.TlsIgnoreNameMismatch == true) + { + return true; + } + + var domain = context.DnsEndPoint.Host; + var dnsNames = ReadDnsNames(cert); + return dnsNames.Any(dns => IsMatch(dns, domain)); + } + + return errors == SslPolicyErrors.None; + } + } + + /// + /// 解析为IPEndPoint + /// + /// + /// + /// + private async IAsyncEnumerable GetIPEndPointsAsync(DnsEndPoint dnsEndPoint, [EnumeratorCancellation] CancellationToken cancellationToken) + { + if (IPAddress.TryParse(this.domainConfig.IPAddress, out var address) || + IPAddress.TryParse(dnsEndPoint.Host, out address)) + { + yield return new IPEndPoint(address, dnsEndPoint.Port); + } + else + { + await foreach (var item in this.domainResolver.ResolveAsync(dnsEndPoint.Host, cancellationToken)) + { + yield return new IPEndPoint(item, dnsEndPoint.Port); + } + } + } + /// /// 读取使用的DNS名称 /// diff --git a/FastGithub.Http/RequestContext.cs b/FastGithub.Http/RequestContext.cs index 12ba6f6..d4a89c2 100644 --- a/FastGithub.Http/RequestContext.cs +++ b/FastGithub.Http/RequestContext.cs @@ -10,16 +10,22 @@ namespace FastGithub.Http /// /// 获取或设置是否为https请求 /// - public bool IsHttps { get; set; } - - /// - /// 请求的域名 - /// - public string? Domain { get; set; } + public bool IsHttps { get; } /// /// 获取或设置Sni值 /// - public TlsSniPattern TlsSniValue { get; set; } + public TlsSniPattern TlsSniValue { get; } + + /// + /// 请求上下文 + /// + /// + /// + public RequestContext(bool isHttps, TlsSniPattern tlsSniValue) + { + IsHttps = isHttps; + TlsSniValue = tlsSniValue; + } } } diff --git a/FastGithub.HttpServer/RequestLoggingMilldeware.cs b/FastGithub.HttpServer/RequestLoggingMilldeware.cs index 5694bad..955d33a 100644 --- a/FastGithub.HttpServer/RequestLoggingMilldeware.cs +++ b/FastGithub.HttpServer/RequestLoggingMilldeware.cs @@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using System; using System.Diagnostics; -using System.IO; using System.Text; using System.Threading.Tasks; @@ -74,7 +73,7 @@ namespace FastGithub.HttpServer return false; } - if (exception is IOException ioException && ioException.InnerException is ConnectionAbortedException) + if (HasInnerException(exception)) { return false; } @@ -82,6 +81,26 @@ namespace FastGithub.HttpServer return true; } + /// + /// 是否有内部异常异常 + /// + /// + /// + /// + private static bool HasInnerException(Exception exception) where TInnerException : Exception + { + var inner = exception.InnerException; + while (inner != null) + { + if (inner is TInnerException) + { + return true; + } + inner = inner.InnerException; + } + return false; + } + /// /// 获取异常信息 /// diff --git a/FastGithub/appsettings.json b/FastGithub/appsettings.json index 246ae6e..b157b18 100644 --- a/FastGithub/appsettings.json +++ b/FastGithub/appsettings.json @@ -4,7 +4,7 @@ "HttpProxyPort": 38457, // http代理端口,linux/osx平台使用 "FallbackDns": [ // dnscrypt-proxy不可用时使用 "114.114.114.114:53", - "8.8.8.8:53" + "119.29.29.29:53" ], "DomainConfigs": { "*.fastgithub.com": { // 域名的*表示除.之外0到多个任意字符