Open yuhanle opened 2 years ago
如果按照你的逻辑,HostResolver不是在这里被使用的吧,看代码这里注释很清楚啊。 // Set up host cache persistence if it's enabled. Happens after building the // URLRequestContext to get access to the HostCache.
应该是HttpStreamFactory::JobController::DoLoop
这里的RewriteUrlWithHostMappingRules(origin_url)
Cronet 是 Chromium 的网络库针对 iOS/Android 等的封装,提供易用接口。
准备
为 iOS 编译 Cronet 的文档在 https://chromium.googlesource.com/chromium/src/+/master/components/cronet/ios/docs/BUILD.md
源码与 Chromium 一起,比较大(超过 16GB),下载需要不少时间,而且由于最近网络不好,
gclient sync
一直失败(无法访问域名 chrome-infra-packages.appspot.com),因此编译无法进行。后来在逛 Google 论坛,有个帖子提到一个地址有编译好的库,例如对于大版本号为 79 的包,可在 https://console.cloud.google.com/storage/browser/chromium-cronet/ios?pli=1&prefix=79 找到一个列表。我们需要用 gsutil 将某个版本下载下来,例如 79.0.3921.0 且针对模拟器:
之后,我们在 Debug-iphonesimulator 可找到 Cronet.framework。
建立 Demo 工程,使其依赖 Cronet.framework,阅读头文件 Cronet.h 并编写测试代码。
原理及使用
Cronet 利用 URLSessionConfiguration 的 protocolClasses 属性,将其自定义 URLProtocol
CRNHTTPProtocolHandler
注入,它将接管由此 congfiguration 建立的 URLSession 所发起的的所有请求,参考 APIinstallIntoSessionConfiguration
。之后,我们可以使用此 session 发起请求,就和正常使用 URLSession 一样,只不过其内部的网络栈被替换为了 Cronet。其它还有一些配置类 API,如设置 User-Agent 之类的。
特别地,有个叫
setHostResolverRulesForTesting
的 API,我们可以通过它设置域名对应的 IP,达到 HTTPDNS 的效果。经我测试,可以在访问https://httpbin.thellsapi.com/get
的应答里,可看到 X-Alicdn-Da-Via 头,如:"X-Alicdn-Da-Via": "183.57.82.166,150.138.126.56"
,表示 SNI 支持,其中的 IP 183.57.82.166 通过Cronet.setHostResolverRulesForTesting("MAP httpbin.thellsapi.com 183.57.82.166")
设置(格式参考其单元测试的写法)。总体上,Cronet 暴露的 API 较少,并通过 iOS 提供的接口做到业务方透明(不引入新的 API)。
相关文件:
Host Resolver相关代码阅读
跟着 setHostResolverRulesForTesting API 分析 HostResolver 的工作原理。
setHostResolverRulesForTesting 将调用 gChromeNet.Get() 的 SetHostResolverRules 方法,而 gChromeNet.Get() 是一个 CronetEnvironment 实例(而且目前是唯一的实例,会在关闭时泄漏)。
其中 gChromeNet 利用了 LAZY_INSTANCE_INITIALIZER 做初始化。
相关文件:
CronetEnvironment
它包含网路栈相关的所有配置。例如上面通过 Cronet 设置的信息最终会传递到 CronetEnvironment 中,例如 User-Agent。
我们在 cronet_environment.mm 里看到 SetHostResolverRules 的实现,它在内部调用 SetHostResolverRulesOnNetworkThread,后者先将
main_context_->host_resolver()
转换为 MappedHostResolver,再调用其方法 SetRulesFromString。其中 maincontext 是一个 URLRequestContext,而 Host Resolver 是在 CronetEnvironment::InitializeOnNetworkThread 里使用 mapped_host_resolver 利用 context_builder.set_host_resolver 初始化的,context_builder 正是一个 URLRequestContextBuilder,它最后创建出 maincontext。奇怪的是 main_contextgetter 是一个 CronetURLRequestContextGetter(定义在 cronet_environment.mm 里,是 URLRequestContextGetter 的子类),还不清楚 main_contextgetter 的用途。它初始化时会传入 CronetEnvironment* environment,这里面有
main_context_->host_resolver()
。且其 GetURLRequestContext() 返回的就是 environment_→GetURLRequestContext(),它最终返回的是 maincontext,连起来的吧!相关文件:
MappedHostResolver
MappedHostResolver 是 HostResolver 的子类。它包含一个 HostResolver 实例(通过初始化传递),它将在请求传递到 impl 前利用 rules 的 RewriteHost 修改之。其中 impl_ 就是内部的 HostResolver,rules 是一个 HostMappingRules。之前提到的 SetRulesFromString 就是调用 HostMappingRules 的 SetRulesFromString 方法。
CRNHTTPProtocolHandler
在 CRNHTTPProtocolHandler 内部,定义了 HttpProtocolHandlerCore。在拦截到请求后,它利用一个 NSURLRequest 生成 HttpProtocolHandlerCore 实例 _core,然后在 startLoading 里绑定调用 net::HttpProtocolHandlerCore::Start,也即
void HttpProtocolHandlerCore::Start(id<CRNNetworkClientProtocol> base_client)
,处理一番后,调用net_request_->Start()
,其中 netrequest 由 context 创建net_request_ = context->CreateRequest(url, DEFAULT_PRIORITY, this).release()
,而这里的 context 就是一个 URLRequestContext,由 g_protocol_handler_delegate 生成。g_protocol_handler_delegate 是 HTTPProtocolHandlerDelegate 类型,是个全局静态变量,由 Cronet 在初始化的时候在 startInternal 里设置。
net::HTTPProtocolHandlerDelegate::SetInstance( gHttpProtocolHandlerDelegate.Get().get());
而 gHttpProtocolHandlerDelegate 是一个 CronetHttpProtocolHandlerDelegate 的 lazy 实例。所以,最终 g_protocol_handler_delegate 就是一个 CronetHttpProtocolHandlerDelegate。
HTTPProtocolHandlerDelegate
它定义在 crn_http_protocol_hander.h 里,不过其方法都是 virtual 标记的,这也符合上面的分析。
CronetHttpProtocolHandlerDelegate
它定义在 Cronet.mm 里,它具体实现了 CanHandleRequest、IsRequestSupported 和 GetDefaultURLRequestContext。
其中 GetDefaultURLRequestContext 返回的是一个 URLRequestContextGetter,即它内部的 getter_,其在初始化时传入,就是 gChromeNet.Get()→GetURLRequestContextGetter(),最终返回的是 CronetEnvironment 里的 main_contextgetter,终于找到它的用途了。
可见,对于 iOS 上的实现来说,其作用的是 main_contextgetter 针对每个请求构造 URLRequestContext,然后创建 request 再 Start()
相关文件:
HostResolver 如何被使用?
在 CronetURLRequestContext::NetworkTasks::Initialize 中,
URLRequest
之前生成 request 时,使用了 HttpProtocolHandlerCore 的 context 作为 delegate
在 其
Start()
中,利用 URLRequestJobManager 构造 Job,使用 networkdelegate(NetworkDelegate),它使用初始化传入的 networkdelegate,最终来源是 URLRequestContextcontext_builder.set_network_delegate( std::make_unique<BasicNetworkDelegate>());
相关文件: