重构HttpClientFactory
This commit is contained in:
parent
fab33b4fad
commit
4f3578dc9e
@ -87,7 +87,7 @@ namespace FastGithub.DomainResolve
|
|||||||
private async Task<IPAddress> LookupCoreAsync(IPEndPoint dns, string domain, CancellationToken cancellationToken)
|
private async Task<IPAddress> LookupCoreAsync(IPEndPoint dns, string domain, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var dnsClient = new DnsClient(dns);
|
var dnsClient = new DnsClient(dns);
|
||||||
using var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(1d));
|
using var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(2d));
|
||||||
using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token);
|
using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token);
|
||||||
|
|
||||||
var addresses = await dnsClient.Lookup(domain, RecordType.A, linkedTokenSource.Token);
|
var addresses = await dnsClient.Lookup(domain, RecordType.A, linkedTokenSource.Token);
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
using FastGithub.Configuration;
|
using FastGithub.Configuration;
|
||||||
using FastGithub.DomainResolve;
|
using FastGithub.DomainResolve;
|
||||||
using System;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FastGithub.Http
|
namespace FastGithub.Http
|
||||||
{
|
{
|
||||||
@ -12,51 +9,24 @@ namespace FastGithub.Http
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class HttpClient : HttpMessageInvoker
|
public class HttpClient : HttpMessageInvoker
|
||||||
{
|
{
|
||||||
private readonly DomainConfig domainConfig;
|
|
||||||
private readonly TimeSpan defaltTimeout = TimeSpan.FromMinutes(2d);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// http客户端
|
/// http客户端
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="domainConfig"></param>
|
/// <param name="domainConfig"></param>
|
||||||
/// <param name="domainResolver"></param>
|
/// <param name="domainResolver"></param>
|
||||||
public HttpClient(DomainConfig domainConfig, IDomainResolver domainResolver)
|
public HttpClient(DomainConfig domainConfig, IDomainResolver domainResolver)
|
||||||
: this(domainConfig, new HttpClientHandler(domainResolver), disposeHandler: true)
|
: this(new HttpClientHandler(domainConfig, domainResolver), disposeHandler: true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// http客户端
|
/// http客户端
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="domainConfig"></param>
|
|
||||||
/// <param name="handler"></param>
|
/// <param name="handler"></param>
|
||||||
/// <param name="disposeHandler"></param>
|
/// <param name="disposeHandler"></param>
|
||||||
internal HttpClient(DomainConfig domainConfig, HttpClientHandler handler, bool disposeHandler)
|
internal HttpClient(HttpClientHandler handler, bool disposeHandler)
|
||||||
: base(handler, disposeHandler)
|
: base(handler, disposeHandler)
|
||||||
{
|
{
|
||||||
this.domainConfig = domainConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 发送数据
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="cancellationToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
request.SetRequestContext(new RequestContext
|
|
||||||
{
|
|
||||||
Host = request.RequestUri?.Host,
|
|
||||||
IPAddress = this.domainConfig.IPAddress,
|
|
||||||
IsHttps = request.RequestUri?.Scheme == Uri.UriSchemeHttps,
|
|
||||||
TlsSniPattern = this.domainConfig.GetTlsSniPattern(),
|
|
||||||
TlsIgnoreNameMismatch = this.domainConfig.TlsIgnoreNameMismatch
|
|
||||||
});
|
|
||||||
|
|
||||||
using var timeoutTokenSource = new CancellationTokenSource(this.domainConfig.Timeout ?? defaltTimeout);
|
|
||||||
using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token);
|
|
||||||
return base.SendAsync(request, linkedTokenSource.Token);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
using FastGithub.Configuration;
|
using FastGithub.Configuration;
|
||||||
using FastGithub.DomainResolve;
|
using FastGithub.DomainResolve;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace FastGithub.Http
|
namespace FastGithub.Http
|
||||||
{
|
{
|
||||||
@ -9,7 +10,8 @@ namespace FastGithub.Http
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
sealed class HttpClientFactory : IHttpClientFactory
|
sealed class HttpClientFactory : IHttpClientFactory
|
||||||
{
|
{
|
||||||
private HttpClientHandler httpClientHanlder;
|
private readonly IDomainResolver domainResolver;
|
||||||
|
private ConcurrentDictionary<DomainConfig, HttpClientHandler> domainHandlers = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// HttpClient工厂
|
/// HttpClient工厂
|
||||||
@ -20,8 +22,8 @@ namespace FastGithub.Http
|
|||||||
IDomainResolver domainResolver,
|
IDomainResolver domainResolver,
|
||||||
IOptionsMonitor<FastGithubOptions> options)
|
IOptionsMonitor<FastGithubOptions> options)
|
||||||
{
|
{
|
||||||
this.httpClientHanlder = new HttpClientHandler(domainResolver);
|
this.domainResolver = domainResolver;
|
||||||
options.OnChange(opt => this.httpClientHanlder = new HttpClientHandler(domainResolver));
|
options.OnChange(opt => this.domainHandlers = new());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -31,7 +33,8 @@ namespace FastGithub.Http
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public HttpClient CreateHttpClient(DomainConfig domainConfig)
|
public HttpClient CreateHttpClient(DomainConfig domainConfig)
|
||||||
{
|
{
|
||||||
return new HttpClient(domainConfig, this.httpClientHanlder, disposeHandler: false);
|
var httpClientHandler = this.domainHandlers.GetOrAdd(domainConfig, config => new HttpClientHandler(config, this.domainResolver));
|
||||||
|
return new HttpClient(httpClientHandler, disposeHandler: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using FastGithub.DomainResolve;
|
using FastGithub.Configuration;
|
||||||
|
using FastGithub.DomainResolve;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -18,23 +19,74 @@ 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 defaltTimeout = TimeSpan.FromMinutes(2d);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// HttpClientHandler
|
/// HttpClientHandler
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="domainResolver"></param>
|
/// <param name="domainConfig"></param>
|
||||||
public HttpClientHandler(IDomainResolver domainResolver)
|
/// <param name="domainResolver"></param>
|
||||||
|
public HttpClientHandler(DomainConfig domainConfig, IDomainResolver domainResolver)
|
||||||
{
|
{
|
||||||
this.domainResolver = domainResolver;
|
this.domainResolver = domainResolver;
|
||||||
this.InnerHandler = CreateSocketsHttpHandler();
|
this.domainConfig = domainConfig;
|
||||||
|
this.InnerHandler = this.CreateSocketsHttpHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 替换域名为ip
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var uri = request.RequestUri;
|
||||||
|
if (uri == null)
|
||||||
|
{
|
||||||
|
throw new FastGithubException("必须指定请求的URI");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求上下文信息
|
||||||
|
var context = new RequestContext
|
||||||
|
{
|
||||||
|
Domain = uri.Host,
|
||||||
|
IsHttps = uri.Scheme == Uri.UriSchemeHttps,
|
||||||
|
TlsSniPattern = this.domainConfig.GetTlsSniPattern().WithDomain(uri.Host).WithRandom()
|
||||||
|
};
|
||||||
|
request.SetRequestContext(context);
|
||||||
|
|
||||||
|
// 解析ip,替换https为http
|
||||||
|
var uriBuilder = new UriBuilder(uri)
|
||||||
|
{
|
||||||
|
Scheme = Uri.UriSchemeHttp
|
||||||
|
};
|
||||||
|
|
||||||
|
if (uri.HostNameType == UriHostNameType.Dns)
|
||||||
|
{
|
||||||
|
if (IPAddress.TryParse(this.domainConfig.IPAddress, out var address) == false)
|
||||||
|
{
|
||||||
|
address = await this.domainResolver.ResolveAsync(context.Domain, cancellationToken);
|
||||||
|
}
|
||||||
|
uriBuilder.Host = address.ToString();
|
||||||
|
request.Headers.Host = context.Domain;
|
||||||
|
context.TlsSniPattern = context.TlsSniPattern.WithIPAddress(address);
|
||||||
|
}
|
||||||
|
request.RequestUri = uriBuilder.Uri;
|
||||||
|
|
||||||
|
using var timeoutTokenSource = new CancellationTokenSource(this.domainConfig.Timeout ?? defaltTimeout);
|
||||||
|
using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token);
|
||||||
|
return await base.SendAsync(request, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建转发代理的httpHandler
|
/// 创建转发代理的httpHandler
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private static SocketsHttpHandler CreateSocketsHttpHandler()
|
private SocketsHttpHandler CreateSocketsHttpHandler()
|
||||||
{
|
{
|
||||||
return new SocketsHttpHandler
|
return new SocketsHttpHandler
|
||||||
{
|
{
|
||||||
@ -68,14 +120,14 @@ namespace FastGithub.Http
|
|||||||
{
|
{
|
||||||
if (errors == SslPolicyErrors.RemoteCertificateNameMismatch)
|
if (errors == SslPolicyErrors.RemoteCertificateNameMismatch)
|
||||||
{
|
{
|
||||||
if (requestContext.TlsIgnoreNameMismatch == true)
|
if (this.domainConfig.TlsIgnoreNameMismatch == true)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var host = requestContext.Host;
|
var domain = requestContext.Domain;
|
||||||
var dnsNames = ReadDnsNames(cert);
|
var dnsNames = ReadDnsNames(cert);
|
||||||
return dnsNames.Any(dns => IsMatch(dns, host));
|
return dnsNames.Any(dns => IsMatch(dns, domain));
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors == SslPolicyErrors.None;
|
return errors == SslPolicyErrors.None;
|
||||||
@ -119,59 +171,23 @@ namespace FastGithub.Http
|
|||||||
/// 比较域名
|
/// 比较域名
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dnsName"></param>
|
/// <param name="dnsName"></param>
|
||||||
/// <param name="host"></param>
|
/// <param name="domain"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private static bool IsMatch(string dnsName, string? host)
|
private static bool IsMatch(string dnsName, string? domain)
|
||||||
{
|
{
|
||||||
if (host == null)
|
if (domain == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (dnsName == host)
|
if (dnsName == domain)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (dnsName[0] == '*')
|
if (dnsName[0] == '*')
|
||||||
{
|
{
|
||||||
return host.EndsWith(dnsName[1..]);
|
return domain.EndsWith(dnsName[1..]);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 替换域名为ip
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="cancellationToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var uri = request.RequestUri;
|
|
||||||
if (uri != null)
|
|
||||||
{
|
|
||||||
var uriBuilder = new UriBuilder(uri)
|
|
||||||
{
|
|
||||||
Scheme = Uri.UriSchemeHttp
|
|
||||||
};
|
|
||||||
|
|
||||||
var domain = uri.Host;
|
|
||||||
var context = request.GetRequestContext();
|
|
||||||
context.TlsSniPattern = context.TlsSniPattern.WithDomain(domain).WithRandom();
|
|
||||||
|
|
||||||
if (uri.HostNameType == UriHostNameType.Dns)
|
|
||||||
{
|
|
||||||
if (IPAddress.TryParse(context.IPAddress, out var address) == false)
|
|
||||||
{
|
|
||||||
address = await this.domainResolver.ResolveAsync(domain, cancellationToken);
|
|
||||||
}
|
|
||||||
uriBuilder.Host = address.ToString();
|
|
||||||
request.Headers.Host = domain;
|
|
||||||
context.TlsSniPattern = context.TlsSniPattern.WithIPAddress(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
request.RequestUri = uriBuilder.Uri;
|
|
||||||
}
|
|
||||||
return await base.SendAsync(request, cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
using FastGithub.Configuration;
|
using FastGithub.Configuration;
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace FastGithub.Http
|
namespace FastGithub.Http
|
||||||
{
|
{
|
||||||
@ -14,23 +13,13 @@ namespace FastGithub.Http
|
|||||||
public bool IsHttps { get; set; }
|
public bool IsHttps { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求的主机
|
/// 请求的域名
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Host { get; set; }
|
public string? Domain { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 请求的ip
|
|
||||||
/// </summary>
|
|
||||||
public string? IPAddress { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置Sni值的表达式
|
/// 获取或设置Sni值的表达式
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TlsSniPattern TlsSniPattern { get; set; }
|
public TlsSniPattern TlsSniPattern { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否忽略服务器证书域名不匹配
|
|
||||||
/// </summary>
|
|
||||||
public bool TlsIgnoreNameMismatch { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,10 +2,12 @@
|
|||||||
"FastGithub": {
|
"FastGithub": {
|
||||||
"DomainConfigs": {
|
"DomainConfigs": {
|
||||||
"i.imgur.com": {
|
"i.imgur.com": {
|
||||||
"IPAddress": "146.75.8.193"
|
"IPAddress": "146.75.8.193",
|
||||||
|
"TlsIgnoreNameMismatch": true
|
||||||
},
|
},
|
||||||
"i.*.imgur.com": {
|
"i.*.imgur.com": {
|
||||||
"IPAddress": "146.75.8.193"
|
"IPAddress": "146.75.8.193",
|
||||||
|
"TlsIgnoreNameMismatch": true
|
||||||
},
|
},
|
||||||
"lh*.googleusercontent.com": {
|
"lh*.googleusercontent.com": {
|
||||||
"Response": {
|
"Response": {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user