域名隔离

This commit is contained in:
老九 2021-10-03 15:57:40 +08:00
parent 3f01444f2d
commit 1163fa4678
6 changed files with 72 additions and 47 deletions

View File

@ -20,7 +20,7 @@ namespace FastGithub.Http
/// <summary> /// <summary>
/// 非首次生命周期 /// 非首次生命周期
/// </summary> /// </summary>
private readonly TimeSpan nextLifeTime = TimeSpan.FromMinutes(1d); private readonly TimeSpan nextLifeTime = TimeSpan.FromSeconds(100d);
/// <summary> /// <summary>
/// LifetimeHttpHandler清理器 /// LifetimeHttpHandler清理器
@ -30,7 +30,7 @@ namespace FastGithub.Http
/// <summary> /// <summary>
/// LazyOf(LifetimeHttpHandler)缓存 /// LazyOf(LifetimeHttpHandler)缓存
/// </summary> /// </summary>
private readonly ConcurrentDictionary<DomainConfig, Lazy<LifetimeHttpHandler>> httpHandlerLazyCache = new(); private readonly ConcurrentDictionary<LifeTimeKey, Lazy<LifetimeHttpHandler>> httpHandlerLazyCache = new();
/// <summary> /// <summary>
@ -45,20 +45,31 @@ namespace FastGithub.Http
/// <summary> /// <summary>
/// 创建httpClient /// 创建httpClient
/// </summary> /// </summary>
/// <param name="domain"></param>
/// <param name="domainConfig"></param> /// <param name="domainConfig"></param>
/// <returns></returns> /// <returns></returns>
public HttpClient CreateHttpClient(DomainConfig domainConfig) public HttpClient CreateHttpClient(string domain, DomainConfig domainConfig)
{ {
var lifetimeHttpHandlerLazy = this.httpHandlerLazyCache.GetOrAdd(domainConfig, CreateLifetimeHttpHandlerLazy); var lifeTimeKey = new LifeTimeKey(domain, domainConfig);
var lifetimeHttpHandler = lifetimeHttpHandlerLazy.Value; var lifetimeHttpHandler = this.httpHandlerLazyCache.GetOrAdd(lifeTimeKey, CreateLifetimeHttpHandlerLazy).Value;
return new HttpClient(lifetimeHttpHandler, disposeHandler: false); return new HttpClient(lifetimeHttpHandler, disposeHandler: false);
Lazy<LifetimeHttpHandler> CreateLifetimeHttpHandlerLazy(DomainConfig domainConfig) Lazy<LifetimeHttpHandler> CreateLifetimeHttpHandlerLazy(LifeTimeKey lifeTimeKey)
{ {
return new Lazy<LifetimeHttpHandler>(() => this.CreateLifetimeHttpHandler(domainConfig, this.firstLiftTime), true); return new Lazy<LifetimeHttpHandler>(() => this.CreateLifetimeHttpHandler(lifeTimeKey, this.firstLiftTime), true);
} }
} }
/// <summary>
/// 创建LifetimeHttpHandler
/// </summary>
/// <param name="lifeTimeKey"></param>
/// <param name="lifeTime"></param>
/// <returns></returns>
private LifetimeHttpHandler CreateLifetimeHttpHandler(LifeTimeKey lifeTimeKey, TimeSpan lifeTime)
{
return new LifetimeHttpHandler(this.domainResolver, lifeTimeKey, lifeTime, this.OnLifetimeHttpHandlerDeactivate);
}
/// <summary> /// <summary>
/// 当有httpHandler失效时 /// 当有httpHandler失效时
@ -66,26 +77,14 @@ namespace FastGithub.Http
/// <param name="lifetimeHttpHandler">httpHandler</param> /// <param name="lifetimeHttpHandler">httpHandler</param>
private void OnLifetimeHttpHandlerDeactivate(LifetimeHttpHandler lifetimeHttpHandler) private void OnLifetimeHttpHandlerDeactivate(LifetimeHttpHandler lifetimeHttpHandler)
{ {
var domainConfig = lifetimeHttpHandler.DomainConfig; var lifeTimeKey = lifetimeHttpHandler.LifeTimeKey;
this.httpHandlerLazyCache[domainConfig] = CreateLifetimeHttpHandlerLazy(domainConfig); this.httpHandlerLazyCache[lifeTimeKey] = CreateLifetimeHttpHandlerLazy(lifeTimeKey);
this.httpHandlerCleaner.Add(lifetimeHttpHandler); this.httpHandlerCleaner.Add(lifetimeHttpHandler);
Lazy<LifetimeHttpHandler> CreateLifetimeHttpHandlerLazy(DomainConfig domainConfig) Lazy<LifetimeHttpHandler> CreateLifetimeHttpHandlerLazy(LifeTimeKey lifeTimeKey)
{ {
return new Lazy<LifetimeHttpHandler>(() => this.CreateLifetimeHttpHandler(domainConfig, this.nextLifeTime), true); return new Lazy<LifetimeHttpHandler>(() => this.CreateLifetimeHttpHandler(lifeTimeKey, this.nextLifeTime), true);
} }
} }
/// <summary>
/// 创建LifetimeHttpHandler
/// </summary>
/// <param name="domainConfig"></param>
/// <param name="lifeTime"></param>
/// <returns></returns>
private LifetimeHttpHandler CreateLifetimeHttpHandler(DomainConfig domainConfig, TimeSpan lifeTime)
{
var httpClientHandler = new HttpClientHandler(domainConfig, this.domainResolver);
return new LifetimeHttpHandler(httpClientHandler, lifeTime, this.OnLifetimeHttpHandlerDeactivate);
}
} }
} }

View File

@ -21,14 +21,10 @@ namespace FastGithub.Http
/// </summary> /// </summary>
class HttpClientHandler : DelegatingHandler class HttpClientHandler : DelegatingHandler
{ {
private readonly DomainConfig domainConfig;
private readonly IDomainResolver domainResolver; private readonly IDomainResolver domainResolver;
private readonly TimeSpan connectTimeout = TimeSpan.FromSeconds(10d); private readonly TimeSpan connectTimeout = TimeSpan.FromSeconds(10d);
/// <summary>
/// 获取域名配置
/// </summary>
public DomainConfig DomainConfig { get; }
/// <summary> /// <summary>
/// HttpClientHandler /// HttpClientHandler
/// </summary> /// </summary>
@ -36,8 +32,8 @@ namespace FastGithub.Http
/// <param name="domainResolver"></param> /// <param name="domainResolver"></param>
public HttpClientHandler(DomainConfig domainConfig, IDomainResolver domainResolver) public HttpClientHandler(DomainConfig domainConfig, IDomainResolver domainResolver)
{ {
this.domainConfig = domainConfig;
this.domainResolver = domainResolver; this.domainResolver = domainResolver;
this.DomainConfig = domainConfig;
this.InnerHandler = this.CreateSocketsHttpHandler(); this.InnerHandler = this.CreateSocketsHttpHandler();
} }
@ -57,16 +53,16 @@ namespace FastGithub.Http
// 请求上下文信息 // 请求上下文信息
var isHttps = uri.Scheme == Uri.UriSchemeHttps; var isHttps = uri.Scheme == Uri.UriSchemeHttps;
var tlsSniValue = this.DomainConfig.GetTlsSniPattern().WithDomain(uri.Host).WithRandom(); var tlsSniValue = this.domainConfig.GetTlsSniPattern().WithDomain(uri.Host).WithRandom();
request.SetRequestContext(new RequestContext(isHttps, tlsSniValue)); request.SetRequestContext(new RequestContext(isHttps, tlsSniValue));
// 设置请求头host修改协议为http // 设置请求头host修改协议为http
request.Headers.Host = uri.Host; request.Headers.Host = uri.Host;
request.RequestUri = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttp }.Uri; request.RequestUri = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttp }.Uri;
if (this.DomainConfig.Timeout != null) if (this.domainConfig.Timeout != null)
{ {
using var timeoutTokenSource = new CancellationTokenSource(this.DomainConfig.Timeout.Value); using var timeoutTokenSource = new CancellationTokenSource(this.domainConfig.Timeout.Value);
using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token); using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token);
return await base.SendAsync(request, linkedTokenSource.Token); return await base.SendAsync(request, linkedTokenSource.Token);
} }
@ -157,7 +153,7 @@ namespace FastGithub.Http
{ {
if (errors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch)) if (errors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch))
{ {
if (this.DomainConfig.TlsIgnoreNameMismatch == true) if (this.domainConfig.TlsIgnoreNameMismatch == true)
{ {
return true; return true;
} }
@ -179,7 +175,7 @@ namespace FastGithub.Http
/// <returns></returns> /// <returns></returns>
private async IAsyncEnumerable<IPEndPoint> GetIPEndPointsAsync(DnsEndPoint dnsEndPoint, [EnumeratorCancellation] CancellationToken cancellationToken) private async IAsyncEnumerable<IPEndPoint> GetIPEndPointsAsync(DnsEndPoint dnsEndPoint, [EnumeratorCancellation] CancellationToken cancellationToken)
{ {
if (IPAddress.TryParse(this.DomainConfig.IPAddress, out var address) || if (IPAddress.TryParse(this.domainConfig.IPAddress, out var address) ||
IPAddress.TryParse(dnsEndPoint.Host, out address)) IPAddress.TryParse(dnsEndPoint.Host, out address))
{ {
yield return new IPEndPoint(address, dnsEndPoint.Port); yield return new IPEndPoint(address, dnsEndPoint.Port);

View File

@ -10,8 +10,9 @@ namespace FastGithub.Http
/// <summary> /// <summary>
/// 创建httpClient /// 创建httpClient
/// </summary> /// </summary>
/// <param name="domain"></param>
/// <param name="domainConfig"></param> /// <param name="domainConfig"></param>
/// <returns></returns> /// <returns></returns>
HttpClient CreateHttpClient(DomainConfig domainConfig); HttpClient CreateHttpClient(string domain, DomainConfig domainConfig);
} }
} }

View File

@ -0,0 +1,31 @@
using FastGithub.Configuration;
namespace FastGithub.Http
{
/// <summary>
/// 生命周期的Key
/// </summary>
record LifeTimeKey
{
/// <summary>
/// 域名
/// </summary>
public string Domain { get; }
/// <summary>
/// 域名配置
/// </summary>
public DomainConfig DomainConfig { get; }
/// <summary>
/// 生命周期的Key
/// </summary>
/// <param name="domain"></param>
/// <param name="domainConfig"></param>
public LifeTimeKey(string domain, DomainConfig domainConfig)
{
this.Domain = domain;
this.DomainConfig = domainConfig;
}
}
}

View File

@ -1,4 +1,4 @@
using FastGithub.Configuration; using FastGithub.DomainResolve;
using System; using System;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
@ -12,21 +12,19 @@ namespace FastGithub.Http
{ {
private readonly Timer timer; private readonly Timer timer;
/// <summary> public LifeTimeKey LifeTimeKey { get; }
/// 获取域名配置
/// </summary>
public DomainConfig DomainConfig { get; }
/// <summary> /// <summary>
/// 具有生命周期的HttpHandler /// 具有生命周期的HttpHandler
/// </summary> /// </summary>
/// <param name="handler">HttpHandler</param> /// <param name="domainResolver"></param>
/// <param name="lifeTime">拦截器的生命周期</param> /// <param name="lifeTimeKey"></param>
/// <param name="deactivateAction">失效回调</param> /// <param name="lifeTime"></param>
public LifetimeHttpHandler(HttpClientHandler handler, TimeSpan lifeTime, Action<LifetimeHttpHandler> deactivateAction) /// <param name="deactivateAction"></param>
: base(handler) public LifetimeHttpHandler(IDomainResolver domainResolver, LifeTimeKey lifeTimeKey, TimeSpan lifeTime, Action<LifetimeHttpHandler> deactivateAction)
{ {
this.DomainConfig = handler.DomainConfig; this.LifeTimeKey = lifeTimeKey;
this.InnerHandler = new HttpClientHandler(lifeTimeKey.DomainConfig, domainResolver);
this.timer = new Timer(this.OnTimerCallback, deactivateAction, lifeTime, Timeout.InfiniteTimeSpan); this.timer = new Timer(this.OnTimerCallback, deactivateAction, lifeTime, Timeout.InfiniteTimeSpan);
} }

View File

@ -47,7 +47,7 @@ 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(domainConfig); var httpClient = this.httpClientFactory.CreateHttpClient(host.Host, domainConfig);
var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient); var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient);
await HandleErrorAsync(context, error); await HandleErrorAsync(context, error);
} }