在简单地分析了cronet的初始化过程之后,我们来看HTTP请求的提交和执行。
UrlRequest的创建
如我们在前面的Cronet android 设计与实现分析——库的初始化中看到的那样,Cronet的客户端,需要通过UrlRequest.Builder创建Request,然后提交给CronetEngine执行。这里我们从UrlRequest的创建开始我们的分析。
与CronetEngine类似,UrlRequest也需要通过Builder来创建。首先需要创建UrlRequest.Builder对象,url,callback,executor和CronetEngine是该创建过程所必须的:
为我们前面创建的Builder设置了所有我们希望定制的属性之后,我们通过调用UrlRequest.Builder.build()创建UrlRequest对象:
这里用了一种类似于 原型模式 的方法,先由CronetEngine创建一个UrlRequest对象;然后将Builder中为UrlRequest定制的一些选项,如Http method,request的header等,设置给UrlRequest;最后将UrlRequest对象返回。
我们追一下org.chromium.net.impl.CronetUrlRequestContext的createRequest()来看看实际的UrlRequest创建过程是什么:
Chromium net提供了强大的debug的能力,可以帮我们抓到许多网络请求执行过程中的信息。但要抓取这些信息,总是要更耗费资源一些。这里会检查是否存在FinishedListener,只有当FinishedListener存在时,抓取的信息才是有意义的,因而,在FinishedListener不存在时,这里会关闭对信息的抓取。
随后,这个方法创建CronetUrlRequest对象,也就是实际的UrlRequest类型,而它的构造过程如下:
这里主要就是根据传入的参数,设置了一些字段的值。
事件通知
我们创建UrlRequest的时候,总是要传入一个Executor。在CronetUrlRequest中,只有postTaskToExecutor()一个方法访问了Executor:
而CronetUrlRequest中有访问到postTaskToExecutor()的方法则有如下这些:
如在onSucceeded()和onCanceled()中:
可见,网络请求被丢给chromium之后,在发生某个事件时,native层会通过JNI机制反call java层的CronetUrlRequest的回调函数,在CronetUrlRequest的回调函数中则向Executor中抛出Task,通过客户端传入的UrlRequest.Callback,在Executor的线程中将事件抛给客户端。
可见传给UrlRequest的Executor主要是用于做事件通知的。
Chromium net URLRequest的创建
语义上而言,客户端通过调用UrlRequest的start()方法启动HTTP请求的执行。CronetUrlRequest的start()方法的实现如下:
在这个方法中会做如下这些事情:
- 检查这个Request是否已经被启动了,若已经被启动了的话,会抛出异常。
- 调用nativeCreateRequestAdapter()创建native层的UrlRequestAdapter。Cronet的大部分逻辑都在native层,因而需要通过UrlRequestAdapter来将Java层和native联系起来。UrlRequestAdapter将Java层的UrlRequest与native层的URLRequest对应起来。
- 回调RequestContext的onRequestStarted()。
- 调用nativeSetHttpMethod()来设置HTTP method。
- 调用nativeAddRequestHeader()来将HTTP header一个个传到native层。
- 设置mStarted标志,以表明Request已经被启动了。
- 调用startInternalLocked(),来启动Request。这个方法会调用nativeStart()来启动Request。
不难看出Java层的CronetUrlRequest只是native的Url Request的一个简单的包装。UrlRequestAdapter连接Java层的CronetUrlRequest和native层中实际表示Url Request的结构,就像CronetURLRequestContextAdapter将Java层的CronetEngine和native层chromium net的URLRequestContext连接起来一样。
UrlRequestAdapter和native层中实际表示Url Request的结构,没有在CronetUrlRequest创建的时候创建,而是在用户实际要启动请求的时候才开始创建。
nativeCreateRequestAdapter()的实现在chromium_android/src/out/Default/gen/components/cronet/android/cronet_jni_headers/cronet/jni/CronetUrlRequest_jni.h中,由构建系统自动产生。它将职责完全委托给CreateRequestAdapter()方法:
在这里主要是创建了一个URLRequestAdapter对象,并将对象的地址返回给Java层保存,像CronetURLRequestContextAdapter的创建那样。
语义上而言,nativeStart()被CronetUrlRequest用于启动请求的执行,其定义同样在chromium_android/src/out/Default/gen/components/cronet/android/cronet_jni_headers/cronet/jni/CronetUrlRequest_jni中。它将职责委托给CronetURLRequestAdapter的Start()方法:
这个函数向CronetURLRequestContextAdapter的network thread抛了一个task,也就是CronetURLRequestAdapter::StartOnNetworkThread:
到这里才会真正的通过URLRequestContext的CreateRequest()创建chromium net的URLRequest。
可见我们调用Java层的CornetUrlRequest的start()及其内部调用的nativeStart(),只是Url Request相关结构的创建过程的延续而已,Url Request相关结构主要是指用于连接Java层的CronetUrlRequest和chromium net的URLRequest的CronetURLRequestAdapter和chromium net的URLRequest。chromium net的URLRequest的创建及启动,都是在CronetURLRequestAdapter::Start(),也就是Java层的CornetUrlRequest的nativeStart()方法的最后,抛向另外的一个线程中的task中完成的。
继续来看URLRequestContext的CreateRequest()创建URLRequest的过程,这个过程倒是蛮直接的(chromium_android/src/net/url_request/url_request_context.cc):
这里创建chromium net的URLRequest时,传入的URLRequest::Delegate是CronetURLRequestAdapter::StartOnNetworkThread传进来的,也就是CronetURLRequestAdapter对象本身,而NetworkDelegate则是来自于URLRequestContext的networkdelegate。可见,CronetURLRequestAdapter不仅用于Java层向chromium net的URLRequest传递信息,也会被chromium net的URLRequest用于向Java层通知事件。
net::URLRequest::Delegate的定义如下:
而CronetURLRequestAdapter对这些回调函数的实现则为:
这些函数都通过JNI机制反call到Java层的回调。
CronetURLRequestAdapter::StartOnNetworkThread中创建了chromium net的URLRequest之后会为它设置HTTP请求的各种参数。在CronetUrlRequest.start()中,通过nativeSetHttpMethod()、nativeAddRequestHeader()等方法向native层设置的Http method和Http headers等参数都是暂存在CronetURLRequestAdapter中的,而这里会将那些参数真正的设置给chromium net的URLRequest。
HTTP请求执行
创建了Chromium net的URLRequest之后,就是请求的执行了。这通过URLRequest::Start()来完成:
URLRequest::Start()首先会初始化LoadTimingInfo结构load_timinginfo,这个结构用于记录请求执行的一些时间信息;其次,如果networkdelegate存在的话,则会调用networkdelegate的回调方法NotifyBeforeURLRequest()等;然后,通过URLRequestJobManager创建创建一个URLRequestJob对象;最后,调用URLRequest::StartJob()启动URLRequestJob。
URLRequestJob的创建
如我们在前面看到的,URLRequest::Start()通过URLRequestJobManager创建URLRequestJob对象。URLRequestJobManager被设计为单例的类,其构造函数和析构函数被声明为private,而其对象的创建、获取和销毁则是借助于模板base::Singleton来完成的,为了方便模板中访问private构造函数和析构函数,这里(chromium_android/src/net/url_request/url_request_job_manager.h)声明了friend struct:
URLRequestJobManager的GetInstance()方法定义(chromium_android/src/net/url_request/url_request_job_manager.cc)如下:
Singleton模板类在chromium的base模块中(chromium_android/src/base/memory/singleton.h)定义:
URLRequestJobManager的CreateJob创建URLRequestJob (chromium_android/src/net/url_request/url_request_job_manager.cc):
URLRequestJob是通过factory创建的。根据URL的scheme的不同,也就是协议的不同,而使用不同的factory创建URLRequestJob对象。在这里会首先判断URL的协议是能被处理。chromium net能处理的协议主要有两类,一类是只要在编译期enable了,就always处理的,如http、https、ws和wss;另一类协议,则不仅要在编译期enable对相关协议的支持,在运行期创建CronetEngine/URLRequestContext时,还可以动态的打开或关闭对相关协议的处理,如data、file、ftp等。
前一类协议的相关信息主要由URLRequestJobManager维护,而后一类协议的信息则由URLRequestContext的URLRequestJobFactory维护。URLRequestJobFactory的实际类型是在URLRequestContextBuilder::Build()中(chromium_android/src/net/url_request/url_request_context_builder.cc)确定的,实际类型为URLRequestJobFactoryImpl:
判断协议是否支持,也是通过LRequestContext的URLRequestJobFactory,也就是URLRequestJobFactoryImpl来完成的:
这里判断可以处理的两种类型的协议的列表中是否包含了请求的协议。
URLRequestJobManager维护的,只要编译期enable就always处理的协议的信息通过一个表kBuiltinFactories来维护(chromium_android/src/net/url_request/url_request_job_manager.cc):
URLRequestJobManager的CreateJob(),在确定URL的协议是支持的之后,会首先尝试使用URLRequestContext的URLRequestJobFactory来创建URLRequestJob,如果失败,则会使用builtin的URLRequestJob的factory。
这里可以看到chromium对URL请求的处理的灵活性。URLRequestJobManager builtin的URLRequestJob的factory,定义了一个http和https协议处理的实现,但通过这里的这套机制,chromium net也可以插入自己定义的http协议处理的实现。
对于http协议和https协议,默认情况下,其URLRequestJob由URLRequestHttpJob::Factory()工厂方法创建。这里我们来看一下它的定义(chromium_android/src/net/url_request/url_request_http_job.cc):
URLRequestHttpJob::Factory()
工厂方法会首先判断URLRequestContext
的http_transaction_factory
是否存在,不存在时创建URLRequestErrorJob
并返回。
否则,尝试通过MaybeInternallyRedirect()
创建URLRequestRedirectJob
,若成功则返回给调用者。
URLRequestRedirectJob
创建失败时,则会创建URLRequestHttpJob
。MaybeInternallyRedirect
中创建URLRequestRedirectJob的过程如下(chromium_android/src/net/url_request/url_request_http_job.cc):
URLRequestRedirectJob
主要用于将非安全的访问方式转换为安全的访问方式。这里首先会判断URL的scheme是否已经是Security的https或wss,若是,则无需再做其它的而直接返回。
其次,会借助于URLRequestContext
的TransportSecurityState
判断要访问的主机是否强制将访问方式确定为security的,若不是,则用非安全的方式访问就可以,而直接返回。
最后,要访问的主机强制要求访问方式为security的,而URL的scheme不是https或wss的情况,则先修改URL的scheme为security的,如https或wss,然后创建URLRequestRedirectJob。
URLRequestRedirectJob的执行
URLRequestRedirectJob的构造过程(chromium_android/src/net/url_request/url_request_redirect_job.cc)如下:
在URLRequest::Start()中我们看到,会通过调用URLRequestJob::Start()来启动执行。这里我们看一下URLRequestRedirectJob的Start()实现:
这里主要是抛了一个task,也就是URLRequestRedirectJob::StartAsync(),给TaskRunner执行:
这里主要是构造了一个HttpResponseHeaders
,继而调用了URLRequestJob::NotifyHeadersComplete()
。在URLRequestJob::NotifyHeadersComplete()
中会对redirect的响应作出适当的处理:
这里先通过GetResponseInfo()函数读取URL请求的响应信息,包括headers等;然后检查响应是否是重定向的响应,若是,则从响应的headers中抓取重定向信息,然后将这一事件通知给客户端;若客户端不follow重定向,则保存重定向信息并退出;若客户端要follow重定向,则通过FollowRedirect()
来follow重定向:
重定向的任务被委托给了URLRequest来完成:
在URLRequest的Redirect()中,是做了一些准备工作之后,再次调用URLRequest的Start()。
URLRequestHttpJob的执行
在URLRequestHttpJob::Factory()中,不需要重定向时会直接创建URLRequestHttpJob用以处理URLRequest。这里来看URLRequestHttpJob的执行(chromium_android/src/net/url_request/url_request_http_job.cc):
这里主要做了三件事:
- 将URLRequest里的一些关于URL请求的信息拷贝出来放进requestinfo结构里。
- 添加一些用户没有显式地设置的HTTP header,如UserAgent等,及其它一些headers。
- 添加Cookie header并启动执行。当然是在Cookie enable的情况下,否则就是直接启动执行。
由AddExtraHeaders()的定义可以看到,其它的headers主要是指AcceptEncoding和AcceptLanguage这两个。
添加Cookie header并启动的过程则大体为:
如果不需要设置Cookies,则会直接调用DoStartTransaction()启动HTTP请求执行;否则,通过cookie_store异步地获取CookieCookieList,并设置给URLRequestHttpJob,然后调用DoStartTransaction()。
DoStartTransaction():
DoStartTransaction()首先检查一下请求是否被取消,如果已经取消了,会调用NotifyCanceled()来通知取消事件。
否则会调用StartTransaction()启动Transaction。这里会根据是否设置了回调,而有着不同的执行路径。如果没有设置回调,会直接调用StartTransactionInternal()启动Transaction。
而如果设置了回调,则先调用回调,并记录一些trace信息,然后调用StartTransactionInternal()。
来看StartTransactionInternal():
在这个函数中,做了这样一些事情:
- 获取URLRequestContext的NetworkQualityEstimator,以向其通知Transaction启动事件。
- 向network_delegate通知Transaction启动事件。
- 如果已经创建了HttpTransaction,则restart。
- 还没有创建HttpTransaction的情况,先通过URLRequestContext的HttpTransactionFactory创建HttpTransaction。
- 处理WS和WSS的情况。
- 为HttpTransaction设置BeforeNetworkStartCallback和BeforeHeadersSentCallback回调。
- 执行HttpTransaction的Start()。
- Post一个Task URLRequestHttpJob::OnStartCompleted()。
HttpTransaction的创建
HttpTransaction由URLRequestContext的HttpTransactionFactory创建的。回顾URLRequestContextBuilder::Build()的如下这段代码:
这里根据是否启用Cache,而使用不同的HttpTransactionFactory。启用了Cache时,会使用HttpCache作为HttpTransactionFactory;而没有启用Cache时,则会使用HttpNetworkLayer作为HttpTransactionFactory。
在URLRequestHttpJob::StartTransactionInternal()中会调用HttpTransactionFactory的CreateTransaction()来创建Transaction。
HttpCache的CreateTransaction()定义(chromium_android/src/net/http/http_cache.cc)如下:
这里是创建类型为HttpCache::Transaction的对象。HttpCache::Transaction (chromium_android/src/net/http/http_cache_transaction.cc)看起来还是蛮复杂的:
然后来看HttpNetworkLayer (chromium_android/src/net/http/http_network_layer.cc)的CreateTransaction()函数:
这里创建的则是HttpNetworkTransaction。
至此,http请求启动的过程就基本分析完了。