Chromium net 的Cronet如何加入HostResolver
背景
这个是源于项目中为了减少DNS的解析时间,同时又能够使用Chromium的net库。
源码分析
Cronet本身是编译了net库,同时提供了一个已经封装好的Java Api,不得不说Google考虑还是很周到的,但Cronet本身提供的Api接口比较少,通过json的配置又不能动态的去更新,所以就需要在dns解析的时候来获取应用层的已经解析的dns列表。
所以,通过java的源码,CronetUrlRequestContext.Java
1 | @UsedByReflection("CronetEngine.java") |
Api层通过CronetEngineBuilder 来构造所提供的参数,然后设置到Jni层。
1 | CronetLibraryLoader.ensureInitialized(builder.getContext(), builder); |
这个先确定库的初始化。 然后在create native 的Config
1 | synchronized (mLock) { |
优先是先创建 nativeCreateRequestContextConfig 创建配置,我们就可以延着这个native
调用栈来查看native处理流程,SDK有一行注释代码
1 | // Native methods are implemented in cronet_url_request_context_adapter.cc. |
cronet_url_request_context_adapter.cc 就是入口了,我们查看下这个类,找到如下函数:
1 | // Create a URLRequestContextConfig from the given parameters. static jlong JNI_CronetUrlRequestContext_CreateRequestContextConfig( JNIEnv* env, const JavaParamRef<jstring>& juser_agent, const JavaParamRef<jstring>& jstorage_path, jboolean jquic_enabled, const JavaParamRef<jstring>& jquic_default_user_agent_id, jboolean jhttp2_enabled, jboolean jbrotli_enabled, jboolean jdisable_cache, jint jhttp_cache_mode, jlong jhttp_cache_max_size, const JavaParamRef<jstring>& jexperimental_quic_connection_options, jlong jmock_cert_verifier, jboolean jenable_network_quality_estimator, jboolean jbypass_public_key_pinning_for_local_trust_anchors, jint jnetwork_thread_priority) { return reinterpret_cast<jlong>(new URLRequestContextConfig( jquic_enabled, ConvertNullableJavaStringToUTF8(env, jquic_default_user_agent_id), jhttp2_enabled, jbrotli_enabled, static_cast<URLRequestContextConfig::HttpCacheType>(jhttp_cache_mode), jhttp_cache_max_size, jdisable_cache, ConvertNullableJavaStringToUTF8(env, jstorage_path), /* accept_languages */ std::string(), ConvertNullableJavaStringToUTF8(env, juser_agent), ConvertNullableJavaStringToUTF8(env, jexperimental_quic_connection_options), base::WrapUnique( reinterpret_cast<net::CertVerifier*>(jmock_cert_verifier)), jenable_network_quality_estimator, jbypass_public_key_pinning_for_local_trust_anchors, jnetwork_thread_priority >= -20 && jnetwork_thread_priority <= 19 ? base::Optional<double>(jnetwork_thread_priority) : base::Optional<double>())); } |
这个是一个静态的函数,把Java端的参数传递到到URLRequestContextConfig这个类里面。 在Java层我们注意到这行代码:
1 | nativeCreateRequestContextAdapter(createNativeUrlRequestContextConfig(builder)); |
也就是说创建的配置项会传递给创建出来的CreateRequestContextAdapter类,我们看看jni的CreateRequestContextAdapter类的构造函数:
1 | CronetURLRequestContextAdapter::CronetURLRequestContextAdapter( std::unique_ptr<URLRequestContextConfig> context_config) { // Create context and pass ownership of |this| (self) to the context. std::unique_ptr<CronetURLRequestContextAdapter> self(this); #if BUILDFLAG(INTEGRATED_MODE) // Create CronetURLRequestContext running in integrated network task runner. context_ = new CronetURLRequestContext(std::move(context_config), std::move(self), GetIntegratedModeNetworkTaskRunner()); #else context_ = new CronetURLRequestContext(std::move(context_config), std::move(self)); #endif } |
从CronetURLRequestContextAdapter的构造函数中我们能过看到参数是URLRequestContextConfig,通过参数创建出CronetURLRequestContext,然后通过context_的上下文来进行Start等操作,所以进行参数解析的是在CronetURLRequestContext中,我们就看看CronetURLRequestContext做了什么事情,入口是构造函数:
1 | CronetURLRequestContext::CronetURLRequestContext( std::unique_ptr<URLRequestContextConfig> context_config, std::unique_ptr<Callback> callback, scoped_refptr<base::SingleThreadTaskRunner> network_task_runner) : default_load_flags_( net::LOAD_NORMAL | (context_config->load_disable_cache ? net::LOAD_DISABLE_CACHE : 0)), network_tasks_( new NetworkTasks(std::move(context_config), std::move(callback))), network_task_runner_(network_task_runner) { if (!network_task_runner_) { network_thread_ = std::make_unique<base::Thread>("network"); base::Thread::Options options; options.message_loop_type = base::MessageLoop::TYPE_IO; network_thread_->StartWithOptions(options); network_task_runner_ = network_thread_->task_runner(); } } |
context_config 作为NetworkTasks的参数,我们看看NetworkTasks,不难发现NetworkTasks中正是进行参数转换的地方
1 | void CronetURLRequestContext::NetworkTasks::Initialize( scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, scoped_refptr<base::SequencedTaskRunner> file_task_runner, std::unique_ptr<net::ProxyConfigService> proxy_config_service) { DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_); DCHECK(!is_context_initialized_); std::unique_ptr<URLRequestContextConfig> config(std::move(context_config_)); network_task_runner_ = network_task_runner; if (config->network_thread_priority) SetNetworkThreadPriorityOnNetworkThread( config->network_thread_priority.value()); base::DisallowBlocking(); net::URLRequestContextBuilder context_builder; context_builder.set_network_delegate( std::make_unique<BasicNetworkDelegate>()); context_builder.set_net_log(g_net_log.Get().net_log()); context_builder.set_proxy_resolution_service( cronet::CreateProxyResolutionService(std::move(proxy_config_service), g_net_log.Get().net_log())); config->ConfigureURLRequestContextBuilder(&context_builder, g_net_log.Get().net_log()); |
通过config来构建出 net::URLRequestContextBuilder,我们看看net::URLRequestContextBuilder的组成,其中有一行:
1 | // By default host_resolver is constructed with CreateDefaultResolver. void set_host_resolver(std::unique_ptr<HostResolver> host_resolver); |
到这里我们就知道,其实CronetURLRequestContext 通过URLRequestContextConfig 来构建自己的URLRequestContextBuilder,URLRequestContextBuilder中包含了设置的所有参数。
在回到我们的问题,我们是要解决自定义dns解析,而Java层没有接口来提供可选的设置项。在Java层可以通过设置ExperimentalOptions来配置不同的参数来开启功能,但这个一旦设置了不能动态更改。在url_request_context_config.cc 的 ParseAndSetExperimentalOptions 中有一段代码如下:
1 | if (async_dns_enable || stale_dns_enable || host_resolver_rules_enable || disable_ipv6_on_wifi) { CHECK(net_log) << "All DNS-related experiments require NetLog."; std::unique_ptr<net::HostResolver> host_resolver; if (stale_dns_enable) { DCHECK(!disable_ipv6_on_wifi); host_resolver.reset(new StaleHostResolver( net::HostResolver::CreateDefaultResolverImpl(net_log), stale_dns_options)); } else { host_resolver = net::HostResolver::CreateDefaultResolver(net_log); } if (disable_ipv6_on_wifi) host_resolver->SetNoIPv6OnWifi(true); if (async_dns_enable) host_resolver->SetDnsClientEnabled(true); if (host_resolver_rules_enable) { std::unique_ptr<net::MappedHostResolver> remapped_resolver( new net::MappedHostResolver(std::move(host_resolver))); remapped_resolver->SetRulesFromString(host_resolver_rules_string); host_resolver = std::move(remapped_resolver); } context_builder->set_host_resolver(std::move(host_resolver)); } |
以上代码的作用就是在开启不同的值时可以提供域名对应的值,但这个只是局限于首次加载配置选项,后面无法更改。
HostResolver域名解析
HostResolver是域名解析的服务的接口,我们先看看这个接口做了什么事情。
1 | virtual int Resolve(const RequestInfo& info, RequestPriority priority, AddressList* addresses, CompletionOnceCallback callback, std::unique_ptr<Request>* out_req, const NetLogWithSource& net_log) = 0; |
这个方法的目的是将解析的ip存储到addresses中,所以,我们就能过在这个地方加入Java端的dns解析