dotnet平台Http消息处理者工厂

dotnet平台Http消息处理者工厂

1 前言#

Microsoft.Extensions.Http是一个设计非常优异的客户端工厂库,其提供了IHttpClientFactory用于创建HttpClientIHttpMessageHandlerFactory用于创建HttpMessageHandler

遗憾的是这个库目前仅非常试用于客户端,而不太适用于转发端。我们对客户端的定义是一个软件在某种业务下使用单账号请求远程服务器的客户端行为,此软件不再充当其它软件的服务端;对转发端的定义是一个软件运行时,帮它的的多个客户端请求远程服务器,同时一般对远程服务器的响应内容做一些包装或修改的软件。

有时哪怕是做客户端软件,当遇到下面需求时,HttpClient和Microsoft.Extensions.Http的者难以解决:

  1. 可以临时申请很多代理服务器
  2. 每个代理服务器能使用3分钟
  3. 使用这些代理服务器源源不断的请求到某站

如果我们使用Microsoft.Extensions.Http,则无法使用动态的代理服务器;如果我们使用动态创建和维护多个HttpClient实例,我们又回到造第二个Microsoft.Extensions.Http的需求。

2 HttpMessageHandlerFactory#

HttpMessageHandlerFactory就是上面要造第二Microsoft.Extensions.Http的需求的产物,其它核心接口定义如下:

 

Copy

/// <summary> /// Http消息处理者工厂 /// </summary> public interface IHttpMessageHandlerFactory { /// <summary> /// 创建用于请求的HttpMessageHandler /// </summary> /// <param name="name">别名</param> /// <param name="proxyUri">支持携带UserInfo的代理地址</param> /// <returns></returns> HttpMessageHandler CreateHandler(string name, Uri? proxyUri); }

当然HttpMessageHandlerFactory也提供了和Microsoft.Extensions.Http相似的Builder能力,在使用服务注册时没有额外的学习成本。

2.1 可选的ProxyUri参数#

接口多了一个可选的Uri参数,有值的时候,代表要使用这个参数做代理。别小看这个Uri参数,它是可继承的类型,传入Uri的子类型还可以实现很多意想不到的骚操作。

2.2 支持创建HttpClient#

IHttpMessageHandlerFactory提供创建HttpClient的扩展,用于做客户端模式,且支持传入与用户实例绑定的CookieContainer,然后Cookie就完全自动化处理。

 

Copy

/// <summary> /// 创建Http客户端 /// </summary> /// <param name="factory"></param> /// <param name="name">别名</param> /// <param name="proxyUri">支持携带UserInfo的代理地址</param> /// <param name="cookieContainer">cookie容器</param> /// <returns></returns> public static HttpClient CreateClient(this IHttpMessageHandlerFactory factory, string name, Uri? proxyUri = null, CookieContainer? cookieContainer = null) { var httpHandler = factory.CreateHandler(name, proxyUri, cookieContainer); return new HttpClient(httpHandler, disposeHandler: false); }

2.3 支持创建HttpMessageInvoker#

IHttpMessageHandlerFactory提供创建HttpMessageInvoker的扩展,用于转发端模式,且支持传入与用户实例绑定的CookieContainer,然后Cookie就完全自动化处理。

 

Copy

/// <summary> /// 创建Http执行器 /// </summary> /// <param name="factory"></param> /// <param name="name">别名</param> /// <param name="proxyUri">支持携带UserInfo的代理地址</param> /// <param name="cookieContainer">cookie容器</param> /// <returns></returns> public static HttpMessageInvoker CreateInvoker(this IHttpMessageHandlerFactory factory, string name, Uri? proxyUri = null, CookieContainer? cookieContainer = null) { var httpHandler = factory.CreateHandler(name, proxyUri, cookieContainer); return new HttpMessageInvoker(httpHandler, disposeHandler: false); }

3 生态与扩展#

如果说HttpMessageHandlerFactory只解决了Microsoft.Extensions.Http的Proxy痛点,但丢了Microsoft.Extensions.Http的生态又不能扩展的话,那无疑HttpMessageHandlerFactory是非常局限和失败的。

实际上Microsoft.Extensions.Http上层的很多组件,移植到HttpMessageHandlerFactory是非常简单的,简单说是DI注册扩展的IHttpClientBuilder改为IHttpMessageHandlerBuilder就行。

3.1 HttpMessageHandlerFactory.Polly#

为HttpMessageHandlerFactory提供Polly策略扩展,使得IHttpMessageHandlerBuilder拥有与IHttpClientFactory完全一致的Polly能力。

3.1.1 AddPolicyHandler能力#

 

Copy

var retryPolicy = Policy.Handle<HttpRequestException>() .OrResult<HttpResponseMessage>(response => { return response.IsSuccessStatusCode == false; }).WaitAndRetryAsync(3, t => TimeSpan.FromSeconds(3d)); services .AddHttpMessageHandlerFactory("App") .AddPolicyHandler(retryPolicy);

3.1.2 AddPolicyHandlerFromRegistry能力#

 

Copy

var retryPolicy = Policy.Handle<HttpRequestException>() .OrResult<HttpResponseMessage>(response => { return response.IsSuccessStatusCode == false; }).WaitAndRetryAsync(3, t => TimeSpan.FromSeconds(3d)); var registry = services.AddPolicyRegistry(); registry.Add("registry1", retryPolicy); services .AddHttpMessageHandlerFactory("App") .AddPolicyHandlerFromRegistry("registry1");

3.1.3 AddTransientHttpErrorPolicy能力#

当以下任意条件成立时,触发TransientHttpErrorPolicy

  • HttpRequestException的网络故障
  • 服务端响应5XX的状态码
  • 408的状态码(request timeout)
 

Copy

services .AddHttpMessageHandlerFactory("App") .AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(new[] { TimeSpan.FromSeconds(1d), TimeSpan.FromSeconds(5d), TimeSpan.FromSeconds(10d) }));

3.2 HttpMessageHandlerFactory.Connection#

为HttpMessageHandlerFactory提供自定义连接的功能。
注意此扩展项目不是免费项目,有如下限制:

  • 不开放和提供源代码
  • nuget包的程序集在应用程序运行2分钟后适用期结束
  • 适用期结束后所有的http请求响应为423 Locked
  • 需要license文件授权方可完全使用

3.2.1 自定义域名解析#

  • 当无代理连接时,连接到自定义解析得到的IP
  • 当使用http代理时,让代理服务器连接到自定义解析得到的IP
  • 当使用socks代理时,让代理服务器连接到自定义解析得到的IP
 

Copy

services .AddHttpMessageHandlerFactory("App") .AddHostResolver<CustomHostResolver>();

 

Copy

sealed class CustomHostResolver : HostResolver { public override ValueTask<HostPort> ResolveAsync(DnsEndPoint endpoint, CancellationToken cancellationToken) { if (endpoint.Host == "www.baidu.com") { return ValueTask.FromResult(new HostPort("14.119.104.189", endpoint.Port)); } return ValueTask.FromResult(new HostPort(endpoint.Host, endpoint.Port)); } }

3.2.2 自定义ssl的sni#

Server Name Indication (SNI) 是 TLS 协议(以前称为 SSL 协议)的扩展,该协议在 HTTPS 中使用。它包含在 TLS/SSL 握手流程中,以确保客户端设备能够看到他们尝试访问的网站的正确 SSL 证书。该扩展使得可以在 TLS 握手期间指定网站的主机名或域名 ,而不是在握手之后打开 HTTP 连接时指定。

 

Copy

services .AddHttpMessageHandlerFactory("App") .AddSslSniProvider<CustomSslSniProvider>();

 

Copy

sealed class CustomSslSniProvider : SslSniProvider { public override ValueTask<string> GetSslSniAsync(string host, CancellationToken cancellationToken) { return ValueTask.FromResult(string.Empty); } public override bool RemoteCertificateValidationCallback(string host, X509Certificate? cert, X509Chain? chain, SslPolicyErrors errors) { return true; } }

4 两个库的场景选择#

无/固定代理动态代理客户端转发端
Microsoft.Extensions.Http适合不适合非常适合功能弱
HttpMessageHandlerFactory适合适合功能弱非常适合

HttpMessageHandlerFactory的源代码在https://github.com/xljiulang/HttpMessageHandlerFactory,其功能单一代码量相对Microsoft.Extensions.Http要少一些,阅读其代码之后去理解Microsoft.Extensions.Http会更容易很多。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值