diff --git a/FastGithub.ReverseProxy/HttpClient.cs b/FastGithub.ReverseProxy/HttpClient.cs new file mode 100644 index 0000000..0e86e5c --- /dev/null +++ b/FastGithub.ReverseProxy/HttpClient.cs @@ -0,0 +1,40 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace FastGithub.ReverseProxy +{ + /// + /// YARP的HttpClient + /// + class HttpClient : HttpMessageInvoker + { + private readonly bool tlsSni; + + /// + /// YARP的HttpClient + /// + /// + /// + /// + public HttpClient(HttpMessageHandler handler, bool disposeHandler, bool tlsSni) : + base(handler, disposeHandler) + { + this.tlsSni = tlsSni; + } + + /// + /// 发送数据 + /// + /// + /// + /// + public override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var isHttps = request.RequestUri?.Scheme == Uri.UriSchemeHttps; + request.SetSniContext(new SniContext(isHttps, this.tlsSni)); + return base.SendAsync(request, cancellationToken); + } + } +} diff --git a/FastGithub.ReverseProxy/NoSniHttpClientHanlder.cs b/FastGithub.ReverseProxy/HttpClientHanlder.cs similarity index 75% rename from FastGithub.ReverseProxy/NoSniHttpClientHanlder.cs rename to FastGithub.ReverseProxy/HttpClientHanlder.cs index 7a8c898..c806768 100644 --- a/FastGithub.ReverseProxy/NoSniHttpClientHanlder.cs +++ b/FastGithub.ReverseProxy/HttpClientHanlder.cs @@ -9,20 +9,20 @@ using System.Threading.Tasks; namespace FastGithub.ReverseProxy { /// - /// 不发送NoSni的HttpClientHandler + /// YARP的HttpClientHandler /// - class NoSniHttpClientHanlder : DelegatingHandler + class HttpClientHanlder : DelegatingHandler { private readonly DomainResolver domainResolver; - private readonly ILogger logger; + private readonly ILogger logger; /// - /// 不发送NoSni的HttpClientHandler + /// YARP的HttpClientHandler /// /// - public NoSniHttpClientHanlder( + public HttpClientHanlder( DomainResolver domainResolver, - ILogger logger) + ILogger logger) { this.domainResolver = domainResolver; this.logger = logger; @@ -45,7 +45,8 @@ namespace FastGithub.ReverseProxy 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) + var sniContext = ctx.InitialRequestMessage.GetSniContext(); + if (sniContext.IsHttps == false) { return stream; } @@ -53,7 +54,7 @@ namespace FastGithub.ReverseProxy var sslStream = new SslStream(stream, leaveInnerStreamOpen: false); await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions { - TargetHost = string.Empty, + TargetHost = sniContext.TlsSniValue, RemoteCertificateValidationCallback = delegate { return true; } }, ct); return sslStream; @@ -74,16 +75,25 @@ namespace FastGithub.ReverseProxy if (uri != null && uri.HostNameType == UriHostNameType.Dns) { var address = await this.domainResolver.ResolveAsync(uri.Host, cancellationToken); - this.logger.LogInformation($"[{address}--NoSni->{uri.Host}]"); - var builder = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttp, Host = address.ToString(), - Port = 443 }; request.RequestUri = builder.Uri; request.Headers.Host = uri.Host; + + // 计算Sni + var context = request.GetSniContext(); + if (context.IsHttps && context.TlsSni) + { + context.TlsSniValue = uri.Host; + this.logger.LogInformation($"[{address}--Sni->{uri.Host}]"); + } + else + { + this.logger.LogInformation($"[{address}--NoSni->{uri.Host}]"); + } } return await base.SendAsync(request, cancellationToken); } diff --git a/FastGithub.ReverseProxy/ReverseProxyMiddleware.cs b/FastGithub.ReverseProxy/ReverseProxyMiddleware.cs index 77e5b45..f3f27cb 100644 --- a/FastGithub.ReverseProxy/ReverseProxyMiddleware.cs +++ b/FastGithub.ReverseProxy/ReverseProxyMiddleware.cs @@ -1,7 +1,6 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using System; -using System.Net.Http; using System.Threading.Tasks; using Yarp.ReverseProxy.Forwarder; @@ -13,21 +12,18 @@ namespace FastGithub.ReverseProxy sealed class ReverseProxyMiddleware { private readonly IHttpForwarder httpForwarder; - private readonly SniHttpClientHanlder sniHttpClientHanlder; - private readonly NoSniHttpClientHanlder noSniHttpClientHanlder; + private readonly HttpClientHanlder httpClientHanlder; private readonly FastGithubConfig fastGithubConfig; private readonly ILogger logger; public ReverseProxyMiddleware( IHttpForwarder httpForwarder, - SniHttpClientHanlder sniHttpClientHanlder, - NoSniHttpClientHanlder noSniHttpClientHanlder, + HttpClientHanlder httpClientHanlder, FastGithubConfig fastGithubConfig, ILogger logger) { this.httpForwarder = httpForwarder; - this.sniHttpClientHanlder = sniHttpClientHanlder; - this.noSniHttpClientHanlder = noSniHttpClientHanlder; + this.httpClientHanlder = httpClientHanlder; this.fastGithubConfig = fastGithubConfig; this.logger = logger; } @@ -59,10 +55,7 @@ namespace FastGithub.ReverseProxy { var destinationPrefix = GetDestinationPrefix(host, domainConfig.Destination); var requestConfig = new ForwarderRequestConfig { Timeout = domainConfig.Timeout }; - - var httpClient = domainConfig.TlsSni - ? new HttpMessageInvoker(this.sniHttpClientHanlder, disposeHandler: false) - : new HttpMessageInvoker(this.noSniHttpClientHanlder, disposeHandler: false); + var httpClient = new HttpClient(this.httpClientHanlder, false, domainConfig.TlsSni); var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient, requestConfig); await ResponseErrorAsync(context, error); diff --git a/FastGithub.ReverseProxy/ReverseProxyServiceCollectionExtensions.cs b/FastGithub.ReverseProxy/ReverseProxyServiceCollectionExtensions.cs index 4f7c55e..15d6d11 100644 --- a/FastGithub.ReverseProxy/ReverseProxyServiceCollectionExtensions.cs +++ b/FastGithub.ReverseProxy/ReverseProxyServiceCollectionExtensions.cs @@ -19,8 +19,7 @@ namespace FastGithub .AddMemoryCache() .AddHttpForwarder() .AddSingleton() - .AddTransient() - .AddTransient() + .AddTransient() .AddSingleton(); } } diff --git a/FastGithub.ReverseProxy/SniContext.cs b/FastGithub.ReverseProxy/SniContext.cs new file mode 100644 index 0000000..8646ab7 --- /dev/null +++ b/FastGithub.ReverseProxy/SniContext.cs @@ -0,0 +1,34 @@ +namespace FastGithub.ReverseProxy +{ + /// + /// Sni上下文 + /// + sealed class SniContext + { + /// + /// 获取请求是否为https + /// + public bool IsHttps { get; } + + /// + /// 获取是否发送Sni + /// + public bool TlsSni { get; } + + /// + /// Sni值 + /// + public string TlsSniValue { get; set; } = string.Empty; + + /// + /// Sni上下文 + /// + /// + /// + public SniContext(bool isHttps, bool tlsSni) + { + this.IsHttps = isHttps; + this.TlsSni = tlsSni; + } + } +} diff --git a/FastGithub.ReverseProxy/SniContextExtensions.cs b/FastGithub.ReverseProxy/SniContextExtensions.cs new file mode 100644 index 0000000..da9cb3d --- /dev/null +++ b/FastGithub.ReverseProxy/SniContextExtensions.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; + +namespace FastGithub.ReverseProxy +{ + /// + /// SniContext扩展 + /// + static class SniContextExtensions + { + private static readonly HttpRequestOptionsKey key = new(nameof(SniContext)); + + /// + /// 设置SniContext + /// + /// + /// + public static void SetSniContext(this HttpRequestMessage httpRequestMessage, SniContext context) + { + httpRequestMessage.Options.Set(key, context); + } + + /// + /// 获取SniContext + /// + /// + /// + public static SniContext GetSniContext(this HttpRequestMessage httpRequestMessage) + { + if (httpRequestMessage.Options.TryGetValue(key, out var value)) + { + return value; + } + throw new InvalidOperationException($"请先调用{nameof(SetSniContext)}"); + } + } +} diff --git a/FastGithub.ReverseProxy/SniHttpClientHanlder.cs b/FastGithub.ReverseProxy/SniHttpClientHanlder.cs deleted file mode 100644 index 9893a22..0000000 --- a/FastGithub.ReverseProxy/SniHttpClientHanlder.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Microsoft.Extensions.Logging; -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - -namespace FastGithub.ReverseProxy -{ - /// - /// 携带Sni的的HttpClientHandler - /// - class SniHttpClientHanlder : DelegatingHandler - { - private readonly DomainResolver domainResolver; - private readonly ILogger logger; - - /// - /// 携带Sni的HttpClientHandler - /// - /// - public SniHttpClientHanlder( - DomainResolver domainResolver, - ILogger logger) - { - this.domainResolver = domainResolver; - this.logger = logger; - - this.InnerHandler = new SocketsHttpHandler - { - Proxy = null, - UseProxy = false, - AllowAutoRedirect = false, - }; - } - - /// - /// 替换域名为ip - /// - /// - /// - /// - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - var uri = request.RequestUri; - if (uri != null && uri.HostNameType == UriHostNameType.Dns) - { - var address = await this.domainResolver.ResolveAsync(uri.Host, cancellationToken); - this.logger.LogInformation($"[{address}--Sni->{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); - } - } -}