diff --git a/FastGithub.HttpServer/ApplicationBuilderExtensions.cs b/FastGithub.HttpServer/ApplicationBuilderExtensions.cs
index b52daa5..ada408f 100644
--- a/FastGithub.HttpServer/ApplicationBuilderExtensions.cs
+++ b/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
{
///
- /// 使用http代理中间件
+ /// 使用http代理策略中间件
///
///
///
- public static IApplicationBuilder UseHttpProxy(this IApplicationBuilder app)
+ public static IApplicationBuilder UseHttpProxyPac(this IApplicationBuilder app)
{
- var middleware = app.ApplicationServices.GetRequiredService();
- return app.Use(next => context => middleware.InvokeAsync(context));
+ var middleware = app.ApplicationServices.GetRequiredService();
+ return app.Use(next => context => middleware.InvokeAsync(context, next));
}
///
diff --git a/FastGithub.HttpServer/CaCertInstallerOfLinux.cs b/FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfLinux.cs
similarity index 68%
rename from FastGithub.HttpServer/CaCertInstallerOfLinux.cs
rename to FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfLinux.cs
index 0c5b5a3..d12017c 100644
--- a/FastGithub.HttpServer/CaCertInstallerOfLinux.cs
+++ b/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
///
public bool IsSupported()
{
- return OperatingSystem.IsLinux() && File.Exists(this.CaCertUpdatePath);
+ return OperatingSystem.IsLinux() && File.Exists(CaCertUpdatePath);
}
///
@@ -44,7 +45,7 @@ namespace FastGithub.HttpServer
/// 证书文件路径
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证书异常");
}
}
}
diff --git a/FastGithub.HttpServer/CaCertInstallerOfLinuxDebian.cs b/FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfLinuxDebian.cs
similarity index 88%
rename from FastGithub.HttpServer/CaCertInstallerOfLinuxDebian.cs
rename to FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfLinuxDebian.cs
index 950dd8c..5e74ae6 100644
--- a/FastGithub.HttpServer/CaCertInstallerOfLinuxDebian.cs
+++ b/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
{
diff --git a/FastGithub.HttpServer/CaCertInstallerOfLinuxRedHat.cs b/FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfLinuxRedHat.cs
similarity index 88%
rename from FastGithub.HttpServer/CaCertInstallerOfLinuxRedHat.cs
rename to FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfLinuxRedHat.cs
index d78f4d8..4fad5c4 100644
--- a/FastGithub.HttpServer/CaCertInstallerOfLinuxRedHat.cs
+++ b/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
{
diff --git a/FastGithub.HttpServer/CaCertInstallerOfMacOS.cs b/FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfMacOS.cs
similarity index 82%
rename from FastGithub.HttpServer/CaCertInstallerOfMacOS.cs
rename to FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfMacOS.cs
index 8aeb009..23dcad8 100644
--- a/FastGithub.HttpServer/CaCertInstallerOfMacOS.cs
+++ b/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
/// 证书文件路径
public void Install(string caCertFilePath)
{
- this.logger.LogWarning($"请手动安装CA证书然后设置信任CA证书{caCertFilePath}");
+ logger.LogWarning($"请手动安装CA证书然后设置信任CA证书{caCertFilePath}");
}
}
}
diff --git a/FastGithub.HttpServer/CaCertInstallerOfWindows.cs b/FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfWindows.cs
similarity index 88%
rename from FastGithub.HttpServer/CaCertInstallerOfWindows.cs
rename to FastGithub.HttpServer/Certs/CaCertInstallers/CaCertInstallerOfWindows.cs
index 7b87e2e..72eb956 100644
--- a/FastGithub.HttpServer/CaCertInstallerOfWindows.cs
+++ b/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}到“将所有的证书都放入下列存储”\\“受信任的根证书颁发机构”");
}
}
}
diff --git a/FastGithub.HttpServer/CertGenerator.cs b/FastGithub.HttpServer/Certs/CertGenerator.cs
similarity index 99%
rename from FastGithub.HttpServer/CertGenerator.cs
rename to FastGithub.HttpServer/Certs/CertGenerator.cs
index 4ddd01b..9162680 100644
--- a/FastGithub.HttpServer/CertGenerator.cs
+++ b/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
{
///
/// 证书生成器
diff --git a/FastGithub.HttpServer/CertService.cs b/FastGithub.HttpServer/Certs/CertService.cs
similarity index 86%
rename from FastGithub.HttpServer/CertService.cs
rename to FastGithub.HttpServer/Certs/CertService.cs
index 7aa2c66..0b04d5d 100644
--- a/FastGithub.HttpServer/CertService.cs
+++ b/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
{
///
/// 证书服务
@@ -54,17 +54,17 @@ namespace FastGithub.HttpServer
///
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
///
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);
}
}
diff --git a/FastGithub.HttpServer/ICaCertInstaller.cs b/FastGithub.HttpServer/Certs/ICaCertInstaller.cs
similarity index 91%
rename from FastGithub.HttpServer/ICaCertInstaller.cs
rename to FastGithub.HttpServer/Certs/ICaCertInstaller.cs
index 7e7e6f5..16f8fd6 100644
--- a/FastGithub.HttpServer/ICaCertInstaller.cs
+++ b/FastGithub.HttpServer/Certs/ICaCertInstaller.cs
@@ -1,4 +1,4 @@
-namespace FastGithub.HttpServer
+namespace FastGithub.HttpServer.Certs
{
///
/// CA证书安装器
diff --git a/FastGithub.HttpServer/FastGithub.HttpServer.csproj b/FastGithub.HttpServer/FastGithub.HttpServer.csproj
index 3d21f0d..2d5a9d5 100644
--- a/FastGithub.HttpServer/FastGithub.HttpServer.csproj
+++ b/FastGithub.HttpServer/FastGithub.HttpServer.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/FastGithub.HttpServer/HttpMiddlewares/HttpProxyPacMiddleware.cs b/FastGithub.HttpServer/HttpMiddlewares/HttpProxyPacMiddleware.cs
new file mode 100644
index 0000000..3bf01b4
--- /dev/null
+++ b/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
+{
+ ///
+ /// http代理策略中间件
+ ///
+ sealed class HttpProxyPacMiddleware
+ {
+ private readonly FastGithubConfig fastGithubConfig;
+
+ ///
+ /// http代理策略中间件
+ ///
+ ///
+ public HttpProxyPacMiddleware(FastGithubConfig fastGithubConfig)
+ {
+ this.fastGithubConfig = fastGithubConfig;
+ }
+
+ ///
+ /// 处理请求
+ ///
+ ///
+ ///
+ ///
+ public async Task InvokeAsync(HttpContext context, RequestDelegate next)
+ {
+ // http请求经过了httpProxy中间件
+ var proxyFeature = context.Features.Get();
+ 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);
+ }
+ }
+
+ ///
+ /// 创建proxypac脚本
+ ///
+ ///
+ ///
+ 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();
+ }
+ }
+}
\ No newline at end of file
diff --git a/FastGithub.HttpServer/HttpReverseProxyMiddleware.cs b/FastGithub.HttpServer/HttpMiddlewares/HttpReverseProxyMiddleware.cs
similarity index 88%
rename from FastGithub.HttpServer/HttpReverseProxyMiddleware.cs
rename to FastGithub.HttpServer/HttpMiddlewares/HttpReverseProxyMiddleware.cs
index c37eef0..d39ada9 100644
--- a/FastGithub.HttpServer/HttpReverseProxyMiddleware.cs
+++ b/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
{
///
/// 反向代理中间件
@@ -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
///
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;
}
diff --git a/FastGithub.HttpServer/IRequestLoggingFeature.cs b/FastGithub.HttpServer/HttpMiddlewares/IRequestLoggingFeature.cs
similarity index 81%
rename from FastGithub.HttpServer/IRequestLoggingFeature.cs
rename to FastGithub.HttpServer/HttpMiddlewares/IRequestLoggingFeature.cs
index 6a0a52f..c23e6fe 100644
--- a/FastGithub.HttpServer/IRequestLoggingFeature.cs
+++ b/FastGithub.HttpServer/HttpMiddlewares/IRequestLoggingFeature.cs
@@ -1,4 +1,4 @@
-namespace FastGithub.HttpServer
+namespace FastGithub.HttpServer.HttpMiddlewares
{
///
/// 请求日志特性
diff --git a/FastGithub.HttpServer/RequestLoggingMilldeware.cs b/FastGithub.HttpServer/HttpMiddlewares/RequestLoggingMilldeware.cs
similarity index 83%
rename from FastGithub.HttpServer/RequestLoggingMilldeware.cs
rename to FastGithub.HttpServer/HttpMiddlewares/RequestLoggingMilldeware.cs
index 0450058..9959d8a 100644
--- a/FastGithub.HttpServer/RequestLoggingMilldeware.cs
+++ b/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
{
///
/// 请求日志中间件
@@ -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)}");
}
}
diff --git a/FastGithub.HttpServer/HttpProxyMiddleware.cs b/FastGithub.HttpServer/HttpProxyMiddleware.cs
deleted file mode 100644
index 86cb801..0000000
--- a/FastGithub.HttpServer/HttpProxyMiddleware.cs
+++ /dev/null
@@ -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
-{
- ///
- /// http代理中间件
- ///
- 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;
- }
- }
-
- ///
- /// http代理中间件
- ///
- ///
- ///
- ///
- ///
- 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);
- }
-
- ///
- /// 处理请求
- ///
- ///
- ///
- 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();
- if (responseFeature != null)
- {
- responseFeature.ReasonPhrase = "Connection Established";
- }
- context.Response.StatusCode = StatusCodes.Status200OK;
- await context.Response.CompleteAsync();
-
- var transport = context.Features.Get()?.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);
- });
- }
- }
-
- ///
- /// 是否为fastgithub服务
- ///
- ///
- ///
- 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);
- }
-
- ///
- /// 创建proxypac脚本
- ///
- ///
- ///
- 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();
- }
-
- ///
- /// 创建连接
- ///
- ///
- ///
- ///
- ///
- private async Task CreateConnectionAsync(HostString host, CancellationToken cancellationToken)
- {
- var innerExceptions = new List();
- 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);
- }
-
- ///
- /// 获取目标终节点
- ///
- ///
- ///
- ///
- private async IAsyncEnumerable 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);
- }
- }
- }
-
- ///
- /// 创建httpHandler
- ///
- ///
- private static SocketsHttpHandler CreateDefaultHttpHandler()
- {
- return new()
- {
- Proxy = null,
- UseProxy = false,
- UseCookies = false,
- AllowAutoRedirect = false,
- AutomaticDecompression = DecompressionMethods.None
- };
- }
- }
-}
\ No newline at end of file
diff --git a/FastGithub.HttpServer/KestrelServerOptionsExtensions.cs b/FastGithub.HttpServer/KestrelServerExtensions.cs
similarity index 65%
rename from FastGithub.HttpServer/KestrelServerOptionsExtensions.cs
rename to FastGithub.HttpServer/KestrelServerExtensions.cs
index a9d46c7..ab1f7ed 100644
--- a/FastGithub.HttpServer/KestrelServerOptionsExtensions.cs
+++ b/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
///
/// Kestrel扩展
///
- public static class KestrelServerOptionsExtensions
+ public static class KestrelServerExtensions
{
///
/// 无限制
@@ -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();
+ var tunnelMiddleware = kestrel.ApplicationServices.GetRequiredService();
+
+ 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代理服务启动完成");
}
///
- /// 监听ssh反向代理
+ /// 监听ssh协议代理
///
///
public static void ListenSshReverseProxy(this KestrelServerOptions kestrel)
@@ -62,7 +73,7 @@ namespace FastGithub
}
///
- /// 监听git反向代理
+ /// 监听git协议代理代理
///
///
public static void ListenGitReverseProxy(this KestrelServerOptions kestrel)
@@ -99,10 +110,6 @@ namespace FastGithub
///
public static void ListenHttpsReverseProxy(this KestrelServerOptions kestrel)
{
- var certService = kestrel.ApplicationServices.GetRequiredService();
- 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();
return loggerFactory.CreateLogger($"{nameof(FastGithub)}.{nameof(HttpServer)}");
}
+
+ ///
+ /// 使用Tls中间件
+ ///
+ ///
+ /// https配置
+ ///
+ public static ListenOptions UseTls(this ListenOptions listen)
+ {
+ var certService = listen.ApplicationServices.GetRequiredService();
+ certService.CreateCaCertIfNotExists();
+ certService.InstallAndTrustCaCert();
+ return listen.UseTls(https => https.ServerCertificateSelector = (ctx, domain) => certService.GetOrCreateServerCert(domain));
+ }
+
+ ///
+ /// 使用Tls中间件
+ ///
+ ///
+ /// https配置
+ ///
+ private static ListenOptions UseTls(this ListenOptions listen, Action configureOptions)
+ {
+ var invadeMiddleware = listen.ApplicationServices.GetRequiredService();
+ var restoreMiddleware = listen.ApplicationServices.GetRequiredService();
+
+ listen.Use(next => context => invadeMiddleware.InvokeAsync(next, context));
+ listen.UseHttps(configureOptions);
+ listen.Use(next => context => restoreMiddleware.InvokeAsync(next, context));
+ return listen;
+ }
}
}
diff --git a/FastGithub.HttpServer/ServiceCollectionExtensions.cs b/FastGithub.HttpServer/ServiceCollectionExtensions.cs
index a3ba4d5..162786e 100644
--- a/FastGithub.HttpServer/ServiceCollectionExtensions.cs
+++ b/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()
.AddSingleton()
.AddSingleton()
+
+ // tcp
.AddSingleton()
+ .AddSingleton()
+
+ // tls
+ .AddSingleton()
+ .AddSingleton()
+
+ // http
+ .AddSingleton()
.AddSingleton()
.AddSingleton();
}
diff --git a/FastGithub.HttpServer/GithubGitReverseProxyHandler.cs b/FastGithub.HttpServer/TcpMiddlewares/GithubGitReverseProxyHandler.cs
similarity index 91%
rename from FastGithub.HttpServer/GithubGitReverseProxyHandler.cs
rename to FastGithub.HttpServer/TcpMiddlewares/GithubGitReverseProxyHandler.cs
index 2888afc..f725a55 100644
--- a/FastGithub.HttpServer/GithubGitReverseProxyHandler.cs
+++ b/FastGithub.HttpServer/TcpMiddlewares/GithubGitReverseProxyHandler.cs
@@ -1,6 +1,6 @@
using FastGithub.DomainResolve;
-namespace FastGithub.HttpServer
+namespace FastGithub.HttpServer.TcpMiddlewares
{
///
/// github的git代理处理者
diff --git a/FastGithub.HttpServer/GithubSshReverseProxyHandler.cs b/FastGithub.HttpServer/TcpMiddlewares/GithubSshReverseProxyHandler.cs
similarity index 91%
rename from FastGithub.HttpServer/GithubSshReverseProxyHandler.cs
rename to FastGithub.HttpServer/TcpMiddlewares/GithubSshReverseProxyHandler.cs
index f6325ac..beb80cd 100644
--- a/FastGithub.HttpServer/GithubSshReverseProxyHandler.cs
+++ b/FastGithub.HttpServer/TcpMiddlewares/GithubSshReverseProxyHandler.cs
@@ -1,6 +1,6 @@
using FastGithub.DomainResolve;
-namespace FastGithub.HttpServer
+namespace FastGithub.HttpServer.TcpMiddlewares
{
///
/// github的ssh代理处理者
diff --git a/FastGithub.HttpServer/TcpMiddlewares/HttpProxyMiddleware.cs b/FastGithub.HttpServer/TcpMiddlewares/HttpProxyMiddleware.cs
new file mode 100644
index 0000000..57eb764
--- /dev/null
+++ b/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
+{
+ ///
+ /// 正向代理中间件
+ ///
+ sealed class HttpProxyMiddleware
+ {
+ private readonly HttpParser 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");
+
+ ///
+ /// 执行中间件
+ ///
+ ///
+ ///
+ ///
+ 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(httpRequest);
+ await next(context);
+ }
+ }
+
+
+ ///
+ /// 获取http请求处理者
+ ///
+ ///
+ ///
+ ///
+ private HttpRequestHandler GetHttpRequestHandler(ReadResult result, out long consumed)
+ {
+ var handler = new HttpRequestHandler();
+ var reader = new SequenceReader(result.Buffer);
+
+ if (this.httpParser.ParseRequestLine(handler, ref reader) &&
+ this.httpParser.ParseHeaders(handler, ref reader))
+ {
+ consumed = reader.Consumed;
+ }
+ else
+ {
+ consumed = 0L;
+ }
+ return handler;
+ }
+
+
+ ///
+ /// 代理请求处理器
+ ///
+ 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 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 name, ReadOnlySpan value)
+ {
+ }
+ void IHttpHeadersHandler.OnHeadersComplete(bool endStream)
+ {
+ }
+ void IHttpHeadersHandler.OnStaticIndexedHeader(int index)
+ {
+ }
+ void IHttpHeadersHandler.OnStaticIndexedHeader(int index, ReadOnlySpan value)
+ {
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/FastGithub.HttpServer/TcpMiddlewares/IHttpProxyFeature.cs b/FastGithub.HttpServer/TcpMiddlewares/IHttpProxyFeature.cs
new file mode 100644
index 0000000..1b4e9c6
--- /dev/null
+++ b/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; }
+ }
+}
diff --git a/FastGithub.HttpServer/TcpMiddlewares/ProxyProtocol.cs b/FastGithub.HttpServer/TcpMiddlewares/ProxyProtocol.cs
new file mode 100644
index 0000000..f16268c
--- /dev/null
+++ b/FastGithub.HttpServer/TcpMiddlewares/ProxyProtocol.cs
@@ -0,0 +1,23 @@
+namespace FastGithub.HttpServer.TcpMiddlewares
+{
+ ///
+ /// 代理协议
+ ///
+ enum ProxyProtocol
+ {
+ ///
+ /// 无代理
+ ///
+ None,
+
+ ///
+ /// http代理
+ ///
+ HttpProxy,
+
+ ///
+ /// 隧道代理
+ ///
+ TunnelProxy
+ }
+}
diff --git a/FastGithub.HttpServer/TcpReverseProxyHandler.cs b/FastGithub.HttpServer/TcpMiddlewares/TcpReverseProxyHandler.cs
similarity index 81%
rename from FastGithub.HttpServer/TcpReverseProxyHandler.cs
rename to FastGithub.HttpServer/TcpMiddlewares/TcpReverseProxyHandler.cs
index 1456c99..a3c8d2e 100644
--- a/FastGithub.HttpServer/TcpReverseProxyHandler.cs
+++ b/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
{
///
- /// tcp反射代理处理者
+ /// tcp协议代理处理者
///
abstract class TcpReverseProxyHandler : ConnectionHandler
{
@@ -21,7 +21,7 @@ namespace FastGithub.HttpServer
private readonly TimeSpan connectTimeout = TimeSpan.FromSeconds(10d);
///
- /// tcp反射代理处理者
+ /// tcp协议代理处理者
///
///
///
@@ -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 CreateConnectionAsync(CancellationToken cancellationToken)
{
var innerExceptions = new List();
- 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);
}
}
}
diff --git a/FastGithub.HttpServer/TcpMiddlewares/TunnelMiddleware.cs b/FastGithub.HttpServer/TcpMiddlewares/TunnelMiddleware.cs
new file mode 100644
index 0000000..7f9af9f
--- /dev/null
+++ b/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
+{
+ ///
+ /// 隧道中间件
+ ///
+ sealed class TunnelMiddleware
+ {
+ private readonly FastGithubConfig fastGithubConfig;
+ private readonly IDomainResolver domainResolver;
+ private readonly TimeSpan connectTimeout = TimeSpan.FromSeconds(10d);
+
+ ///
+ /// 隧道中间件
+ ///
+ ///
+ ///
+ public TunnelMiddleware(
+ FastGithubConfig fastGithubConfig,
+ IDomainResolver domainResolver)
+ {
+ this.fastGithubConfig = fastGithubConfig;
+ this.domainResolver = domainResolver;
+ }
+
+ ///
+ /// 执行中间件
+ ///
+ ///
+ ///
+ ///
+ public async Task InvokeAsync(ConnectionDelegate next, ConnectionContext context)
+ {
+ var proxyFeature = context.Features.Get();
+ if (proxyFeature == null || // 非代理
+ proxyFeature.ProxyProtocol != ProxyProtocol.TunnelProxy || //非隧道代理
+ context.Features.Get() != null) // 经过隧道的https
+ {
+ await next(context);
+ }
+ else
+ {
+ var transport = context.Features.Get()?.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);
+ }
+ }
+ }
+
+
+ ///
+ /// 创建连接
+ ///
+ ///
+ ///
+ ///
+ ///
+ private async Task CreateConnectionAsync(HostString host, CancellationToken cancellationToken)
+ {
+ var innerExceptions = new List();
+ 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);
+ }
+
+ ///
+ /// 获取目标终节点
+ ///
+ ///
+ ///
+ ///
+ private async IAsyncEnumerable 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);
+ }
+ }
+ }
+ }
+}
diff --git a/FastGithub.HttpServer/TlsMiddlewares/FakeTlsConnectionFeature.cs b/FastGithub.HttpServer/TlsMiddlewares/FakeTlsConnectionFeature.cs
new file mode 100644
index 0000000..9dcbc89
--- /dev/null
+++ b/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
+{
+ ///
+ /// 假冒的TlsConnectionFeature
+ ///
+ sealed class FakeTlsConnectionFeature : ITlsConnectionFeature
+ {
+ public static FakeTlsConnectionFeature Instance { get; } = new FakeTlsConnectionFeature();
+
+ public X509Certificate2? ClientCertificate
+ {
+ get => throw new NotImplementedException();
+ set => throw new NotImplementedException();
+ }
+
+ public Task GetClientCertificateAsync(CancellationToken cancellationToken)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/FastGithub.HttpServer/TlsMiddlewares/TlsInvadeMiddleware.cs b/FastGithub.HttpServer/TlsMiddlewares/TlsInvadeMiddleware.cs
new file mode 100644
index 0000000..548da17
--- /dev/null
+++ b/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
+{
+ ///
+ /// https入侵中间件
+ ///
+ sealed class TlsInvadeMiddleware
+ {
+ ///
+ /// 执行中间件
+ ///
+ ///
+ ///
+ public async Task InvokeAsync(ConnectionDelegate next, ConnectionContext context)
+ {
+ // 连接不是tls
+ if (await IsTlsConnectionAsync(context) == false)
+ {
+ // 没有任何tls中间件执行过
+ if (context.Features.Get() == null)
+ {
+ // 设置假的ITlsConnectionFeature,迫使https中间件跳过自身的工作
+ context.Features.Set(FakeTlsConnectionFeature.Instance);
+ }
+ }
+ await next(context);
+ }
+
+
+ ///
+ /// 是否为tls协议
+ ///
+ ///
+ ///
+ private static async Task 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(result.Buffer);
+ return reader.TryRead(out var firstByte) &&
+ reader.TryRead(out var nextByte) &&
+ firstByte == 0x16 &&
+ nextByte == 0x3;
+ }
+ }
+ }
+}
diff --git a/FastGithub.HttpServer/TlsMiddlewares/TlsRestoreMiddleware.cs b/FastGithub.HttpServer/TlsMiddlewares/TlsRestoreMiddleware.cs
new file mode 100644
index 0000000..c9df0ff
--- /dev/null
+++ b/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
+{
+ ///
+ /// https恢复中间件
+ ///
+ sealed class TlsRestoreMiddleware
+ {
+ ///
+ /// 执行中间件
+ ///
+ ///
+ ///
+ public async Task InvokeAsync(ConnectionDelegate next, ConnectionContext context)
+ {
+ if (context.Features.Get() == FakeTlsConnectionFeature.Instance)
+ {
+ // 擦除入侵
+ context.Features.Set(null);
+ }
+ await next(context);
+ }
+ }
+}
diff --git a/FastGithub/Program.cs b/FastGithub/Program.cs
index bb94608..2c2129b 100644
--- a/FastGithub/Program.cs
+++ b/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();
}
diff --git a/FastGithub/Startup.cs b/FastGithub/Startup.cs
index c58df1f..1e6b29c 100644
--- a/FastGithub/Startup.cs
+++ b/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
///
public void Configure(IApplicationBuilder app)
{
- var httpProxyPort = app.ApplicationServices.GetRequiredService>().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().GetFlowStatistics();
- return context.Response.WriteAsJsonAsync(flowStatistics);
- });
+ var flowStatistics = context.RequestServices.GetRequiredService().GetFlowStatistics();
+ return context.Response.WriteAsJsonAsync(flowStatistics);
});
});
}