kestrel层实现http代理
This commit is contained in:
parent
27c2bf27bd
commit
58f79ddc19
@ -1,4 +1,4 @@
|
|||||||
using FastGithub.HttpServer;
|
using FastGithub.HttpServer.HttpMiddlewares;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
@ -10,14 +10,14 @@ namespace FastGithub
|
|||||||
public static class ApplicationBuilderExtensions
|
public static class ApplicationBuilderExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 使用http代理中间件
|
/// 使用http代理策略中间件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="app"></param>
|
/// <param name="app"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IApplicationBuilder UseHttpProxy(this IApplicationBuilder app)
|
public static IApplicationBuilder UseHttpProxyPac(this IApplicationBuilder app)
|
||||||
{
|
{
|
||||||
var middleware = app.ApplicationServices.GetRequiredService<HttpProxyMiddleware>();
|
var middleware = app.ApplicationServices.GetRequiredService<HttpProxyPacMiddleware>();
|
||||||
return app.Use(next => context => middleware.InvokeAsync(context));
|
return app.Use(next => context => middleware.InvokeAsync(context, next));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using FastGithub;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace FastGithub.HttpServer
|
namespace FastGithub.HttpServer.Certs.CaCertInstallers
|
||||||
{
|
{
|
||||||
abstract class CaCertInstallerOfLinux : ICaCertInstaller
|
abstract class CaCertInstallerOfLinux : ICaCertInstaller
|
||||||
{
|
{
|
||||||
@ -35,7 +36,7 @@ namespace FastGithub.HttpServer
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public bool IsSupported()
|
public bool IsSupported()
|
||||||
{
|
{
|
||||||
return OperatingSystem.IsLinux() && File.Exists(this.CaCertUpdatePath);
|
return OperatingSystem.IsLinux() && File.Exists(CaCertUpdatePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -44,7 +45,7 @@ namespace FastGithub.HttpServer
|
|||||||
/// <param name="caCertFilePath">证书文件路径</param>
|
/// <param name="caCertFilePath">证书文件路径</param>
|
||||||
public void Install(string caCertFilePath)
|
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)))
|
if (File.Exists(destCertFilePath) && File.ReadAllBytes(caCertFilePath).SequenceEqual(File.ReadAllBytes(destCertFilePath)))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -52,25 +53,25 @@ namespace FastGithub.HttpServer
|
|||||||
|
|
||||||
if (geteuid() != 0)
|
if (geteuid() != 0)
|
||||||
{
|
{
|
||||||
this.logger.LogWarning($"无法自动安装CA证书{caCertFilePath}:没有root权限");
|
logger.LogWarning($"无法自动安装CA证书{caCertFilePath}:没有root权限");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(this.CaCertStorePath);
|
Directory.CreateDirectory(CaCertStorePath);
|
||||||
foreach (var item in Directory.GetFiles(this.CaCertStorePath, "fastgithub.*"))
|
foreach (var item in Directory.GetFiles(CaCertStorePath, "fastgithub.*"))
|
||||||
{
|
{
|
||||||
File.Delete(item);
|
File.Delete(item);
|
||||||
}
|
}
|
||||||
File.Copy(caCertFilePath, destCertFilePath, overwrite: true);
|
File.Copy(caCertFilePath, destCertFilePath, overwrite: true);
|
||||||
Process.Start(this.CaCertUpdatePath).WaitForExit();
|
Process.Start(CaCertUpdatePath).WaitForExit();
|
||||||
this.logger.LogInformation($"已自动向系统安装CA证书{caCertFilePath}");
|
logger.LogInformation($"已自动向系统安装CA证书{caCertFilePath}");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
File.Delete(destCertFilePath);
|
File.Delete(destCertFilePath);
|
||||||
this.logger.LogWarning(ex.Message, "自动安装CA证书异常");
|
logger.LogWarning(ex.Message, "自动安装CA证书异常");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace FastGithub.HttpServer
|
namespace FastGithub.HttpServer.Certs.CaCertInstallers
|
||||||
{
|
{
|
||||||
sealed class CaCertInstallerOfLinuxDebian : CaCertInstallerOfLinux
|
sealed class CaCertInstallerOfLinuxDebian : CaCertInstallerOfLinux
|
||||||
{
|
{
|
||||||
@ -1,6 +1,6 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace FastGithub.HttpServer
|
namespace FastGithub.HttpServer.Certs.CaCertInstallers
|
||||||
{
|
{
|
||||||
sealed class CaCertInstallerOfLinuxRedHat : CaCertInstallerOfLinux
|
sealed class CaCertInstallerOfLinuxRedHat : CaCertInstallerOfLinux
|
||||||
{
|
{
|
||||||
@ -1,7 +1,7 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace FastGithub.HttpServer
|
namespace FastGithub.HttpServer.Certs.CaCertInstallers
|
||||||
{
|
{
|
||||||
sealed class CaCertInstallerOfMacOS : ICaCertInstaller
|
sealed class CaCertInstallerOfMacOS : ICaCertInstaller
|
||||||
{
|
{
|
||||||
@ -27,7 +27,7 @@ namespace FastGithub.HttpServer
|
|||||||
/// <param name="caCertFilePath">证书文件路径</param>
|
/// <param name="caCertFilePath">证书文件路径</param>
|
||||||
public void Install(string caCertFilePath)
|
public void Install(string caCertFilePath)
|
||||||
{
|
{
|
||||||
this.logger.LogWarning($"请手动安装CA证书然后设置信任CA证书{caCertFilePath}");
|
logger.LogWarning($"请手动安装CA证书然后设置信任CA证书{caCertFilePath}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,7 +2,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
namespace FastGithub.HttpServer
|
namespace FastGithub.HttpServer.Certs.CaCertInstallers
|
||||||
{
|
{
|
||||||
sealed class CaCertInstallerOfWindows : ICaCertInstaller
|
sealed class CaCertInstallerOfWindows : ICaCertInstaller
|
||||||
{
|
{
|
||||||
@ -50,7 +50,7 @@ namespace FastGithub.HttpServer
|
|||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
this.logger.LogWarning($"请手动安装CA证书{caCertFilePath}到“将所有的证书都放入下列存储”\\“受信任的根证书颁发机构”");
|
logger.LogWarning($"请手动安装CA证书{caCertFilePath}到“将所有的证书都放入下列存储”\\“受信任的根证书颁发机构”");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -19,7 +19,7 @@ using System.Net;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2;
|
using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2;
|
||||||
|
|
||||||
namespace FastGithub.HttpServer
|
namespace FastGithub.HttpServer.Certs
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 证书生成器
|
/// 证书生成器
|
||||||
@ -8,7 +8,7 @@ using System.Linq;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
namespace FastGithub.HttpServer
|
namespace FastGithub.HttpServer.Certs
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 证书服务
|
/// 证书服务
|
||||||
@ -54,17 +54,17 @@ namespace FastGithub.HttpServer
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool CreateCaCertIfNotExists()
|
public bool CreateCaCertIfNotExists()
|
||||||
{
|
{
|
||||||
if (File.Exists(this.CaCerFilePath) && File.Exists(this.CaKeyFilePath))
|
if (File.Exists(CaCerFilePath) && File.Exists(CaKeyFilePath))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
File.Delete(this.CaCerFilePath);
|
File.Delete(CaCerFilePath);
|
||||||
File.Delete(this.CaKeyFilePath);
|
File.Delete(CaKeyFilePath);
|
||||||
|
|
||||||
var validFrom = DateTime.Today.AddDays(-1);
|
var validFrom = DateTime.Today.AddDays(-1);
|
||||||
var validTo = DateTime.Today.AddYears(10);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,14 +73,14 @@ namespace FastGithub.HttpServer
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void InstallAndTrustCaCert()
|
public void InstallAndTrustCaCert()
|
||||||
{
|
{
|
||||||
var installer = this.certInstallers.FirstOrDefault(item => item.IsSupported());
|
var installer = certInstallers.FirstOrDefault(item => item.IsSupported());
|
||||||
if (installer != null)
|
if (installer != null)
|
||||||
{
|
{
|
||||||
installer.Install(this.CaCerFilePath);
|
installer.Install(CaCerFilePath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.logger.LogWarning($"请根据你的系统平台手动安装和信任CA证书{this.CaCerFilePath}");
|
logger.LogWarning($"请根据你的系统平台手动安装和信任CA证书{CaCerFilePath}");
|
||||||
}
|
}
|
||||||
|
|
||||||
GitConfigSslverify(false);
|
GitConfigSslverify(false);
|
||||||
@ -119,7 +119,7 @@ namespace FastGithub.HttpServer
|
|||||||
public X509Certificate2 GetOrCreateServerCert(string? domain)
|
public X509Certificate2 GetOrCreateServerCert(string? domain)
|
||||||
{
|
{
|
||||||
var key = $"{nameof(CertService)}:{domain}";
|
var key = $"{nameof(CertService)}:{domain}";
|
||||||
return this.serverCertCache.GetOrCreate(key, GetOrCreateCert);
|
return serverCertCache.GetOrCreate(key, GetOrCreateCert);
|
||||||
|
|
||||||
// 生成域名的1年证书
|
// 生成域名的1年证书
|
||||||
X509Certificate2 GetOrCreateCert(ICacheEntry entry)
|
X509Certificate2 GetOrCreateCert(ICacheEntry entry)
|
||||||
@ -129,7 +129,7 @@ namespace FastGithub.HttpServer
|
|||||||
var validTo = DateTime.Today.AddYears(1);
|
var validTo = DateTime.Today.AddYears(1);
|
||||||
|
|
||||||
entry.SetAbsoluteExpiration(validTo);
|
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,4 +1,4 @@
|
|||||||
namespace FastGithub.HttpServer
|
namespace FastGithub.HttpServer.Certs
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// CA证书安装器
|
/// CA证书安装器
|
||||||
@ -7,7 +7,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
|
<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>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,7 +8,7 @@ using System.Net;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Yarp.ReverseProxy.Forwarder;
|
using Yarp.ReverseProxy.Forwarder;
|
||||||
|
|
||||||
namespace FastGithub.HttpServer
|
namespace FastGithub.HttpServer.HttpMiddlewares
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 反向代理中间件
|
/// 反向代理中间件
|
||||||
@ -43,7 +43,7 @@ namespace FastGithub.HttpServer
|
|||||||
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
|
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
|
||||||
{
|
{
|
||||||
var host = context.Request.Host;
|
var host = context.Request.Host;
|
||||||
if (this.TryGetDomainConfig(host, out var domainConfig) == false)
|
if (TryGetDomainConfig(host, out var domainConfig) == false)
|
||||||
{
|
{
|
||||||
await next(context);
|
await next(context);
|
||||||
}
|
}
|
||||||
@ -51,8 +51,8 @@ namespace FastGithub.HttpServer
|
|||||||
{
|
{
|
||||||
var scheme = context.Request.Scheme;
|
var scheme = context.Request.Scheme;
|
||||||
var destinationPrefix = GetDestinationPrefix(scheme, host, domainConfig.Destination);
|
var destinationPrefix = GetDestinationPrefix(scheme, host, domainConfig.Destination);
|
||||||
var httpClient = this.httpClientFactory.CreateHttpClient(host.Host, domainConfig);
|
var httpClient = httpClientFactory.CreateHttpClient(host.Host, domainConfig);
|
||||||
var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient);
|
var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient, ForwarderRequestConfig.Empty, HttpTransformer.Empty);
|
||||||
await HandleErrorAsync(context, error);
|
await HandleErrorAsync(context, error);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -74,7 +74,7 @@ namespace FastGithub.HttpServer
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private bool TryGetDomainConfig(HostString host, [MaybeNullWhen(false)] out DomainConfig domainConfig)
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
@ -82,7 +82,7 @@ namespace FastGithub.HttpServer
|
|||||||
// 未配置的域名,但仍然被解析到本机ip的域名
|
// 未配置的域名,但仍然被解析到本机ip的域名
|
||||||
if (OperatingSystem.IsWindows() && IsDomain(host.Host))
|
if (OperatingSystem.IsWindows() && IsDomain(host.Host))
|
||||||
{
|
{
|
||||||
this.logger.LogWarning($"域名{host.Host}可能已经被DNS污染,如果域名为本机域名,请解析为非回环IP");
|
logger.LogWarning($"域名{host.Host}可能已经被DNS污染,如果域名为本机域名,请解析为非回环IP");
|
||||||
domainConfig = defaultDomainConfig;
|
domainConfig = defaultDomainConfig;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -113,7 +113,7 @@ namespace FastGithub.HttpServer
|
|||||||
|
|
||||||
var baseUri = new Uri(defaultValue);
|
var baseUri = new Uri(defaultValue);
|
||||||
var result = new Uri(baseUri, destination).ToString();
|
var result = new Uri(baseUri, destination).ToString();
|
||||||
this.logger.LogInformation($"{defaultValue} => {result}");
|
logger.LogInformation($"{defaultValue} => {result}");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
namespace FastGithub.HttpServer
|
namespace FastGithub.HttpServer.HttpMiddlewares
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求日志特性
|
/// 请求日志特性
|
||||||
@ -6,7 +6,7 @@ using System.Diagnostics;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace FastGithub.HttpServer
|
namespace FastGithub.HttpServer.HttpMiddlewares
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求日志中间件
|
/// 请求日志中间件
|
||||||
@ -55,15 +55,15 @@ namespace FastGithub.HttpServer
|
|||||||
var exception = context.GetForwarderErrorFeature()?.Exception;
|
var exception = context.GetForwarderErrorFeature()?.Exception;
|
||||||
if (exception == null)
|
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))
|
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
|
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)}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,8 +1,11 @@
|
|||||||
using FastGithub.Configuration;
|
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.Connections;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
@ -13,7 +16,7 @@ namespace FastGithub
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Kestrel扩展
|
/// Kestrel扩展
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class KestrelServerOptionsExtensions
|
public static class KestrelServerExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 无限制
|
/// 无限制
|
||||||
@ -40,13 +43,21 @@ namespace FastGithub
|
|||||||
throw new FastGithubException($"tcp端口{httpProxyPort}已经被其它进程占用,请在配置文件更换{nameof(FastGithubOptions.HttpProxyPort)}为其它端口");
|
throw new FastGithubException($"tcp端口{httpProxyPort}已经被其它进程占用,请在配置文件更换{nameof(FastGithubOptions.HttpProxyPort)}为其它端口");
|
||||||
}
|
}
|
||||||
|
|
||||||
var logger = kestrel.GetLogger();
|
kestrel.ListenLocalhost(httpProxyPort, listen =>
|
||||||
kestrel.ListenLocalhost(httpProxyPort);
|
{
|
||||||
logger.LogInformation($"已监听http://localhost:{httpProxyPort},http代理服务启动完成");
|
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>
|
/// <summary>
|
||||||
/// 监听ssh反向代理
|
/// 监听ssh协议代理
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="kestrel"></param>
|
/// <param name="kestrel"></param>
|
||||||
public static void ListenSshReverseProxy(this KestrelServerOptions kestrel)
|
public static void ListenSshReverseProxy(this KestrelServerOptions kestrel)
|
||||||
@ -62,7 +73,7 @@ namespace FastGithub
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 监听git反向代理
|
/// 监听git协议代理代理
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="kestrel"></param>
|
/// <param name="kestrel"></param>
|
||||||
public static void ListenGitReverseProxy(this KestrelServerOptions kestrel)
|
public static void ListenGitReverseProxy(this KestrelServerOptions kestrel)
|
||||||
@ -99,10 +110,6 @@ namespace FastGithub
|
|||||||
/// <exception cref="FastGithubException"></exception>
|
/// <exception cref="FastGithubException"></exception>
|
||||||
public static void ListenHttpsReverseProxy(this KestrelServerOptions kestrel)
|
public static void ListenHttpsReverseProxy(this KestrelServerOptions kestrel)
|
||||||
{
|
{
|
||||||
var certService = kestrel.ApplicationServices.GetRequiredService<CertService>();
|
|
||||||
certService.CreateCaCertIfNotExists();
|
|
||||||
certService.InstallAndTrustCaCert();
|
|
||||||
|
|
||||||
var httpsPort = GlobalListener.HttpsPort;
|
var httpsPort = GlobalListener.HttpsPort;
|
||||||
kestrel.ListenLocalhost(httpsPort, listen =>
|
kestrel.ListenLocalhost(httpsPort, listen =>
|
||||||
{
|
{
|
||||||
@ -110,10 +117,7 @@ namespace FastGithub
|
|||||||
{
|
{
|
||||||
listen.UseFlowAnalyze();
|
listen.UseFlowAnalyze();
|
||||||
}
|
}
|
||||||
listen.UseHttps(https =>
|
listen.UseTls();
|
||||||
{
|
|
||||||
https.ServerCertificateSelector = (ctx, domain) => certService.GetOrCreateServerCert(domain);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
@ -133,5 +137,36 @@ namespace FastGithub
|
|||||||
var loggerFactory = kestrel.ApplicationServices.GetRequiredService<ILoggerFactory>();
|
var loggerFactory = kestrel.ApplicationServices.GetRequiredService<ILoggerFactory>();
|
||||||
return loggerFactory.CreateLogger($"{nameof(FastGithub)}.{nameof(HttpServer)}");
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
namespace FastGithub
|
namespace FastGithub
|
||||||
{
|
{
|
||||||
@ -22,7 +26,17 @@ namespace FastGithub
|
|||||||
.AddSingleton<ICaCertInstaller, CaCertInstallerOfWindows>()
|
.AddSingleton<ICaCertInstaller, CaCertInstallerOfWindows>()
|
||||||
.AddSingleton<ICaCertInstaller, CaCertInstallerOfLinuxRedHat>()
|
.AddSingleton<ICaCertInstaller, CaCertInstallerOfLinuxRedHat>()
|
||||||
.AddSingleton<ICaCertInstaller, CaCertInstallerOfLinuxDebian>()
|
.AddSingleton<ICaCertInstaller, CaCertInstallerOfLinuxDebian>()
|
||||||
|
|
||||||
|
// tcp
|
||||||
.AddSingleton<HttpProxyMiddleware>()
|
.AddSingleton<HttpProxyMiddleware>()
|
||||||
|
.AddSingleton<TunnelMiddleware>()
|
||||||
|
|
||||||
|
// tls
|
||||||
|
.AddSingleton<TlsInvadeMiddleware>()
|
||||||
|
.AddSingleton<TlsRestoreMiddleware>()
|
||||||
|
|
||||||
|
// http
|
||||||
|
.AddSingleton<HttpProxyPacMiddleware>()
|
||||||
.AddSingleton<RequestLoggingMiddleware>()
|
.AddSingleton<RequestLoggingMiddleware>()
|
||||||
.AddSingleton<HttpReverseProxyMiddleware>();
|
.AddSingleton<HttpReverseProxyMiddleware>();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
using FastGithub.DomainResolve;
|
using FastGithub.DomainResolve;
|
||||||
|
|
||||||
namespace FastGithub.HttpServer
|
namespace FastGithub.HttpServer.TcpMiddlewares
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// github的git代理处理者
|
/// github的git代理处理者
|
||||||
@ -1,6 +1,6 @@
|
|||||||
using FastGithub.DomainResolve;
|
using FastGithub.DomainResolve;
|
||||||
|
|
||||||
namespace FastGithub.HttpServer
|
namespace FastGithub.HttpServer.TcpMiddlewares
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// github的ssh代理处理者
|
/// github的ssh代理处理者
|
||||||
135
FastGithub.HttpServer/TcpMiddlewares/HttpProxyMiddleware.cs
Normal file
135
FastGithub.HttpServer/TcpMiddlewares/HttpProxyMiddleware.cs
Normal file
@ -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
FastGithub.HttpServer/TcpMiddlewares/IHttpProxyFeature.cs
Normal file
11
FastGithub.HttpServer/TcpMiddlewares/IHttpProxyFeature.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace FastGithub.HttpServer.TcpMiddlewares
|
||||||
|
{
|
||||||
|
interface IHttpProxyFeature
|
||||||
|
{
|
||||||
|
HostString ProxyHost { get; }
|
||||||
|
|
||||||
|
ProxyProtocol ProxyProtocol { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
23
FastGithub.HttpServer/TcpMiddlewares/ProxyProtocol.cs
Normal file
23
FastGithub.HttpServer/TcpMiddlewares/ProxyProtocol.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
namespace FastGithub.HttpServer.TcpMiddlewares
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 代理协议
|
||||||
|
/// </summary>
|
||||||
|
enum ProxyProtocol
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 无代理
|
||||||
|
/// </summary>
|
||||||
|
None,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// http代理
|
||||||
|
/// </summary>
|
||||||
|
HttpProxy,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 隧道代理
|
||||||
|
/// </summary>
|
||||||
|
TunnelProxy
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,10 +9,10 @@ using System.Net.Sockets;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace FastGithub.HttpServer
|
namespace FastGithub.HttpServer.TcpMiddlewares
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// tcp反射代理处理者
|
/// tcp协议代理处理者
|
||||||
/// </summary>
|
/// </summary>
|
||||||
abstract class TcpReverseProxyHandler : ConnectionHandler
|
abstract class TcpReverseProxyHandler : ConnectionHandler
|
||||||
{
|
{
|
||||||
@ -21,7 +21,7 @@ namespace FastGithub.HttpServer
|
|||||||
private readonly TimeSpan connectTimeout = TimeSpan.FromSeconds(10d);
|
private readonly TimeSpan connectTimeout = TimeSpan.FromSeconds(10d);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// tcp反射代理处理者
|
/// tcp协议代理处理者
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="domainResolver"></param>
|
/// <param name="domainResolver"></param>
|
||||||
/// <param name="endPoint"></param>
|
/// <param name="endPoint"></param>
|
||||||
@ -39,7 +39,7 @@ namespace FastGithub.HttpServer
|
|||||||
public override async Task OnConnectedAsync(ConnectionContext context)
|
public override async Task OnConnectedAsync(ConnectionContext context)
|
||||||
{
|
{
|
||||||
var cancellationToken = context.ConnectionClosed;
|
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 task1 = connection.CopyToAsync(context.Transport.Output, cancellationToken);
|
||||||
var task2 = context.Transport.Input.CopyToAsync(connection, cancellationToken);
|
var task2 = context.Transport.Input.CopyToAsync(connection, cancellationToken);
|
||||||
await Task.WhenAny(task1, task2);
|
await Task.WhenAny(task1, task2);
|
||||||
@ -54,14 +54,14 @@ namespace FastGithub.HttpServer
|
|||||||
private async Task<Stream> CreateConnectionAsync(CancellationToken cancellationToken)
|
private async Task<Stream> CreateConnectionAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var innerExceptions = new List<Exception>();
|
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);
|
var socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var timeoutTokenSource = new CancellationTokenSource(this.connectTimeout);
|
using var timeoutTokenSource = new CancellationTokenSource(connectTimeout);
|
||||||
using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token);
|
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);
|
return new NetworkStream(socket, ownsSocket: false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -71,7 +71,7 @@ namespace FastGithub.HttpServer
|
|||||||
innerExceptions.Add(ex);
|
innerExceptions.Add(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new AggregateException($"无法连接到{this.endPoint.Host}:{this.endPoint.Port}", innerExceptions);
|
throw new AggregateException($"无法连接到{endPoint.Host}:{endPoint.Port}", innerExceptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
132
FastGithub.HttpServer/TcpMiddlewares/TunnelMiddleware.cs
Normal file
132
FastGithub.HttpServer/TcpMiddlewares/TunnelMiddleware.cs
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
FastGithub.HttpServer/TlsMiddlewares/TlsInvadeMiddleware.cs
Normal file
64
FastGithub.HttpServer/TlsMiddlewares/TlsInvadeMiddleware.cs
Normal file
@ -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
FastGithub.HttpServer/TlsMiddlewares/TlsRestoreMiddleware.cs
Normal file
27
FastGithub.HttpServer/TlsMiddlewares/TlsRestoreMiddleware.cs
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -67,11 +67,10 @@ namespace FastGithub
|
|||||||
webBuilder.UseKestrel(kestrel =>
|
webBuilder.UseKestrel(kestrel =>
|
||||||
{
|
{
|
||||||
kestrel.NoLimit();
|
kestrel.NoLimit();
|
||||||
kestrel.ListenHttpsReverseProxy();
|
|
||||||
kestrel.ListenHttpReverseProxy();
|
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
|
kestrel.ListenHttpsReverseProxy();
|
||||||
|
kestrel.ListenHttpReverseProxy();
|
||||||
kestrel.ListenSshReverseProxy();
|
kestrel.ListenSshReverseProxy();
|
||||||
kestrel.ListenGitReverseProxy();
|
kestrel.ListenGitReverseProxy();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,6 @@ using Microsoft.AspNetCore.Builder;
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace FastGithub
|
namespace FastGithub
|
||||||
@ -53,20 +52,13 @@ namespace FastGithub
|
|||||||
/// <param name="app"></param>
|
/// <param name="app"></param>
|
||||||
public void Configure(IApplicationBuilder app)
|
public void Configure(IApplicationBuilder app)
|
||||||
{
|
{
|
||||||
var httpProxyPort = app.ApplicationServices.GetRequiredService<IOptions<FastGithubOptions>>().Value.HttpProxyPort;
|
app.UseHttpProxyPac();
|
||||||
app.MapWhen(context => context.Connection.LocalPort == httpProxyPort, appBuilder =>
|
app.UseRequestLogging();
|
||||||
{
|
app.UseHttpReverseProxy();
|
||||||
appBuilder.UseHttpProxy();
|
|
||||||
});
|
|
||||||
|
|
||||||
app.MapWhen(context => context.Connection.LocalPort != httpProxyPort, appBuilder =>
|
app.UseRouting();
|
||||||
{
|
app.DisableRequestLogging();
|
||||||
appBuilder.UseRequestLogging();
|
app.UseEndpoints(endpoint =>
|
||||||
appBuilder.UseHttpReverseProxy();
|
|
||||||
|
|
||||||
appBuilder.UseRouting();
|
|
||||||
appBuilder.DisableRequestLogging();
|
|
||||||
appBuilder.UseEndpoints(endpoint =>
|
|
||||||
{
|
{
|
||||||
endpoint.MapGet("/flowStatistics", context =>
|
endpoint.MapGet("/flowStatistics", context =>
|
||||||
{
|
{
|
||||||
@ -74,7 +66,6 @@ namespace FastGithub
|
|||||||
return context.Response.WriteAsJsonAsync(flowStatistics);
|
return context.Response.WriteAsJsonAsync(flowStatistics);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user