Skip to content

Commit

Permalink
kestrel层实现http代理
Browse files Browse the repository at this point in the history
  • Loading branch information
xljiulang committed Jun 20, 2022
1 parent 27c2bf2 commit 58f79dd
Show file tree
Hide file tree
Showing 29 changed files with 621 additions and 336 deletions.
10 changes: 5 additions & 5 deletions FastGithub.HttpServer/ApplicationBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using FastGithub.HttpServer;
using FastGithub.HttpServer.HttpMiddlewares;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

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

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -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
{
Expand Down Expand Up @@ -35,7 +36,7 @@ public CaCertInstallerOfLinux(ILogger logger)
/// <returns></returns>
public bool IsSupported()
{
return OperatingSystem.IsLinux() && File.Exists(this.CaCertUpdatePath);
return OperatingSystem.IsLinux() && File.Exists(CaCertUpdatePath);
}

/// <summary>
Expand All @@ -44,33 +45,33 @@ public bool IsSupported()
/// <param name="caCertFilePath">证书文件路径</param>
public void Install(string caCertFilePath)
{
var destCertFilePath = Path.Combine(this.CaCertStorePath, Path.GetFileName(caCertFilePath));
var destCertFilePath = Path.Combine(CaCertStorePath, Path.GetFileName(caCertFilePath));
if (File.Exists(destCertFilePath) && File.ReadAllBytes(caCertFilePath).SequenceEqual(File.ReadAllBytes(destCertFilePath)))
{
return;
}

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证书异常");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Microsoft.Extensions.Logging;

namespace FastGithub.HttpServer
namespace FastGithub.HttpServer.Certs.CaCertInstallers
{
sealed class CaCertInstallerOfLinuxDebian : CaCertInstallerOfLinux
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Microsoft.Extensions.Logging;

namespace FastGithub.HttpServer
namespace FastGithub.HttpServer.Certs.CaCertInstallers
{
sealed class CaCertInstallerOfLinuxRedHat : CaCertInstallerOfLinux
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Microsoft.Extensions.Logging;
using System;

namespace FastGithub.HttpServer
namespace FastGithub.HttpServer.Certs.CaCertInstallers
{
sealed class CaCertInstallerOfMacOS : ICaCertInstaller
{
Expand All @@ -27,7 +27,7 @@ public bool IsSupported()
/// <param name="caCertFilePath">证书文件路径</param>
public void Install(string caCertFilePath)
{
this.logger.LogWarning($"请手动安装CA证书然后设置信任CA证书{caCertFilePath}");
logger.LogWarning($"请手动安装CA证书然后设置信任CA证书{caCertFilePath}");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System;
using System.Security.Cryptography.X509Certificates;

namespace FastGithub.HttpServer
namespace FastGithub.HttpServer.Certs.CaCertInstallers
{
sealed class CaCertInstallerOfWindows : ICaCertInstaller
{
Expand Down Expand Up @@ -50,7 +50,7 @@ public void Install(string caCertFilePath)
}
catch (Exception)
{
this.logger.LogWarning($"请手动安装CA证书{caCertFilePath}到“将所有的证书都放入下列存储”\\“受信任的根证书颁发机构”");
logger.LogWarning($"请手动安装CA证书{caCertFilePath}到“将所有的证书都放入下列存储”\\“受信任的根证书颁发机构”");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
using System.Text;
using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2;

namespace FastGithub.HttpServer
namespace FastGithub.HttpServer.Certs
{
/// <summary>
/// 证书生成器
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
using System.Net;
using System.Security.Cryptography.X509Certificates;

namespace FastGithub.HttpServer
namespace FastGithub.HttpServer.Certs
{
/// <summary>
/// 证书服务
Expand Down Expand Up @@ -54,17 +54,17 @@ public CertService(
/// </summary>
public bool CreateCaCertIfNotExists()
{
if (File.Exists(this.CaCerFilePath) && File.Exists(this.CaKeyFilePath))
if (File.Exists(CaCerFilePath) && File.Exists(CaKeyFilePath))
{
return false;
}

File.Delete(this.CaCerFilePath);
File.Delete(this.CaKeyFilePath);
File.Delete(CaCerFilePath);
File.Delete(CaKeyFilePath);

var validFrom = DateTime.Today.AddDays(-1);
var validTo = DateTime.Today.AddYears(10);
CertGenerator.GenerateBySelf(new[] { nameof(FastGithub) }, KEY_SIZE_BITS, validFrom, validTo, this.CaCerFilePath, this.CaKeyFilePath);
CertGenerator.GenerateBySelf(new[] { nameof(FastGithub) }, KEY_SIZE_BITS, validFrom, validTo, CaCerFilePath, CaKeyFilePath);
return true;
}

Expand All @@ -73,14 +73,14 @@ public bool CreateCaCertIfNotExists()
/// </summary>
public void InstallAndTrustCaCert()
{
var installer = this.certInstallers.FirstOrDefault(item => item.IsSupported());
var installer = certInstallers.FirstOrDefault(item => item.IsSupported());
if (installer != null)
{
installer.Install(this.CaCerFilePath);
installer.Install(CaCerFilePath);
}
else
{
this.logger.LogWarning($"请根据你的系统平台手动安装和信任CA证书{this.CaCerFilePath}");
logger.LogWarning($"请根据你的系统平台手动安装和信任CA证书{CaCerFilePath}");
}

GitConfigSslverify(false);
Expand Down Expand Up @@ -119,7 +119,7 @@ public static bool GitConfigSslverify(bool value)
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)
Expand All @@ -129,7 +129,7 @@ X509Certificate2 GetOrCreateCert(ICacheEntry entry)
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);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace FastGithub.HttpServer
namespace FastGithub.HttpServer.Certs
{
/// <summary>
/// CA证书安装器
Expand Down
2 changes: 1 addition & 1 deletion FastGithub.HttpServer/FastGithub.HttpServer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
<PackageReference Include="Yarp.ReverseProxy" Version="1.0.0" />
<PackageReference Include="Yarp.ReverseProxy" Version="1.1.0" />
</ItemGroup>

<ItemGroup>
Expand Down
68 changes: 68 additions & 0 deletions FastGithub.HttpServer/HttpMiddlewares/HttpProxyPacMiddleware.cs
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
using System.Threading.Tasks;
using Yarp.ReverseProxy.Forwarder;

namespace FastGithub.HttpServer
namespace FastGithub.HttpServer.HttpMiddlewares
{
/// <summary>
/// 反向代理中间件
Expand Down Expand Up @@ -43,16 +43,16 @@ public HttpReverseProxyMiddleware(
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);
}
else if (domainConfig.Response == null)
{
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
Expand All @@ -74,15 +74,15 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next)
/// <returns></returns>
private bool TryGetDomainConfig(HostString host, [MaybeNullWhen(false)] out DomainConfig domainConfig)
{
if (this.fastGithubConfig.TryGetDomainConfig(host.Host, out domainConfig) == true)
if (fastGithubConfig.TryGetDomainConfig(host.Host, out domainConfig) == true)
{
return true;
}

// 未配置的域名,但仍然被解析到本机ip的域名
if (OperatingSystem.IsWindows() && IsDomain(host.Host))
{
this.logger.LogWarning($"域名{host.Host}可能已经被DNS污染,如果域名为本机域名,请解析为非回环IP");
logger.LogWarning($"域名{host.Host}可能已经被DNS污染,如果域名为本机域名,请解析为非回环IP");
domainConfig = defaultDomainConfig;
return true;
}
Expand Down Expand Up @@ -113,7 +113,7 @@ private string GetDestinationPrefix(string scheme, HostString host, Uri? destina

var baseUri = new Uri(defaultValue);
var result = new Uri(baseUri, destination).ToString();
this.logger.LogInformation($"{defaultValue} => {result}");
logger.LogInformation($"{defaultValue} => {result}");
return result;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace FastGithub.HttpServer
namespace FastGithub.HttpServer.HttpMiddlewares
{
/// <summary>
/// 请求日志特性
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using System.Text;
using System.Threading.Tasks;

namespace FastGithub.HttpServer
namespace FastGithub.HttpServer.HttpMiddlewares
{
/// <summary>
/// 请求日志中间件
Expand Down Expand Up @@ -51,19 +51,19 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next)
}

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)}");
}
}

Expand Down
Loading

0 comments on commit 58f79dd

Please sign in to comment.