From c33d9040133b3e959094d46d984535283b5ecf2d Mon Sep 17 00:00:00 2001 From: xljiulang <366193849@qq.com> Date: Thu, 15 Jul 2021 02:49:31 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8F=8D=E5=90=91=E4=BB=A3?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GithubDnsHttpHandler.cs | 61 ------- .../GithubHttpClientHanlder.cs | 114 +++++++++++++ .../GithubReverseProxyOptions.cs | 5 - .../LifetimeHttpHandler.cs | 58 ------- .../LifetimeHttpHandlerCleaner.cs | 158 ------------------ .../NoneSniHttpClientFactory.cs | 111 ------------ ...everseProxyApplicationBuilderExtensions.cs | 7 +- ...ReverseProxyServiceCollectionExtensions.cs | 1 + .../GithubLookupFactoryOptions.cs | 2 +- FastGithub/appsettings.json | 1 - 10 files changed, 119 insertions(+), 399 deletions(-) delete mode 100644 FastGithub.ReverseProxy/GithubDnsHttpHandler.cs create mode 100644 FastGithub.ReverseProxy/GithubHttpClientHanlder.cs delete mode 100644 FastGithub.ReverseProxy/LifetimeHttpHandler.cs delete mode 100644 FastGithub.ReverseProxy/LifetimeHttpHandlerCleaner.cs delete mode 100644 FastGithub.ReverseProxy/NoneSniHttpClientFactory.cs diff --git a/FastGithub.ReverseProxy/GithubDnsHttpHandler.cs b/FastGithub.ReverseProxy/GithubDnsHttpHandler.cs deleted file mode 100644 index 2eb04a0..0000000 --- a/FastGithub.ReverseProxy/GithubDnsHttpHandler.cs +++ /dev/null @@ -1,61 +0,0 @@ -using FastGithub.Scanner; -using Microsoft.Extensions.Logging; -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - -namespace FastGithub.ReverseProxy -{ - /// - /// Github的dns解析的httpHandler - /// 使扫描索结果作为github的https请求的域名解析 - /// - sealed class GithubDnsHttpHandler : DelegatingHandler - { - private readonly IGithubScanResults scanResults; - private readonly ILogger logger; - - /// - /// Github的dns解析的httpHandler - /// - /// - /// - public GithubDnsHttpHandler( - IGithubScanResults scanResults, - HttpMessageHandler innerHandler, - ILogger logger) - : base(innerHandler) - { - this.scanResults = scanResults; - this.logger = logger; - } - - /// - /// 发送消息 - /// - /// - /// - /// - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - var uri = request.RequestUri; - if (uri != null && uri.HostNameType == UriHostNameType.Dns) - { - var address = this.scanResults.FindBestAddress(uri.Host); - if (address != null) - { - this.logger.LogInformation($"使用{address}请求{uri.Host}"); - var builder = new UriBuilder(uri) - { - Host = address.ToString() - }; - request.RequestUri = builder.Uri; - request.Headers.Host = uri.Host; - } - } - - return await base.SendAsync(request, cancellationToken); - } - } -} diff --git a/FastGithub.ReverseProxy/GithubHttpClientHanlder.cs b/FastGithub.ReverseProxy/GithubHttpClientHanlder.cs new file mode 100644 index 0000000..b60f2f9 --- /dev/null +++ b/FastGithub.ReverseProxy/GithubHttpClientHanlder.cs @@ -0,0 +1,114 @@ +using FastGithub.Scanner; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using System.Net; +using System.Net.Http; +using System.Net.Security; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace FastGithub.ReverseProxy +{ + /// + /// 使用于请求github的HttpClientHandler + /// + [Service(ServiceLifetime.Transient)] + sealed class GithubHttpClientHanlder : DelegatingHandler + { + private readonly IGithubScanResults githubScanResults; + private readonly ILogger logger; + private readonly IMemoryCache memoryCache; + + /// + /// 请求github的HttpClientHandler + /// + /// + /// + public GithubHttpClientHanlder( + IGithubScanResults githubScanResults, + ILogger logger, + IMemoryCache memoryCache) + { + this.githubScanResults = githubScanResults; + this.logger = logger; + this.memoryCache = memoryCache; + this.InnerHandler = CreateNoneSniHttpHandler(); + } + + /// + /// 创建无Sni发送的httpHandler + /// + /// + private static HttpMessageHandler CreateNoneSniHttpHandler() + { + return new SocketsHttpHandler + { + Proxy = null, + UseProxy = false, + AllowAutoRedirect = false, + ConnectCallback = async (ctx, ct) => + { + var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); + await socket.ConnectAsync(ctx.DnsEndPoint, ct); + var stream = new NetworkStream(socket, ownsSocket: true); + if (ctx.InitialRequestMessage.Headers.Host == null) + { + return stream; + } + + var sslStream = new SslStream(stream, leaveInnerStreamOpen: false); + await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions + { + TargetHost = string.Empty, + RemoteCertificateValidationCallback = delegate { return true; } + }, ct); + return sslStream; + } + }; + } + + + /// + /// 查找最快的ip来发送消息 + /// + /// + /// + /// + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var uri = request.RequestUri; + if (uri != null && uri.HostNameType == UriHostNameType.Dns) + { + var address = this.Resolve(uri.Host); + if (address != null) + { + this.logger.LogInformation($"使用{address}请求{uri.Host}"); + var builder = new UriBuilder(uri) + { + Host = address.ToString() + }; + request.RequestUri = builder.Uri; + request.Headers.Host = uri.Host; + } + } + return await base.SendAsync(request, cancellationToken); + } + + /// + /// 解析域名 + /// + /// + /// + private IPAddress? Resolve(string domain) + { + return this.memoryCache.GetOrCreate(typeof(GithubHttpClientHanlder), e => + { + e.SetAbsoluteExpiration(TimeSpan.FromSeconds(1d)); + return this.githubScanResults.FindBestAddress(domain); + }); + } + } +} diff --git a/FastGithub.ReverseProxy/GithubReverseProxyOptions.cs b/FastGithub.ReverseProxy/GithubReverseProxyOptions.cs index 8608d2b..2c53567 100644 --- a/FastGithub.ReverseProxy/GithubReverseProxyOptions.cs +++ b/FastGithub.ReverseProxy/GithubReverseProxyOptions.cs @@ -13,11 +13,6 @@ namespace FastGithub.ReverseProxy /// public bool Enable { get; set; } = true; - /// - /// 每个服务的最大代理连接数 - /// - public int MaxConnectionsPerServer { get; set; } = int.MaxValue; - /// /// 请求配置 /// diff --git a/FastGithub.ReverseProxy/LifetimeHttpHandler.cs b/FastGithub.ReverseProxy/LifetimeHttpHandler.cs deleted file mode 100644 index 932309f..0000000 --- a/FastGithub.ReverseProxy/LifetimeHttpHandler.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Diagnostics; -using System.Net.Http; -using System.Threading; - -namespace FastGithub.ReverseProxy -{ - /// - /// 表示自主管理生命周期的的HttpMessageHandler - /// - [DebuggerDisplay("LifeTime = {lifeTime}")] - sealed class LifetimeHttpHandler : DelegatingHandler - { - /// - /// 生命周期 - /// - private readonly TimeSpan lifeTime; - - /// - /// Token取消源 - /// - private readonly CancellationTokenSource tokenSource = new(); - - /// - /// 具有生命周期的HttpHandler - /// - /// HttpHandler - /// 拦截器的生命周期 - /// 失效回调 - /// - public LifetimeHttpHandler(HttpMessageHandler handler, TimeSpan lifeTime, Action deactivateAction) - : base(handler) - { - if (deactivateAction == null) - { - throw new ArgumentNullException(nameof(deactivateAction)); - } - - this.lifeTime = lifeTime; - - this.tokenSource.Token.Register(() => - { - this.tokenSource.Dispose(); - deactivateAction.Invoke(this); - }, useSynchronizationContext: false); - - this.tokenSource.CancelAfter(lifeTime); - } - - /// - /// 这里不释放资源 - /// - /// - protected override void Dispose(bool disposing) - { - } - } -} diff --git a/FastGithub.ReverseProxy/LifetimeHttpHandlerCleaner.cs b/FastGithub.ReverseProxy/LifetimeHttpHandlerCleaner.cs deleted file mode 100644 index bd1ca86..0000000 --- a/FastGithub.ReverseProxy/LifetimeHttpHandlerCleaner.cs +++ /dev/null @@ -1,158 +0,0 @@ -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Concurrent; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; - -namespace FastGithub.ReverseProxy -{ - /// - /// 表示LifetimeHttpHandler清理器 - /// - sealed class LifetimeHttpHandlerCleaner - { - private readonly ILogger logger; - - /// - /// 当前监视生命周期的记录的数量 - /// - private int trackingEntryCount = 0; - - /// - /// 监视生命周期的记录队列 - /// - private readonly ConcurrentQueue trackingEntries = new(); - - /// - /// 获取或设置清理的时间间隔 - /// 默认10s - /// - public TimeSpan CleanupInterval { get; set; } = TimeSpan.FromSeconds(10d); - - /// - /// LifetimeHttpHandler清理器 - /// - /// - public LifetimeHttpHandlerCleaner(ILogger logger) - { - this.logger = logger; - } - - /// - /// 添加要清除的httpHandler - /// - /// httpHandler - public void Add(LifetimeHttpHandler handler) - { - var entry = new TrackingEntry(handler); - this.trackingEntries.Enqueue(entry); - - // 从0变为1,要启动清理作业 - if (Interlocked.Increment(ref this.trackingEntryCount) == 1) - { - this.StartCleanup(); - } - } - - /// - /// 启动清理作业 - /// - private async void StartCleanup() - { - try - { - while (true) - { - await Task.Delay(this.CleanupInterval); - if (this.Cleanup() == true) - { - break; - } - } - } - catch (Exception ex) - { - this.logger.LogError(ex, "清理HttpMessageHandler出现不可预期的异常"); - // 这是应该不可能发生的 - } - } - - /// - /// 清理失效的拦截器 - /// 返回是否完全清理 - /// - /// - private bool Cleanup() - { - var cleanCount = this.trackingEntries.Count; - this.logger.LogTrace($"尝试清理{cleanCount}条HttpMessageHandler"); - - for (var i = 0; i < cleanCount; i++) - { - this.trackingEntries.TryDequeue(out var entry); - Debug.Assert(entry != null); - - if (entry.CanDispose == false) - { - this.trackingEntries.Enqueue(entry); - continue; - } - - this.logger.LogTrace($"释放了{entry.GetHashCode()}@HttpMessageHandler"); - entry.Dispose(); - if (Interlocked.Decrement(ref this.trackingEntryCount) == 0) - { - return true; - } - } - - return false; - } - - - /// - /// 表示监视生命周期的记录 - /// - private class TrackingEntry : IDisposable - { - /// - /// 用于释放资源的对象 - /// - private readonly IDisposable disposable; - - /// - /// 监视对象的弱引用 - /// - private readonly WeakReference weakReference; - - /// - /// 获取是否可以释放资源 - /// - /// - public bool CanDispose => this.weakReference.IsAlive == false; - - /// - /// 监视生命周期的记录 - /// - /// 激活状态的httpHandler - public TrackingEntry(LifetimeHttpHandler handler) - { - this.disposable = handler.InnerHandler!; - this.weakReference = new WeakReference(handler); - } - - /// - /// 释放资源 - /// - public void Dispose() - { - try - { - this.disposable.Dispose(); - } - catch (Exception) { } - } - } - } -} diff --git a/FastGithub.ReverseProxy/NoneSniHttpClientFactory.cs b/FastGithub.ReverseProxy/NoneSniHttpClientFactory.cs deleted file mode 100644 index 16ca0dd..0000000 --- a/FastGithub.ReverseProxy/NoneSniHttpClientFactory.cs +++ /dev/null @@ -1,111 +0,0 @@ -using FastGithub.Scanner; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System; -using System.Net.Http; -using System.Net.Security; -using System.Net.Sockets; - -namespace FastGithub.ReverseProxy -{ - /// - /// 禁用tls sni的HttpClient工厂 - /// - [Service(ServiceLifetime.Singleton)] - sealed class NoneSniHttpClientFactory - { - private readonly IGithubScanResults githubScanResults; - private readonly IOptionsMonitor options; - private readonly ILogger logger; - - /// - /// 生命周期 - /// - private readonly TimeSpan lifeTime = TimeSpan.FromMinutes(2d); - - /// - /// 具有生命周期的httpHandler延时创建对象 - /// - private volatile Lazy lifeTimeHttpHandlerLazy; - - /// - /// HttpHandler清理器 - /// - private readonly LifetimeHttpHandlerCleaner httpHandlerCleaner; - - - /// - /// 禁用tls sni的HttpClient工厂 - /// - /// - public NoneSniHttpClientFactory( - IGithubScanResults githubScanResults, - IOptionsMonitor options, - ILogger logger) - { - this.githubScanResults = githubScanResults; - this.options = options; - this.logger = logger; - this.lifeTimeHttpHandlerLazy = new Lazy(this.CreateHttpHandler, true); - this.httpHandlerCleaner = new LifetimeHttpHandlerCleaner(logger); - } - - /// - /// 创建HttpClient - /// - /// - public HttpMessageInvoker CreateHttpClient() - { - var handler = this.lifeTimeHttpHandlerLazy.Value; - return new HttpMessageInvoker(handler); - } - - /// - /// 创建具有生命周期控制的httpHandler - /// - /// - private LifetimeHttpHandler CreateHttpHandler() - { - var noneSniHandler = new SocketsHttpHandler - { - Proxy = null, - UseProxy = false, - AllowAutoRedirect = false, - MaxConnectionsPerServer = this.options.CurrentValue.MaxConnectionsPerServer, - ConnectCallback = async (ctx, ct) => - { - var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); - await socket.ConnectAsync(ctx.DnsEndPoint, ct); - var stream = new NetworkStream(socket, ownsSocket: true); - if (ctx.InitialRequestMessage.Headers.Host == null) - { - return stream; - } - - var sslStream = new SslStream(stream, leaveInnerStreamOpen: false); - await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions - { - TargetHost = string.Empty, - RemoteCertificateValidationCallback = delegate { return true; } - }, ct); - return sslStream; - } - }; - - var dnsHandler = new GithubDnsHttpHandler(this.githubScanResults, noneSniHandler, this.logger); - return new LifetimeHttpHandler(dnsHandler, this.lifeTime, this.OnHttpHandlerDeactivate); - } - - /// - /// 当有httpHandler失效时 - /// - /// httpHandler - private void OnHttpHandlerDeactivate(LifetimeHttpHandler handler) - { - // 切换激活状态的记录的实例 - this.lifeTimeHttpHandlerLazy = new Lazy(this.CreateHttpHandler, true); - this.httpHandlerCleaner.Add(handler); - } - } -} diff --git a/FastGithub.ReverseProxy/ReverseProxyApplicationBuilderExtensions.cs b/FastGithub.ReverseProxy/ReverseProxyApplicationBuilderExtensions.cs index 62dfff0..908b827 100644 --- a/FastGithub.ReverseProxy/ReverseProxyApplicationBuilderExtensions.cs +++ b/FastGithub.ReverseProxy/ReverseProxyApplicationBuilderExtensions.cs @@ -4,8 +4,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using System; -using System.Linq; +using System.Net.Http; using Yarp.ReverseProxy.Forwarder; namespace FastGithub @@ -23,7 +22,7 @@ namespace FastGithub public static IApplicationBuilder UseGithubReverseProxy(this IApplicationBuilder app) { var httpForwarder = app.ApplicationServices.GetRequiredService(); - var httpClientFactory = app.ApplicationServices.GetRequiredService(); + var httpClientHanlder = app.ApplicationServices.GetRequiredService(); var lookupOptions = app.ApplicationServices.GetRequiredService>(); var options = app.ApplicationServices.GetRequiredService>(); @@ -38,7 +37,7 @@ namespace FastGithub { var port = context.Request.Host.Port ?? 443; var destinationPrefix = $"http://{host}:{port}/"; - var httpClient = httpClientFactory.CreateHttpClient(); + var httpClient = new HttpMessageInvoker(httpClientHanlder, disposeHandler: false); var requestConfig = options.CurrentValue.ForwarderRequestConfig; await httpForwarder.SendAsync(context, destinationPrefix, httpClient, requestConfig); } diff --git a/FastGithub.ReverseProxy/ReverseProxyServiceCollectionExtensions.cs b/FastGithub.ReverseProxy/ReverseProxyServiceCollectionExtensions.cs index df95e9e..b7245f7 100644 --- a/FastGithub.ReverseProxy/ReverseProxyServiceCollectionExtensions.cs +++ b/FastGithub.ReverseProxy/ReverseProxyServiceCollectionExtensions.cs @@ -19,6 +19,7 @@ namespace FastGithub var assembly = typeof(ReverseProxyServiceCollectionExtensions).Assembly; return services .AddServiceAndOptions(assembly, configuration) + .AddMemoryCache() .AddHttpForwarder(); } } diff --git a/FastGithub.Scanner/GithubLookupFactoryOptions.cs b/FastGithub.Scanner/GithubLookupFactoryOptions.cs index 354e0f5..da8e753 100644 --- a/FastGithub.Scanner/GithubLookupFactoryOptions.cs +++ b/FastGithub.Scanner/GithubLookupFactoryOptions.cs @@ -11,6 +11,6 @@ namespace FastGithub.Scanner /// /// 反查的域名 /// - public string [] Domains { get; set; } = new string[0]; + public HashSet Domains { get; set; } = new(); } } diff --git a/FastGithub/appsettings.json b/FastGithub/appsettings.json index f4581d5..9fb6915 100644 --- a/FastGithub/appsettings.json +++ b/FastGithub/appsettings.json @@ -6,7 +6,6 @@ }, "ReverseProxy": { "Enable": true, // Ƿʹ÷github, - "MaxConnectionsPerServer": 100, // "ForwarderRequestConfig": { "Timeout": "00:02:00" // ʱʱ }