前面我们分析到,在URLRequestHttpJob::StartTransactionInternal()
中,会通过URLRequestContext
的HttpTransactionFactory
创建HttpTransaction
,在URLRequestContextBuilder::Build()
中创建HttpTransactionFactory
的过程如下:
|
|
Chromium net中有两个HttpTransactionFactory
的实现,分别是HttpCache
和HttpNetworkLayer
,它们分别在cache被打开和被关闭时用到。这里还会创建HttpNetworkSession
。而在cache打开时,在创建HttpCache
的同时,还会为它创建http_cache_backend。
HttpCache的创建过程(chromium_android/src/net/http/http_cache.cc)如下:
这里还是会创建HttpNetworkLayer
。HttpTransactionFactory相关的几个类之间的关系如下:
HttpNetworkTransaction表示一个直接的网络事务,可以理解为一个网络连接。HttpNetworkSession用于管理网络连接。HttpNetworkLayer主要用于创建HttpNetworkTransaction。HttpCache和HttpCache::Transaction用于处理缓存。HttpCache::Transaction表示一个启用了缓存的网络事务,它会借助于HttpCache保存的HttpNetworkLayer引用创建HttpNetworkTransaction,借助于HttpNetworkTransaction访问网络,并根据需要将结果缓存起来。HttpCache则对缓存进行管理。HttpNetworkLayer和HttpCache都是HttpTransactionFactory,而HttpNetworkTransaction和HttpCache::Transaction都是HttpTransaction。
我们先不关心启用cache时,HTTP请求的处理流程,来看HttpNetworkLayer。则在URLRequestHttpJob::StartTransactionInternal()
中将通过HttpNetworkLayer创建类型为HttpNetworkTransaction的HttpTransaction:
URLRequestHttpJob::StartTransactionInternal()
创建了HttpTransaction之后,它会执行HttpTransaction的Start()来启动对HTTP事务的处理,HttpNetworkTransaction的Start()定义如下:
Chromium net将所有HTTP请求的处理抽象为几个步骤,并通过一个循环DoLoop()来一步一步地执行。DoLoop()的定义 (chromium_android/src/net/http/http_network_transaction.cc) 如下:
接下来我们逐个地分析这些步骤。
Stream的创建
DoNotifyBeforeCreateStream()执行before_network_start_callback:
在DoCreateStream()中创建Stream:
当请求是一个Websocket请求时,通过HttpNetworkSession的http_stream_factory_for_websocket创建Stream,而其他情况下,则会通过HttpNetworkSession的http_stream_factory创建Stream。
由HttpNetworkSession的创建过程 (chromium_android/src/net/http/http_network_session.cc) 可以看到,http_stream_factory_for_websocket和http_stream_factory都是HttpStreamFactoryImpl:
为Websocket之外的其它请求创建Stream的过程 (chromium_android/src/net/http/http_stream_factory_impl.cc) 为:
在HttpStreamFactoryImpl::RequestStreamInternal()中,主要是创建了一个JobController,然后用job_controller->Start()创建了Request,也就是HttpStreamRequest。
由HttpStreamFactoryImpl的构造函数可以看到,jobfactory是DefaultJobFactory,这个类的实现也相当简单(chromium_android/src/net/http/http_stream_factory_impl.cc) :
JobController的Start()定义 (chromium_android/src/net/http/http_stream_factory_impl_job_controller.cc) 如下:
在这里主要是创建了一个Request,然后调用CreateJobs()创建了一些Jobs:
这里的过程如下:
- 应用主机映射规则,对url做修饰。
- 通过job_factory创建main_job。
- 查找备选服务。
- 找到了备选服务,则创建alternative_job,并Start它。
- Start main_job。
我们前面提到的一些Jobs主要是指main_job,和可能会创建的alternative_job。
接着我们来看Job的Start()方法(chromium_android/src/net/http/http_stream_factory_impl_job.cc) :
执行调用流程大体如下:
在HttpStreamFactoryImpl::Job::RunLoop()中,主要是调用了HttpStreamFactoryImpl::Job::DoLoop(),并针对其执行结果,调用响应的回调函数,如:
从前面的HttpStreamFactoryImpl::JobController::CreateJobs()中可以看到,delegate_正是HttpStreamFactoryImpl::JobController。
而在HttpStreamFactoryImpl::Job::DoLoop()中,则是处理Stream建立的事情。与HttpNetworkTransaction的 Start() 执行的DoLoop()类似,HttpStreamFactoryImpl::Job::DoLoop()也是将Stream创建的过程抽象为一系列的步骤,通过一个循环,以一种类似于状态机模式的方式逐步骤执行:
以执行一个QUIC请求为例,创建Stream的整个执行流程大体如下:
备选服务机制
在HttpStreamFactoryImpl::JobController的CreateJobs()中我们看到,在通过job_factory创建main_job之后,会查找备选服务,在找到了备选服务时,还会为它创建job,并Start。那备选服务又是一套什么样的机制呢?
我们从两个方面来探究这套机制究竟是什么样的,及它又被用来做什么,一是备选服务的信息是从哪里及如何获取的,二是备选服务对HttpStreamFactoryImpl::Job::Job的操作的影响。
获取备选服务信息
我们先来看备选服务信息的获取。在HttpStreamFactoryImpl::JobController的CreateJobs()中,通过GetAlternativeServiceFor()来获取备选服务的信息:
这里主要通过GetAlternativeServiceForInternal()获取备选服务的信息:
这个函数的执行流程如下:
- 检查Url的scheme是否为https,若不是,直接返回空的AlternativeService。即备选服务只用于https。
- 从session_获取HttpServerProperties http_server_properties。
- 从http_server_properties获取所有的备选服务信息。
- 遍历上一步找到的备选服务,找到一个可用的。
在Chromium net中,以https为scheme的请求有多种,一是常规的HTTP/1.1 + TLS的请求,二是SPDY/HTTP2请求,三是QUIC协议的请求。备选服务主要用于后两种协议的请求。这里会根据同源策略、端口、协议是否打开及主机是否在白名单等判断一个备选服务是否可用。
我们可以看到,备选服务的所有信息,都来源于http_server_properties。http_server_properties来源于HttpNetworkSession。HttpNetworkSession的http_server_properties在URLRequestContextBuilder::Build()中创建:
Cronet库的初始化过程中,会执行CronetURLRequestContextAdapter::InitializeOnNetworkThread(),在这个方法中,通过URLRequestContextBuilder构建了URLRequestContext之后,会向其中添加备选服务的信息:
这里更是限定了只允许给QUIC协议添加备选服务。而这里添加的备选服务的信息都来自于URLRequestContextConfig。
继续来看给HttpServerProperties添加备选服务信息的过程 (chromium_android/src/net/http/http_server_properties_impl.cc):
HttpServerPropertiesImpl用一个Map来管理备选服务的信息,key为原始服务的scheme+host+port,用url::SchemeHostPort来表示,而value则为AlternativeServiceInfoVector,即备选服务信息的列表。
用户添加备选服务信息
在CronetEngine.Builder中 (chromium_android/src/components/cronet/android/api/src/org/chromium/net/CronetEngine.java),提供了接口,来添加QUIC服务器的一些信息:
在CronetUrlRequestContext创建中,创建native UrlRequestContextConfig时会将所有的QUIC hint信息传递给native层。
nativeAddQuicHint()在chromium_android/src/components/cronet/android/cronet_url_request_context_adapter.cc中定义:
备选服务对HttpStreamFactoryImpl::Job::Job的操作的影响
为备选服务和为常规服务会以略有不同的方式创建Job:
为QUIC备选服务创建的Job,在创建时期,就会将usingquic置为true。这个标记的设置将对后续创建Stream的过程产生决定性的影响。
总结一下,备选服务机制像是一种过渡方案,用于在协议开发早期,还没有确定协议协商机制的情况下。在chromium net中,scheme为https的请求所用的协议有可能是HTTP/1.1+TLS、HTTP2和QUIC这三种的任一种,其中前两种都是基于TCP的,而QUIC是基于UDP的。当前前面的两种协议已经有了NPN和ALPN这样的协议协商的机制,而传给chromium net一个scheme为https的QUIC请求的URL,它也是不知道要用QUIC协议来做请求的。而备选服务机制,则允许chromium net的用户指定,对某些主机的访问采用特定的协议进行。此外,在HttpStreamFactoryImpl::JobController的CreateJobs()中,在为备选服务创建Job之外,还是会创建main_job,即是说,传给chromium net一个以https为scheme的Url,它一定会尝试用TCP的方式建立连接的,只是对于请求QUIC协议的服务,这个连接将会失败,而真正取回数据的将是alternative_job。