瀏覽代碼

kestrel层实现http代理

陈国伟 3 年之前
父節點
當前提交
58f79ddc19
共有 29 個文件被更改,包括 621 次插入336 次删除
  1. 5 5
      FastGithub.HttpServer/ApplicationBuilderExtensions.cs
  2. 11 10
      FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfLinux.cs
  3. 1 1
      FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfLinuxDebian.cs
  4. 1 1
      FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfLinuxRedHat.cs
  5. 2 2
      FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfMacOS.cs
  6. 2 2
      FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfWindows.cs
  7. 1 1
      FastGithub.HttpServer/Certs/CertGenerator.cs
  8. 10 10
      FastGithub.HttpServer/Certs/CertService.cs
  9. 1 1
      FastGithub.HttpServer/Certs/ICaCertInstaller.cs
  10. 1 1
      FastGithub.HttpServer/FastGithub.HttpServer.csproj
  11. 68 0
      FastGithub.HttpServer/HttpMiddlewares/HttpProxyPacMiddleware.cs
  12. 7 7
      FastGithub.HttpServer/HttpMiddlewares/HttpReverseProxyMiddleware.cs
  13. 1 1
      FastGithub.HttpServer/HttpMiddlewares/IRequestLoggingFeature.cs
  14. 5 5
      FastGithub.HttpServer/HttpMiddlewares/RequestLoggingMilldeware.cs
  15. 0 242
      FastGithub.HttpServer/HttpProxyMiddleware.cs
  16. 50 15
      FastGithub.HttpServer/KestrelServerExtensions.cs
  17. 15 1
      FastGithub.HttpServer/ServiceCollectionExtensions.cs
  18. 1 1
      FastGithub.HttpServer/TcpMiddlewares/GithubGitReverseProxyHandler.cs
  19. 1 1
      FastGithub.HttpServer/TcpMiddlewares/GithubSshReverseProxyHandler.cs
  20. 135 0
      FastGithub.HttpServer/TcpMiddlewares/HttpProxyMiddleware.cs
  21. 11 0
      FastGithub.HttpServer/TcpMiddlewares/IHttpProxyFeature.cs
  22. 23 0
      FastGithub.HttpServer/TcpMiddlewares/ProxyProtocol.cs
  23. 8 8
      FastGithub.HttpServer/TcpMiddlewares/TcpReverseProxyHandler.cs
  24. 132 0
      FastGithub.HttpServer/TcpMiddlewares/TunnelMiddleware.cs
  25. 27 0
      FastGithub.HttpServer/TlsMiddlewares/FakeTlsConnectionFeature.cs
  26. 64 0
      FastGithub.HttpServer/TlsMiddlewares/TlsInvadeMiddleware.cs
  27. 27 0
      FastGithub.HttpServer/TlsMiddlewares/TlsRestoreMiddleware.cs
  28. 2 3
      FastGithub/Program.cs
  29. 9 18
      FastGithub/Startup.cs

+ 5 - 5
FastGithub.HttpServer/ApplicationBuilderExtensions.cs

@@ -1,4 +1,4 @@
-using FastGithub.HttpServer;
+using FastGithub.HttpServer.HttpMiddlewares;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.Extensions.DependencyInjection;
 
@@ -10,14 +10,14 @@ namespace FastGithub
     public static class ApplicationBuilderExtensions
     {
         /// <summary>
-        /// 使用http代理中间件
+        /// 使用http代理策略中间件
         /// </summary>
         /// <param name="app"></param> 
         /// <returns></returns>
-        public static IApplicationBuilder UseHttpProxy(this IApplicationBuilder app)
+        public static IApplicationBuilder UseHttpProxyPac(this IApplicationBuilder app)
         {
-            var middleware = app.ApplicationServices.GetRequiredService<HttpProxyMiddleware>();
-            return app.Use(next => context => middleware.InvokeAsync(context));
+            var middleware = app.ApplicationServices.GetRequiredService<HttpProxyPacMiddleware>();
+            return app.Use(next => context => middleware.InvokeAsync(context, next));
         }
 
         /// <summary>

+ 11 - 10
FastGithub.HttpServer/CaCertInstallerOfLinux.cs → FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfLinux.cs

@@ -1,11 +1,12 @@
-using Microsoft.Extensions.Logging;
+using FastGithub;
+using Microsoft.Extensions.Logging;
 using System;
 using System.Diagnostics;
 using System.IO;
 using System.Linq;
 using System.Runtime.InteropServices;
 
-namespace FastGithub.HttpServer
+namespace FastGithub.HttpServer.Certs.CaCertInstallers
 {
     abstract class CaCertInstallerOfLinux : ICaCertInstaller
     {
@@ -35,7 +36,7 @@ namespace FastGithub.HttpServer
         /// <returns></returns>
         public bool IsSupported()
         {
-            return OperatingSystem.IsLinux() && File.Exists(this.CaCertUpdatePath);
+            return OperatingSystem.IsLinux() && File.Exists(CaCertUpdatePath);
         }
 
         /// <summary>
@@ -44,7 +45,7 @@ namespace FastGithub.HttpServer
         /// <param name="caCertFilePath">证书文件路径</param>
         public void Install(string caCertFilePath)
         {
-            var destCertFilePath = Path.Combine(this.CaCertStorePath, Path.GetFileName(caCertFilePath));
+            var destCertFilePath = Path.Combine(CaCertStorePath, Path.GetFileName(caCertFilePath));
             if (File.Exists(destCertFilePath) && File.ReadAllBytes(caCertFilePath).SequenceEqual(File.ReadAllBytes(destCertFilePath)))
             {
                 return;
@@ -52,25 +53,25 @@ namespace FastGithub.HttpServer
 
             if (geteuid() != 0)
             {
-                this.logger.LogWarning($"无法自动安装CA证书{caCertFilePath}:没有root权限");
+                logger.LogWarning($"无法自动安装CA证书{caCertFilePath}:没有root权限");
                 return;
             }
 
             try
             {
-                Directory.CreateDirectory(this.CaCertStorePath);
-                foreach (var item in Directory.GetFiles(this.CaCertStorePath, "fastgithub.*"))
+                Directory.CreateDirectory(CaCertStorePath);
+                foreach (var item in Directory.GetFiles(CaCertStorePath, "fastgithub.*"))
                 {
                     File.Delete(item);
                 }
                 File.Copy(caCertFilePath, destCertFilePath, overwrite: true);
-                Process.Start(this.CaCertUpdatePath).WaitForExit();
-                this.logger.LogInformation($"已自动向系统安装CA证书{caCertFilePath}");
+                Process.Start(CaCertUpdatePath).WaitForExit();
+                logger.LogInformation($"已自动向系统安装CA证书{caCertFilePath}");
             }
             catch (Exception ex)
             {
                 File.Delete(destCertFilePath);
-                this.logger.LogWarning(ex.Message, "自动安装CA证书异常");
+                logger.LogWarning(ex.Message, "自动安装CA证书异常");
             }
         }
     }

+ 1 - 1
FastGithub.HttpServer/CaCertInstallerOfLinuxDebian.cs → FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfLinuxDebian.cs

@@ -1,6 +1,6 @@
 using Microsoft.Extensions.Logging;
 
-namespace FastGithub.HttpServer
+namespace FastGithub.HttpServer.Certs.CaCertInstallers
 {
     sealed class CaCertInstallerOfLinuxDebian : CaCertInstallerOfLinux
     {

+ 1 - 1
FastGithub.HttpServer/CaCertInstallerOfLinuxRedHat.cs → FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfLinuxRedHat.cs

@@ -1,6 +1,6 @@
 using Microsoft.Extensions.Logging;
 
-namespace FastGithub.HttpServer
+namespace FastGithub.HttpServer.Certs.CaCertInstallers
 {
     sealed class CaCertInstallerOfLinuxRedHat : CaCertInstallerOfLinux
     {

+ 2 - 2
FastGithub.HttpServer/CaCertInstallerOfMacOS.cs → FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfMacOS.cs

@@ -1,7 +1,7 @@
 using Microsoft.Extensions.Logging;
 using System;
 
-namespace FastGithub.HttpServer
+namespace FastGithub.HttpServer.Certs.CaCertInstallers
 {
     sealed class CaCertInstallerOfMacOS : ICaCertInstaller
     {
@@ -27,7 +27,7 @@ namespace FastGithub.HttpServer
         /// <param name="caCertFilePath">证书文件路径</param>
         public void Install(string caCertFilePath)
         {
-            this.logger.LogWarning($"请手动安装CA证书然后设置信任CA证书{caCertFilePath}");
+            logger.LogWarning($"请手动安装CA证书然后设置信任CA证书{caCertFilePath}");
         }
     }
 }

+ 2 - 2
FastGithub.HttpServer/CaCertInstallerOfWindows.cs → FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfWindows.cs

@@ -2,7 +2,7 @@
 using System;
 using System.Security.Cryptography.X509Certificates;
 
-namespace FastGithub.HttpServer
+namespace FastGithub.HttpServer.Certs.CaCertInstallers
 {
     sealed class CaCertInstallerOfWindows : ICaCertInstaller
     {
@@ -50,7 +50,7 @@ namespace FastGithub.HttpServer
             }
             catch (Exception)
             {
-                this.logger.LogWarning($"请手动安装CA证书{caCertFilePath}到“将所有的证书都放入下列存储”\\“受信任的根证书颁发机构”");
+                logger.LogWarning($"请手动安装CA证书{caCertFilePath}到“将所有的证书都放入下列存储”\\“受信任的根证书颁发机构”");
             }
         }
     }

+ 1 - 1
FastGithub.HttpServer/CertGenerator.cs → FastGithub.HttpServer/Certs/CertGenerator.cs

@@ -19,7 +19,7 @@ using System.Net;
 using System.Text;
 using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2;
 
-namespace FastGithub.HttpServer
+namespace FastGithub.HttpServer.Certs
 {
     /// <summary>
     /// 证书生成器

+ 10 - 10
FastGithub.HttpServer/CertService.cs → FastGithub.HttpServer/Certs/CertService.cs

@@ -8,7 +8,7 @@ using System.Linq;
 using System.Net;
 using System.Security.Cryptography.X509Certificates;
 
-namespace FastGithub.HttpServer
+namespace FastGithub.HttpServer.Certs
 {
     /// <summary>
     /// 证书服务
@@ -54,17 +54,17 @@ namespace FastGithub.HttpServer
         /// </summary> 
         public bool CreateCaCertIfNotExists()
         {
-            if (File.Exists(this.CaCerFilePath) && File.Exists(this.CaKeyFilePath))
+            if (File.Exists(CaCerFilePath) && File.Exists(CaKeyFilePath))
             {
                 return false;
             }
 
-            File.Delete(this.CaCerFilePath);
-            File.Delete(this.CaKeyFilePath);
+            File.Delete(CaCerFilePath);
+            File.Delete(CaKeyFilePath);
 
             var validFrom = DateTime.Today.AddDays(-1);
             var validTo = DateTime.Today.AddYears(10);
-            CertGenerator.GenerateBySelf(new[] { nameof(FastGithub) }, KEY_SIZE_BITS, validFrom, validTo, this.CaCerFilePath, this.CaKeyFilePath);
+            CertGenerator.GenerateBySelf(new[] { nameof(FastGithub) }, KEY_SIZE_BITS, validFrom, validTo, CaCerFilePath, CaKeyFilePath);
             return true;
         }
 
@@ -73,14 +73,14 @@ namespace FastGithub.HttpServer
         /// </summary> 
         public void InstallAndTrustCaCert()
         {
-            var installer = this.certInstallers.FirstOrDefault(item => item.IsSupported());
+            var installer = certInstallers.FirstOrDefault(item => item.IsSupported());
             if (installer != null)
             {
-                installer.Install(this.CaCerFilePath);
+                installer.Install(CaCerFilePath);
             }
             else
             {
-                this.logger.LogWarning($"请根据你的系统平台手动安装和信任CA证书{this.CaCerFilePath}");
+                logger.LogWarning($"请根据你的系统平台手动安装和信任CA证书{CaCerFilePath}");
             }
 
             GitConfigSslverify(false);
@@ -119,7 +119,7 @@ namespace FastGithub.HttpServer
         public X509Certificate2 GetOrCreateServerCert(string? domain)
         {
             var key = $"{nameof(CertService)}:{domain}";
-            return this.serverCertCache.GetOrCreate(key, GetOrCreateCert);
+            return serverCertCache.GetOrCreate(key, GetOrCreateCert);
 
             // 生成域名的1年证书
             X509Certificate2 GetOrCreateCert(ICacheEntry entry)
@@ -129,7 +129,7 @@ namespace FastGithub.HttpServer
                 var validTo = DateTime.Today.AddYears(1);
 
                 entry.SetAbsoluteExpiration(validTo);
-                return CertGenerator.GenerateByCa(domains, KEY_SIZE_BITS, validFrom, validTo, this.CaCerFilePath, this.CaKeyFilePath);
+                return CertGenerator.GenerateByCa(domains, KEY_SIZE_BITS, validFrom, validTo, CaCerFilePath, CaKeyFilePath);
             }
         }
 

+ 1 - 1
FastGithub.HttpServer/ICaCertInstaller.cs → FastGithub.HttpServer/Certs/ICaCertInstaller.cs

@@ -1,4 +1,4 @@
-namespace FastGithub.HttpServer
+namespace FastGithub.HttpServer.Certs
 {
     /// <summary>
     /// CA证书安装器

+ 1 - 1
FastGithub.HttpServer/FastGithub.HttpServer.csproj

@@ -7,7 +7,7 @@
 	<ItemGroup>
 		<FrameworkReference Include="Microsoft.AspNetCore.App" />
 		<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
-		<PackageReference Include="Yarp.ReverseProxy" Version="1.0.0" />
+		<PackageReference Include="Yarp.ReverseProxy" Version="1.1.0" />
 	</ItemGroup>
 
 	<ItemGroup>

+ 68 - 0
FastGithub.HttpServer/HttpMiddlewares/HttpProxyPacMiddleware.cs

@@ -0,0 +1,68 @@
+using FastGithub.Configuration;
+using FastGithub.HttpServer.TcpMiddlewares;
+using Microsoft.AspNetCore.Http;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FastGithub.HttpServer.HttpMiddlewares
+{
+    /// <summary>
+    /// http代理策略中间件
+    /// </summary>
+    sealed class HttpProxyPacMiddleware
+    {
+        private readonly FastGithubConfig fastGithubConfig;
+
+        /// <summary>
+        /// http代理策略中间件
+        /// </summary>
+        /// <param name="fastGithubConfig"></param> 
+        public HttpProxyPacMiddleware(FastGithubConfig fastGithubConfig)
+        {
+            this.fastGithubConfig = fastGithubConfig;
+        }
+
+        /// <summary>
+        /// 处理请求
+        /// </summary>
+        /// <param name="context"></param>
+        /// <param name="next"></param>
+        /// <returns></returns>
+        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
+        {
+            // http请求经过了httpProxy中间件
+            var proxyFeature = context.Features.Get<IHttpProxyFeature>();
+            if (proxyFeature != null && proxyFeature.ProxyProtocol == ProxyProtocol.None)
+            {
+                var proxyPac = this.CreateProxyPac(context.Request.Host);
+                context.Response.ContentType = "application/x-ns-proxy-autoconfig";
+                context.Response.Headers.Add("Content-Disposition", $"attachment;filename=proxy.pac");
+                await context.Response.WriteAsync(proxyPac);
+            }
+            else
+            {
+                await next(context);
+            }
+        }
+
+        /// <summary>
+        /// 创建proxypac脚本
+        /// </summary>
+        /// <param name="proxyHost"></param>
+        /// <returns></returns>
+        private string CreateProxyPac(HostString proxyHost)
+        {
+            var buidler = new StringBuilder();
+            buidler.AppendLine("function FindProxyForURL(url, host){");
+            buidler.AppendLine($"    var fastgithub = 'PROXY {proxyHost}';");
+            foreach (var domain in fastGithubConfig.GetDomainPatterns())
+            {
+                buidler.AppendLine($"    if (shExpMatch(host, '{domain}')) return fastgithub;");
+            }
+            buidler.AppendLine("    return 'DIRECT';");
+            buidler.AppendLine("}");
+            return buidler.ToString();
+        }
+    }
+}

+ 7 - 7
FastGithub.HttpServer/HttpReverseProxyMiddleware.cs → FastGithub.HttpServer/HttpMiddlewares/HttpReverseProxyMiddleware.cs

@@ -8,7 +8,7 @@ using System.Net;
 using System.Threading.Tasks;
 using Yarp.ReverseProxy.Forwarder;
 
-namespace FastGithub.HttpServer
+namespace FastGithub.HttpServer.HttpMiddlewares
 {
     /// <summary>
     /// 反向代理中间件
@@ -43,7 +43,7 @@ namespace FastGithub.HttpServer
         public async Task InvokeAsync(HttpContext context, RequestDelegate next)
         {
             var host = context.Request.Host;
-            if (this.TryGetDomainConfig(host, out var domainConfig) == false)
+            if (TryGetDomainConfig(host, out var domainConfig) == false)
             {
                 await next(context);
             }
@@ -51,8 +51,8 @@ namespace FastGithub.HttpServer
             {
                 var scheme = context.Request.Scheme;
                 var destinationPrefix = GetDestinationPrefix(scheme, host, domainConfig.Destination);
-                var httpClient = this.httpClientFactory.CreateHttpClient(host.Host, domainConfig);
-                var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient);
+                var httpClient = httpClientFactory.CreateHttpClient(host.Host, domainConfig);
+                var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient, ForwarderRequestConfig.Empty, HttpTransformer.Empty);
                 await HandleErrorAsync(context, error);
             }
             else
@@ -74,7 +74,7 @@ namespace FastGithub.HttpServer
         /// <returns></returns>
         private bool TryGetDomainConfig(HostString host, [MaybeNullWhen(false)] out DomainConfig domainConfig)
         {
-            if (this.fastGithubConfig.TryGetDomainConfig(host.Host, out domainConfig) == true)
+            if (fastGithubConfig.TryGetDomainConfig(host.Host, out domainConfig) == true)
             {
                 return true;
             }
@@ -82,7 +82,7 @@ namespace FastGithub.HttpServer
             // 未配置的域名,但仍然被解析到本机ip的域名
             if (OperatingSystem.IsWindows() && IsDomain(host.Host))
             {
-                this.logger.LogWarning($"域名{host.Host}可能已经被DNS污染,如果域名为本机域名,请解析为非回环IP");
+                logger.LogWarning($"域名{host.Host}可能已经被DNS污染,如果域名为本机域名,请解析为非回环IP");
                 domainConfig = defaultDomainConfig;
                 return true;
             }
@@ -113,7 +113,7 @@ namespace FastGithub.HttpServer
 
             var baseUri = new Uri(defaultValue);
             var result = new Uri(baseUri, destination).ToString();
-            this.logger.LogInformation($"{defaultValue} => {result}");
+            logger.LogInformation($"{defaultValue} => {result}");
             return result;
         }
 

+ 1 - 1
FastGithub.HttpServer/IRequestLoggingFeature.cs → FastGithub.HttpServer/HttpMiddlewares/IRequestLoggingFeature.cs

@@ -1,4 +1,4 @@
-namespace FastGithub.HttpServer
+namespace FastGithub.HttpServer.HttpMiddlewares
 {
     /// <summary>
     /// 请求日志特性

+ 5 - 5
FastGithub.HttpServer/RequestLoggingMilldeware.cs → FastGithub.HttpServer/HttpMiddlewares/RequestLoggingMilldeware.cs

@@ -6,7 +6,7 @@ using System.Diagnostics;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace FastGithub.HttpServer
+namespace FastGithub.HttpServer.HttpMiddlewares
 {
     /// <summary>
     /// 请求日志中间件
@@ -51,19 +51,19 @@ namespace FastGithub.HttpServer
             }
 
             var request = context.Request;
-            var response = context.Response; 
+            var response = context.Response;
             var exception = context.GetForwarderErrorFeature()?.Exception;
             if (exception == null)
             {
-                this.logger.LogInformation($"{request.Method} {request.Scheme}://{request.Host}{request.Path} responded {response.StatusCode} in {stopwatch.Elapsed.TotalMilliseconds} ms");
+                logger.LogInformation($"{request.Method} {request.Scheme}://{request.Host}{request.Path} responded {response.StatusCode} in {stopwatch.Elapsed.TotalMilliseconds} ms");
             }
             else if (IsError(exception))
             {
-                this.logger.LogError($"{request.Method} {request.Scheme}://{request.Host}{request.Path} responded {response.StatusCode} in {stopwatch.Elapsed.TotalMilliseconds} ms{Environment.NewLine}{exception}");
+                logger.LogError($"{request.Method} {request.Scheme}://{request.Host}{request.Path} responded {response.StatusCode} in {stopwatch.Elapsed.TotalMilliseconds} ms{Environment.NewLine}{exception}");
             }
             else
             {
-                this.logger.LogWarning($"{request.Method} {request.Scheme}://{request.Host}{request.Path} responded {response.StatusCode} in {stopwatch.Elapsed.TotalMilliseconds} ms{Environment.NewLine}{GetMessage(exception)}");
+                logger.LogWarning($"{request.Method} {request.Scheme}://{request.Host}{request.Path} responded {response.StatusCode} in {stopwatch.Elapsed.TotalMilliseconds} ms{Environment.NewLine}{GetMessage(exception)}");
             }
         }
 

+ 0 - 242
FastGithub.HttpServer/HttpProxyMiddleware.cs

@@ -1,242 +0,0 @@
-using FastGithub.Configuration;
-using FastGithub.DomainResolve;
-using Microsoft.AspNetCore.Connections.Features;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Http.Features;
-using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.IO.Pipelines;
-using System.Net;
-using System.Net.Http;
-using System.Net.Sockets;
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using Yarp.ReverseProxy.Forwarder;
-
-namespace FastGithub.HttpServer
-{
-    /// <summary>
-    /// http代理中间件
-    /// </summary>
-    sealed class HttpProxyMiddleware
-    {
-        private const string LOCALHOST = "localhost";
-        private const int HTTP_PORT = 80;
-        private const int HTTPS_PORT = 443;
-
-        private readonly FastGithubConfig fastGithubConfig;
-        private readonly IDomainResolver domainResolver;
-        private readonly IHttpForwarder httpForwarder;
-        private readonly HttpReverseProxyMiddleware httpReverseProxy;
-
-        private readonly HttpMessageInvoker defaultHttpClient;
-        private readonly TimeSpan connectTimeout = TimeSpan.FromSeconds(10d);
-
-        static HttpProxyMiddleware()
-        {
-            // https://github.com/dotnet/aspnetcore/issues/37421
-            var authority = typeof(HttpParser<>).Assembly
-                .GetType("Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.HttpCharacters")?
-                .GetField("_authority", BindingFlags.NonPublic | BindingFlags.Static)?
-                .GetValue(null);
-
-            if (authority is bool[] authorityArray)
-            {
-                authorityArray['-'] = true;
-            }
-        }
-
-        /// <summary>
-        /// http代理中间件
-        /// </summary>
-        /// <param name="fastGithubConfig"></param>
-        /// <param name="domainResolver"></param>
-        /// <param name="httpForwarder"></param>
-        /// <param name="httpReverseProxy"></param>
-        public HttpProxyMiddleware(
-            FastGithubConfig fastGithubConfig,
-            IDomainResolver domainResolver,
-            IHttpForwarder httpForwarder,
-            HttpReverseProxyMiddleware httpReverseProxy)
-        {
-            this.fastGithubConfig = fastGithubConfig;
-            this.domainResolver = domainResolver;
-            this.httpForwarder = httpForwarder;
-            this.httpReverseProxy = httpReverseProxy;
-
-            this.defaultHttpClient = new HttpMessageInvoker(CreateDefaultHttpHandler(), disposeHandler: false);
-        }
-
-        /// <summary>
-        /// 处理请求
-        /// </summary>
-        /// <param name="context"></param>
-        /// <returns></returns>
-        public async Task InvokeAsync(HttpContext context)
-        {
-            var host = context.Request.Host;
-            if (this.IsFastGithubServer(host) == true)
-            {
-                var proxyPac = this.CreateProxyPac(host);
-                context.Response.ContentType = "application/x-ns-proxy-autoconfig";
-                context.Response.Headers.Add("Content-Disposition", $"attachment;filename=proxy.pac");
-                await context.Response.WriteAsync(proxyPac);
-            }
-            else if (context.Request.Method == HttpMethods.Connect)
-            {
-                var cancellationToken = context.RequestAborted;
-                using var connection = await this.CreateConnectionAsync(host, cancellationToken);
-                var responseFeature = context.Features.Get<IHttpResponseFeature>();
-                if (responseFeature != null)
-                {
-                    responseFeature.ReasonPhrase = "Connection Established";
-                }
-                context.Response.StatusCode = StatusCodes.Status200OK;
-                await context.Response.CompleteAsync();
-
-                var transport = context.Features.Get<IConnectionTransportFeature>()?.Transport;
-                if (transport != null)
-                {
-                    var task1 = connection.CopyToAsync(transport.Output, cancellationToken);
-                    var task2 = transport.Input.CopyToAsync(connection, cancellationToken);
-                    await Task.WhenAny(task1, task2);
-                }
-            }
-            else
-            {
-                await this.httpReverseProxy.InvokeAsync(context, async next =>
-                {
-                    var destinationPrefix = $"{context.Request.Scheme}://{context.Request.Host}";
-                    await this.httpForwarder.SendAsync(context, destinationPrefix, this.defaultHttpClient);
-                });
-            }
-        }
-
-        /// <summary>
-        /// 是否为fastgithub服务
-        /// </summary>
-        /// <param name="host"></param>
-        /// <returns></returns>
-        private bool IsFastGithubServer(HostString host)
-        {
-            if (host.Port != this.fastGithubConfig.HttpProxyPort)
-            {
-                return false;
-            }
-
-            if (host.Host == LOCALHOST)
-            {
-                return true;
-            }
-
-            return IPAddress.TryParse(host.Host, out var address) && IPAddress.IsLoopback(address);
-        }
-
-        /// <summary>
-        /// 创建proxypac脚本
-        /// </summary>
-        /// <param name="proxyHost"></param>
-        /// <returns></returns>
-        private string CreateProxyPac(HostString proxyHost)
-        {
-            var buidler = new StringBuilder();
-            buidler.AppendLine("function FindProxyForURL(url, host){");
-            buidler.AppendLine($"    var fastgithub = 'PROXY {proxyHost}';");
-            foreach (var domain in this.fastGithubConfig.GetDomainPatterns())
-            {
-                buidler.AppendLine($"    if (shExpMatch(host, '{domain}')) return fastgithub;");
-            }
-            buidler.AppendLine("    return 'DIRECT';");
-            buidler.AppendLine("}");
-            return buidler.ToString();
-        }
-
-        /// <summary>
-        /// 创建连接
-        /// </summary>
-        /// <param name="host"></param>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        /// <exception cref="AggregateException"></exception>
-        private async Task<Stream> CreateConnectionAsync(HostString host, CancellationToken cancellationToken)
-        {
-            var innerExceptions = new List<Exception>();
-            await foreach (var endPoint in this.GetUpstreamEndPointsAsync(host, cancellationToken))
-            {
-                var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
-                try
-                {
-                    using var timeoutTokenSource = new CancellationTokenSource(this.connectTimeout);
-                    using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token);
-                    await socket.ConnectAsync(endPoint, linkedTokenSource.Token);
-                    return new NetworkStream(socket, ownsSocket: false);
-                }
-                catch (Exception ex)
-                {
-                    socket.Dispose();
-                    cancellationToken.ThrowIfCancellationRequested();
-                    innerExceptions.Add(ex);
-                }
-            }
-            throw new AggregateException($"无法连接到{host}", innerExceptions);
-        }
-
-        /// <summary>
-        /// 获取目标终节点
-        /// </summary>
-        /// <param name="host"></param>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        private async IAsyncEnumerable<EndPoint> GetUpstreamEndPointsAsync(HostString host, [EnumeratorCancellation] CancellationToken cancellationToken)
-        {
-            var targetHost = host.Host;
-            var targetPort = host.Port ?? HTTPS_PORT;
-
-            if (IPAddress.TryParse(targetHost, out var address) == true)
-            {
-                yield return new IPEndPoint(address, targetPort);
-            }
-            else if (this.fastGithubConfig.IsMatch(targetHost) == false)
-            {
-                yield return new DnsEndPoint(targetHost, targetPort);
-            }
-            else if (targetPort == HTTP_PORT)
-            {
-                yield return new IPEndPoint(IPAddress.Loopback, GlobalListener.HttpPort);
-            }
-            else if (targetPort == HTTPS_PORT)
-            {
-                yield return new IPEndPoint(IPAddress.Loopback, GlobalListener.HttpsPort);
-            }
-            else
-            {
-                var dnsEndPoint = new DnsEndPoint(targetHost, targetPort);
-                await foreach (var item in this.domainResolver.ResolveAsync(dnsEndPoint, cancellationToken))
-                {
-                    yield return new IPEndPoint(item, targetPort);
-                }
-            }
-        }
-
-        /// <summary>
-        /// 创建httpHandler
-        /// </summary>
-        /// <returns></returns>
-        private static SocketsHttpHandler CreateDefaultHttpHandler()
-        {
-            return new()
-            {
-                Proxy = null,
-                UseProxy = false,
-                UseCookies = false,
-                AllowAutoRedirect = false,
-                AutomaticDecompression = DecompressionMethods.None
-            };
-        }
-    }
-}

+ 50 - 15
FastGithub.HttpServer/KestrelServerOptionsExtensions.cs → FastGithub.HttpServer/KestrelServerExtensions.cs

@@ -1,8 +1,11 @@
 using FastGithub.Configuration;
-using FastGithub.HttpServer;
+using FastGithub.HttpServer.Certs;
+using FastGithub.HttpServer.TcpMiddlewares;
+using FastGithub.HttpServer.TlsMiddlewares;
 using Microsoft.AspNetCore.Connections;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Server.Kestrel.Core;
+using Microsoft.AspNetCore.Server.Kestrel.Https;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Options;
@@ -13,7 +16,7 @@ namespace FastGithub
     /// <summary>
     /// Kestrel扩展
     /// </summary>
-    public static class KestrelServerOptionsExtensions
+    public static class KestrelServerExtensions
     {
         /// <summary>
         /// 无限制
@@ -40,13 +43,21 @@ namespace FastGithub
                 throw new FastGithubException($"tcp端口{httpProxyPort}已经被其它进程占用,请在配置文件更换{nameof(FastGithubOptions.HttpProxyPort)}为其它端口");
             }
 
-            var logger = kestrel.GetLogger();
-            kestrel.ListenLocalhost(httpProxyPort);
-            logger.LogInformation($"已监听http://localhost:{httpProxyPort},http代理服务启动完成");
+            kestrel.ListenLocalhost(httpProxyPort, listen =>
+            {
+                var proxyMiddleware = kestrel.ApplicationServices.GetRequiredService<HttpProxyMiddleware>();
+                var tunnelMiddleware = kestrel.ApplicationServices.GetRequiredService<TunnelMiddleware>();
+
+                listen.Use(next => context => proxyMiddleware.InvokeAsync(next, context));
+                listen.UseTls();
+                listen.Use(next => context => tunnelMiddleware.InvokeAsync(next, context));
+            });
+
+            kestrel.GetLogger().LogInformation($"已监听http://localhost:{httpProxyPort},http代理服务启动完成");
         }
 
         /// <summary>
-        /// 监听ssh反向代理
+        /// 监听ssh协议代理
         /// </summary>
         /// <param name="kestrel"></param>
         public static void ListenSshReverseProxy(this KestrelServerOptions kestrel)
@@ -62,7 +73,7 @@ namespace FastGithub
         }
 
         /// <summary>
-        /// 监听git反向代理
+        /// 监听git协议代理代理
         /// </summary>
         /// <param name="kestrel"></param>
         public static void ListenGitReverseProxy(this KestrelServerOptions kestrel)
@@ -99,10 +110,6 @@ namespace FastGithub
         /// <exception cref="FastGithubException"></exception>
         public static void ListenHttpsReverseProxy(this KestrelServerOptions kestrel)
         {
-            var certService = kestrel.ApplicationServices.GetRequiredService<CertService>();
-            certService.CreateCaCertIfNotExists();
-            certService.InstallAndTrustCaCert();
-
             var httpsPort = GlobalListener.HttpsPort;
             kestrel.ListenLocalhost(httpsPort, listen =>
             {
@@ -110,10 +117,7 @@ namespace FastGithub
                 {
                     listen.UseFlowAnalyze();
                 }
-                listen.UseHttps(https =>
-                {
-                    https.ServerCertificateSelector = (ctx, domain) => certService.GetOrCreateServerCert(domain);
-                });
+                listen.UseTls();
             });
 
             if (OperatingSystem.IsWindows())
@@ -133,5 +137,36 @@ namespace FastGithub
             var loggerFactory = kestrel.ApplicationServices.GetRequiredService<ILoggerFactory>();
             return loggerFactory.CreateLogger($"{nameof(FastGithub)}.{nameof(HttpServer)}");
         }
+
+        /// <summary>
+        /// 使用Tls中间件
+        /// </summary>
+        /// <param name="listen"></param>
+        /// <param name="configureOptions">https配置</param>
+        /// <returns></returns>
+        public static ListenOptions UseTls(this ListenOptions listen)
+        {
+            var certService = listen.ApplicationServices.GetRequiredService<CertService>();
+            certService.CreateCaCertIfNotExists();
+            certService.InstallAndTrustCaCert();
+            return listen.UseTls(https => https.ServerCertificateSelector = (ctx, domain) => certService.GetOrCreateServerCert(domain));
+        }
+
+        /// <summary>
+        /// 使用Tls中间件
+        /// </summary>
+        /// <param name="listen"></param>
+        /// <param name="configureOptions">https配置</param>
+        /// <returns></returns>
+        private static ListenOptions UseTls(this ListenOptions listen, Action<HttpsConnectionAdapterOptions> configureOptions)
+        {
+            var invadeMiddleware = listen.ApplicationServices.GetRequiredService<TlsInvadeMiddleware>();
+            var restoreMiddleware = listen.ApplicationServices.GetRequiredService<TlsRestoreMiddleware>();
+
+            listen.Use(next => context => invadeMiddleware.InvokeAsync(next, context));
+            listen.UseHttps(configureOptions);
+            listen.Use(next => context => restoreMiddleware.InvokeAsync(next, context));
+            return listen;
+        }
     }
 }

+ 15 - 1
FastGithub.HttpServer/ServiceCollectionExtensions.cs

@@ -1,4 +1,8 @@
-using FastGithub.HttpServer;
+using FastGithub.HttpServer.Certs;
+using FastGithub.HttpServer.Certs.CaCertInstallers;
+using FastGithub.HttpServer.HttpMiddlewares;
+using FastGithub.HttpServer.TcpMiddlewares;
+using FastGithub.HttpServer.TlsMiddlewares;
 using Microsoft.Extensions.DependencyInjection;
 namespace FastGithub
 {
@@ -22,7 +26,17 @@ namespace FastGithub
                 .AddSingleton<ICaCertInstaller, CaCertInstallerOfWindows>()
                 .AddSingleton<ICaCertInstaller, CaCertInstallerOfLinuxRedHat>()
                 .AddSingleton<ICaCertInstaller, CaCertInstallerOfLinuxDebian>()
+
+                // tcp
                 .AddSingleton<HttpProxyMiddleware>()
+                .AddSingleton<TunnelMiddleware>()
+
+                // tls
+                .AddSingleton<TlsInvadeMiddleware>()
+                .AddSingleton<TlsRestoreMiddleware>()
+
+                // http
+                .AddSingleton<HttpProxyPacMiddleware>()
                 .AddSingleton<RequestLoggingMiddleware>()
                 .AddSingleton<HttpReverseProxyMiddleware>();
         }

+ 1 - 1
FastGithub.HttpServer/GithubGitReverseProxyHandler.cs → FastGithub.HttpServer/TcpMiddlewares/GithubGitReverseProxyHandler.cs

@@ -1,6 +1,6 @@
 using FastGithub.DomainResolve;
 
-namespace FastGithub.HttpServer
+namespace FastGithub.HttpServer.TcpMiddlewares
 {
     /// <summary>
     /// github的git代理处理者

+ 1 - 1
FastGithub.HttpServer/GithubSshReverseProxyHandler.cs → FastGithub.HttpServer/TcpMiddlewares/GithubSshReverseProxyHandler.cs

@@ -1,6 +1,6 @@
 using FastGithub.DomainResolve;
 
-namespace FastGithub.HttpServer
+namespace FastGithub.HttpServer.TcpMiddlewares
 {
     /// <summary>
     /// github的ssh代理处理者

+ 135 - 0
FastGithub.HttpServer/TcpMiddlewares/HttpProxyMiddleware.cs

@@ -0,0 +1,135 @@
+using Microsoft.AspNetCore.Connections;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
+using System;
+using System.Buffers;
+using System.IO.Pipelines;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FastGithub.HttpServer.TcpMiddlewares
+{
+    /// <summary>
+    /// 正向代理中间件
+    /// </summary>
+    sealed class HttpProxyMiddleware
+    {
+        private readonly HttpParser<HttpRequestHandler> httpParser = new();
+        private readonly byte[] http200 = Encoding.ASCII.GetBytes("HTTP/1.1 200 Connection Established\r\n\r\n");
+        private readonly byte[] http400 = Encoding.ASCII.GetBytes("HTTP/1.1 400 Bad Request\r\n\r\n");
+
+        /// <summary>
+        /// 执行中间件
+        /// </summary>
+        /// <param name="next"></param>
+        /// <param name="context"></param>
+        /// <returns></returns>
+        public async Task InvokeAsync(ConnectionDelegate next, ConnectionContext context)
+        {
+            var result = await context.Transport.Input.ReadAsync();
+            var httpRequest = this.GetHttpRequestHandler(result, out var consumed);
+
+            // 协议错误
+            if (consumed == 0L)
+            {
+                await context.Transport.Output.WriteAsync(this.http400, context.ConnectionClosed);
+            }
+            else
+            {
+                // 隧道代理连接请求
+                if (httpRequest.ProxyProtocol == ProxyProtocol.TunnelProxy)
+                {
+                    var position = result.Buffer.GetPosition(consumed);
+                    context.Transport.Input.AdvanceTo(position);
+                    await context.Transport.Output.WriteAsync(this.http200, context.ConnectionClosed);
+                }
+                else
+                {
+                    var position = result.Buffer.Start;
+                    context.Transport.Input.AdvanceTo(position);
+                }
+
+                context.Features.Set<IHttpProxyFeature>(httpRequest);
+                await next(context);
+            }
+        }
+
+
+        /// <summary>
+        /// 获取http请求处理者
+        /// </summary>
+        /// <param name="result"></param>
+        /// <param name="consumed"></param>
+        /// <returns></returns>
+        private HttpRequestHandler GetHttpRequestHandler(ReadResult result, out long consumed)
+        {
+            var handler = new HttpRequestHandler();
+            var reader = new SequenceReader<byte>(result.Buffer);
+
+            if (this.httpParser.ParseRequestLine(handler, ref reader) &&
+                this.httpParser.ParseHeaders(handler, ref reader))
+            {
+                consumed = reader.Consumed;
+            }
+            else
+            {
+                consumed = 0L;
+            }
+            return handler;
+        }
+
+
+        /// <summary>
+        /// 代理请求处理器
+        /// </summary>
+        private class HttpRequestHandler : IHttpRequestLineHandler, IHttpHeadersHandler, IHttpProxyFeature
+        {
+            private HttpMethod method;
+
+            public HostString ProxyHost { get; private set; }
+
+            public ProxyProtocol ProxyProtocol
+            {
+                get
+                {
+                    if (this.ProxyHost.HasValue == false)
+                    {
+                        return ProxyProtocol.None;
+                    }
+                    if (this.method == HttpMethod.Connect)
+                    {
+                        return ProxyProtocol.TunnelProxy;
+                    }
+                    return ProxyProtocol.HttpProxy;
+                }
+            }
+
+            void IHttpRequestLineHandler.OnStartLine(HttpVersionAndMethod versionAndMethod, TargetOffsetPathLength targetPath, Span<byte> startLine)
+            {
+                this.method = versionAndMethod.Method;
+                var host = Encoding.ASCII.GetString(startLine.Slice(targetPath.Offset, targetPath.Length));
+                if (versionAndMethod.Method == HttpMethod.Connect)
+                {
+                    this.ProxyHost = HostString.FromUriComponent(host);
+                }
+                else if (Uri.TryCreate(host, UriKind.Absolute, out var uri))
+                {
+                    this.ProxyHost = HostString.FromUriComponent(uri);
+                }
+            }
+
+            void IHttpHeadersHandler.OnHeader(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
+            {
+            }
+            void IHttpHeadersHandler.OnHeadersComplete(bool endStream)
+            {
+            }
+            void IHttpHeadersHandler.OnStaticIndexedHeader(int index)
+            {
+            }
+            void IHttpHeadersHandler.OnStaticIndexedHeader(int index, ReadOnlySpan<byte> value)
+            {
+            }
+        }
+    }
+}

+ 11 - 0
FastGithub.HttpServer/TcpMiddlewares/IHttpProxyFeature.cs

@@ -0,0 +1,11 @@
+using Microsoft.AspNetCore.Http;
+
+namespace FastGithub.HttpServer.TcpMiddlewares
+{
+    interface IHttpProxyFeature
+    {
+        HostString ProxyHost { get; }
+
+        ProxyProtocol ProxyProtocol { get; }
+    }
+}

+ 23 - 0
FastGithub.HttpServer/TcpMiddlewares/ProxyProtocol.cs

@@ -0,0 +1,23 @@
+namespace FastGithub.HttpServer.TcpMiddlewares
+{
+    /// <summary>
+    /// 代理协议
+    /// </summary>
+    enum ProxyProtocol
+    {
+        /// <summary>
+        /// 无代理
+        /// </summary>
+        None,
+
+        /// <summary>
+        /// http代理
+        /// </summary>
+        HttpProxy,
+
+        /// <summary>
+        /// 隧道代理
+        /// </summary>
+        TunnelProxy
+    }
+}

+ 8 - 8
FastGithub.HttpServer/TcpReverseProxyHandler.cs → FastGithub.HttpServer/TcpMiddlewares/TcpReverseProxyHandler.cs

@@ -9,10 +9,10 @@ using System.Net.Sockets;
 using System.Threading;
 using System.Threading.Tasks;
 
-namespace FastGithub.HttpServer
+namespace FastGithub.HttpServer.TcpMiddlewares
 {
     /// <summary>
-    /// tcp反射代理处理者
+    /// tcp协议代理处理者
     /// </summary>
     abstract class TcpReverseProxyHandler : ConnectionHandler
     {
@@ -21,7 +21,7 @@ namespace FastGithub.HttpServer
         private readonly TimeSpan connectTimeout = TimeSpan.FromSeconds(10d);
 
         /// <summary>
-        /// tcp反射代理处理者
+        /// tcp协议代理处理者
         /// </summary>
         /// <param name="domainResolver"></param>
         /// <param name="endPoint"></param>
@@ -39,7 +39,7 @@ namespace FastGithub.HttpServer
         public override async Task OnConnectedAsync(ConnectionContext context)
         {
             var cancellationToken = context.ConnectionClosed;
-            using var connection = await this.CreateConnectionAsync(cancellationToken);
+            using var connection = await CreateConnectionAsync(cancellationToken);
             var task1 = connection.CopyToAsync(context.Transport.Output, cancellationToken);
             var task2 = context.Transport.Input.CopyToAsync(connection, cancellationToken);
             await Task.WhenAny(task1, task2);
@@ -54,14 +54,14 @@ namespace FastGithub.HttpServer
         private async Task<Stream> CreateConnectionAsync(CancellationToken cancellationToken)
         {
             var innerExceptions = new List<Exception>();
-            await foreach (var address in this.domainResolver.ResolveAsync(this.endPoint, cancellationToken))
+            await foreach (var address in domainResolver.ResolveAsync(endPoint, cancellationToken))
             {
                 var socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                 try
                 {
-                    using var timeoutTokenSource = new CancellationTokenSource(this.connectTimeout);
+                    using var timeoutTokenSource = new CancellationTokenSource(connectTimeout);
                     using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token);
-                    await socket.ConnectAsync(address, this.endPoint.Port, linkedTokenSource.Token);
+                    await socket.ConnectAsync(address, endPoint.Port, linkedTokenSource.Token);
                     return new NetworkStream(socket, ownsSocket: false);
                 }
                 catch (Exception ex)
@@ -71,7 +71,7 @@ namespace FastGithub.HttpServer
                     innerExceptions.Add(ex);
                 }
             }
-            throw new AggregateException($"无法连接到{this.endPoint.Host}:{this.endPoint.Port}", innerExceptions);
+            throw new AggregateException($"无法连接到{endPoint.Host}:{endPoint.Port}", innerExceptions);
         }
     }
 }

+ 132 - 0
FastGithub.HttpServer/TcpMiddlewares/TunnelMiddleware.cs

@@ -0,0 +1,132 @@
+using FastGithub.Configuration;
+using FastGithub.DomainResolve;
+using Microsoft.AspNetCore.Connections;
+using Microsoft.AspNetCore.Connections.Features;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Pipelines;
+using System.Net;
+using System.Net.Sockets;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FastGithub.HttpServer.TcpMiddlewares
+{
+    /// <summary>
+    /// 隧道中间件
+    /// </summary>
+    sealed class TunnelMiddleware
+    {
+        private readonly FastGithubConfig fastGithubConfig;
+        private readonly IDomainResolver domainResolver;
+        private readonly TimeSpan connectTimeout = TimeSpan.FromSeconds(10d);
+
+        /// <summary>
+        /// 隧道中间件
+        /// </summary>
+        /// <param name="fastGithubConfig"></param>
+        /// <param name="domainResolver"></param> 
+        public TunnelMiddleware(
+            FastGithubConfig fastGithubConfig,
+            IDomainResolver domainResolver)
+        {
+            this.fastGithubConfig = fastGithubConfig;
+            this.domainResolver = domainResolver;
+        }
+
+        /// <summary>
+        /// 执行中间件
+        /// </summary>
+        /// <param name="next"></param>
+        /// <param name="context"></param>
+        /// <returns></returns>
+        public async Task InvokeAsync(ConnectionDelegate next, ConnectionContext context)
+        {
+            var proxyFeature = context.Features.Get<IHttpProxyFeature>();
+            if (proxyFeature == null || // 非代理
+                proxyFeature.ProxyProtocol != ProxyProtocol.TunnelProxy || //非隧道代理
+                context.Features.Get<ITlsConnectionFeature>() != null) // 经过隧道的https
+            {
+                await next(context);
+            }
+            else
+            {
+                var transport = context.Features.Get<IConnectionTransportFeature>()?.Transport;
+                if (transport != null)
+                {
+                    var cancellationToken = context.ConnectionClosed;
+                    using var connection = await this.CreateConnectionAsync(proxyFeature.ProxyHost, cancellationToken);
+
+                    var task1 = connection.CopyToAsync(transport.Output, cancellationToken);
+                    var task2 = transport.Input.CopyToAsync(connection, cancellationToken);
+                    await Task.WhenAny(task1, task2);
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// 创建连接
+        /// </summary>
+        /// <param name="host"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        /// <exception cref="AggregateException"></exception>
+        private async Task<Stream> CreateConnectionAsync(HostString host, CancellationToken cancellationToken)
+        {
+            var innerExceptions = new List<Exception>();
+            await foreach (var endPoint in this.GetUpstreamEndPointsAsync(host, cancellationToken))
+            {
+                var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
+                try
+                {
+                    using var timeoutTokenSource = new CancellationTokenSource(this.connectTimeout);
+                    using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token);
+                    await socket.ConnectAsync(endPoint, linkedTokenSource.Token);
+                    return new NetworkStream(socket, ownsSocket: true);
+                }
+                catch (Exception ex)
+                {
+                    socket.Dispose();
+                    cancellationToken.ThrowIfCancellationRequested();
+                    innerExceptions.Add(ex);
+                }
+            }
+            throw new AggregateException($"无法连接到{host}", innerExceptions);
+        }
+
+        /// <summary>
+        /// 获取目标终节点
+        /// </summary>
+        /// <param name="host"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        private async IAsyncEnumerable<EndPoint> GetUpstreamEndPointsAsync(HostString host, [EnumeratorCancellation] CancellationToken cancellationToken)
+        {
+            const int HTTPS_PORT = 443;
+            var targetHost = host.Host;
+            var targetPort = host.Port ?? HTTPS_PORT;
+
+            if (IPAddress.TryParse(targetHost, out var address) == true)
+            {
+                yield return new IPEndPoint(address, targetPort);
+            }
+            else if (this.fastGithubConfig.IsMatch(targetHost) == false)
+            {
+                yield return new DnsEndPoint(targetHost, targetPort);
+            }
+            else
+            {
+                var dnsEndPoint = new DnsEndPoint(targetHost, targetPort);
+                await foreach (var item in this.domainResolver.ResolveAsync(dnsEndPoint, cancellationToken))
+                {
+                    yield return new IPEndPoint(item, targetPort);
+                }
+            }
+        }
+    }
+}

+ 27 - 0
FastGithub.HttpServer/TlsMiddlewares/FakeTlsConnectionFeature.cs

@@ -0,0 +1,27 @@
+using Microsoft.AspNetCore.Http.Features;
+using System;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FastGithub.HttpServer.TlsMiddlewares
+{
+    /// <summary>
+    /// 假冒的TlsConnectionFeature
+    /// </summary>
+    sealed class FakeTlsConnectionFeature : ITlsConnectionFeature
+    {
+        public static FakeTlsConnectionFeature Instance { get; } = new FakeTlsConnectionFeature();
+
+        public X509Certificate2? ClientCertificate
+        {
+            get => throw new NotImplementedException();
+            set => throw new NotImplementedException();
+        }
+
+        public Task<X509Certificate2?> GetClientCertificateAsync(CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 64 - 0
FastGithub.HttpServer/TlsMiddlewares/TlsInvadeMiddleware.cs

@@ -0,0 +1,64 @@
+using Microsoft.AspNetCore.Connections;
+using Microsoft.AspNetCore.Http.Features;
+using System.Buffers;
+using System.IO.Pipelines;
+using System.Threading.Tasks;
+
+namespace FastGithub.HttpServer.TlsMiddlewares
+{
+    /// <summary>
+    /// https入侵中间件
+    /// </summary>
+    sealed class TlsInvadeMiddleware
+    {  
+        /// <summary>
+        /// 执行中间件
+        /// </summary>
+        /// <param name="context"></param>
+        /// <returns></returns>
+        public async Task InvokeAsync(ConnectionDelegate next, ConnectionContext context)
+        {
+            // 连接不是tls
+            if (await IsTlsConnectionAsync(context) == false)
+            {
+                // 没有任何tls中间件执行过
+                if (context.Features.Get<ITlsConnectionFeature>() == null)
+                {
+                    // 设置假的ITlsConnectionFeature,迫使https中间件跳过自身的工作
+                    context.Features.Set<ITlsConnectionFeature>(FakeTlsConnectionFeature.Instance);
+                }
+            }
+            await next(context);
+        }
+
+
+        /// <summary>
+        /// 是否为tls协议
+        /// </summary>
+        /// <param name="context"></param>
+        /// <returns></returns>
+        private static async Task<bool> IsTlsConnectionAsync(ConnectionContext context)
+        {
+            try
+            {
+                var result = await context.Transport.Input.ReadAtLeastAsync(2, context.ConnectionClosed);
+                var state = IsTlsProtocol(result);
+                context.Transport.Input.AdvanceTo(result.Buffer.Start);
+                return state;
+            }
+            catch
+            {
+                return false;
+            }
+
+            static bool IsTlsProtocol(ReadResult result)
+            {
+                var reader = new SequenceReader<byte>(result.Buffer);
+                return reader.TryRead(out var firstByte) &&
+                    reader.TryRead(out var nextByte) &&
+                    firstByte == 0x16 &&
+                    nextByte == 0x3;
+            }
+        }
+    }
+}

+ 27 - 0
FastGithub.HttpServer/TlsMiddlewares/TlsRestoreMiddleware.cs

@@ -0,0 +1,27 @@
+using Microsoft.AspNetCore.Connections;
+using Microsoft.AspNetCore.Http.Features;
+using System.Threading.Tasks;
+
+namespace FastGithub.HttpServer.TlsMiddlewares
+{
+    /// <summary>
+    /// https恢复中间件
+    /// </summary>
+    sealed class TlsRestoreMiddleware
+    {
+        /// <summary>
+        /// 执行中间件
+        /// </summary>
+        /// <param name="context"></param>
+        /// <returns></returns>
+        public async Task InvokeAsync(ConnectionDelegate next, ConnectionContext context)
+        {
+            if (context.Features.Get<ITlsConnectionFeature>() == FakeTlsConnectionFeature.Instance)
+            {
+                // 擦除入侵
+                context.Features.Set<ITlsConnectionFeature>(null);
+            }
+            await next(context);
+        }
+    }
+}

+ 2 - 3
FastGithub/Program.cs

@@ -67,11 +67,10 @@ namespace FastGithub
                     webBuilder.UseKestrel(kestrel =>
                     {
                         kestrel.NoLimit();
-                        kestrel.ListenHttpsReverseProxy();
-                        kestrel.ListenHttpReverseProxy();
-
                         if (OperatingSystem.IsWindows())
                         {
+                            kestrel.ListenHttpsReverseProxy();
+                            kestrel.ListenHttpReverseProxy();
                             kestrel.ListenSshReverseProxy();
                             kestrel.ListenGitReverseProxy();
                         }

+ 9 - 18
FastGithub/Startup.cs

@@ -4,7 +4,6 @@ using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Options;
 using System;
 
 namespace FastGithub
@@ -53,26 +52,18 @@ namespace FastGithub
         /// <param name="app"></param>
         public void Configure(IApplicationBuilder app)
         {
-            var httpProxyPort = app.ApplicationServices.GetRequiredService<IOptions<FastGithubOptions>>().Value.HttpProxyPort;
-            app.MapWhen(context => context.Connection.LocalPort == httpProxyPort, appBuilder =>
-            {
-                appBuilder.UseHttpProxy();
-            });
+            app.UseHttpProxyPac();
+            app.UseRequestLogging();
+            app.UseHttpReverseProxy();
 
-            app.MapWhen(context => context.Connection.LocalPort != httpProxyPort, appBuilder =>
+            app.UseRouting();
+            app.DisableRequestLogging();
+            app.UseEndpoints(endpoint =>
             {
-                appBuilder.UseRequestLogging();
-                appBuilder.UseHttpReverseProxy();
-
-                appBuilder.UseRouting();
-                appBuilder.DisableRequestLogging();
-                appBuilder.UseEndpoints(endpoint =>
+                endpoint.MapGet("/flowStatistics", context =>
                 {
-                    endpoint.MapGet("/flowStatistics", context =>
-                    {
-                        var flowStatistics = context.RequestServices.GetRequiredService<IFlowAnalyzer>().GetFlowStatistics();
-                        return context.Response.WriteAsJsonAsync(flowStatistics);
-                    });
+                    var flowStatistics = context.RequestServices.GetRequiredService<IFlowAnalyzer>().GetFlowStatistics();
+                    return context.Response.WriteAsJsonAsync(flowStatistics);
                 });
             });
         }