上一節(jié)我們介紹了Chrome對于DNS協(xié)議的解析,今天我們繼續(xù)介紹一個更為大家所熟知的協(xié)議,HTTP協(xié)議。
HTTP協(xié)議是一種很常見的協(xié)議,在chromium網(wǎng)絡庫中,對HTTP的解析主要是分為兩部分,一部分是去緩存數(shù)據(jù)的獲取,另外一部分則是重新加載網(wǎng)絡資源。簡單點說就是當我們在瀏覽器中輸入http://www.bytedance.net的時候,優(yōu)先去瀏覽器中的緩存數(shù)據(jù)查找是否存在相應的資源,如果存在則直接加在緩存中的數(shù)據(jù),否則就需要建立TCP連接,進行網(wǎng)絡數(shù)據(jù)的加載。對于剛才舉的那個例子,要看在chrome中是否已經(jīng)有緩存數(shù)據(jù)存在,只需要在瀏覽器中輸入 chrome://cache/ 即可查看:
和DNS一樣,在net網(wǎng)絡庫中的HTTP源碼文件多達上百個,一個個去看效率低并且也學不到什么有用的東西,還是像上次一樣先找出其主流程,分析關鍵函數(shù),了解整個過程的來龍去脈,最后才是逐個擊破。
可以把HTTP的資源加載看作是瀏覽器解析一個用戶指定的URL的過程(實際上URL不僅僅包含http,還包含https、ftp等等協(xié)議),而整個chrome的net庫對外的一個大的接口函數(shù)就是UrlRequest,顧名思義,這個函數(shù)代表一個URL請求,net庫就是要根據(jù)這個請求,快速準確地返回相應的數(shù)據(jù),這個函數(shù)的文件位于 /net/url_request。 當我們在瀏覽器中輸入http://www.bytedance.net時,通過URL解析,得到其scheme是http,這個時候就會調(diào)用到HTTP模塊進行相應的處理,關于這一部分的詳細過程,后續(xù)我會再找機會詳細分析URL的解析過程。
當HTTP模塊收到這個URL解析請求時,就像我們最先提到的那樣,會先去調(diào)用HttpCache::Transaction::Start 這個方法,這個函數(shù)的部分代碼如下:
intHttpCache::Transaction::Start(constHttpRequestInfo* request,constCompletionCallback& callback,constBoundNetLog& net_log) {
…
next_state_ = STATE_GET_BACKEND;intrv = DoLoop(OK);// Setting this here allows us to check for the existence of a callback_ to// determine if we are still inside Start.if(rv == ERR_IO_PENDING) {
callback_ = tracked_objects::ScopedTracker::TrackCallback(
FROM_HERE_WITH_EXPLICIT_FUNCTION("422516 HttpCache::Transaction::Start"),
callback);
}returnrv;
}
可以看到這部分代碼主要是啟動了一個狀態(tài)機,并且制定了初始狀態(tài)STATE_GET_BACKEND 和 結束狀態(tài) ERR_IO_PENDING。關于這個結束狀態(tài),是為了防止狀態(tài)機阻塞后不能及時結束而導致程序無法繼續(xù)執(zhí)行,因此當狀態(tài)碼為ERR_IO_PENDING,則直接跳出該狀態(tài)機循環(huán)。啟動這個狀態(tài)機是為了從緩存中獲取我們想要的數(shù)據(jù),這部分的流程分支有很多,光狀態(tài)機的state就有40種之多,作者也將一些流程分支都整理了出來(這里只貼了一部分,想看完整版的可以去http_cache_transaction.cc 這個文件中看)
大概看了下,好家伙,有14種狀態(tài),這還只是作者列出來的,一些異常情況還未考慮進來,但是從根本上來看,大致可以分為兩種情況:
情況一就是Cached entry,這部分狀態(tài)就代表我們從緩存數(shù)據(jù)中找到了想要的資源,直接拿過來用就可以了,不需要再去進行網(wǎng)絡請求。
情況二就是Not-cached entry,這部分狀態(tài)代表我們并沒有從緩存中找到想要的數(shù)據(jù),因此我們需要調(diào)用 SendRequest 來實現(xiàn)網(wǎng)絡加載資源的請求,如果調(diào)用成功,即我們通過網(wǎng)絡請求獲取了想要的資源,除了將資源返回給用戶之外,還需要調(diào)用CacheWriteData 來更新緩存中的數(shù)據(jù),這樣等下次訪問時,就可以直接在cache命中。
再來看一下讀取緩存數(shù)據(jù)的流程:
首先是狀態(tài)變更:GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse* -> CacheDispatchValidation
用文字解讀一下就是,初始化Entry,打開這個Entry,根據(jù)URL獲取Entry數(shù)據(jù),讀取數(shù)據(jù)并且構造響應消息體。
這里的cache_key_實際上就是URL,通過這個值便可以唯一檢索到緩存中的數(shù)據(jù)。
以上是HttpCache::Transaction狀態(tài)機的實現(xiàn)概述,簡單點說就是以URL為key值在緩存中查找數(shù)據(jù),如果匹配成功則直接返回,如果不成功則需要發(fā)送網(wǎng)絡請求,這就涉及到我們接下來要介紹的HttpNetworkTransaction了。
當我們需要調(diào)用網(wǎng)絡加載所需資源時,就要把狀態(tài)機狀態(tài)置為STATE_SEND_REQUEST,此時就會調(diào)用到下面這個函數(shù):
我們注意到這個函數(shù)最后是調(diào)用了network_trans中的Start方法,此時便會調(diào)用到
int HttpNetworkTransaction::Start 這個函數(shù)里來,同樣的,在該函數(shù)中也新啟動了一個狀態(tài)機,并且將初始狀態(tài)設置為 STATE_NOTIFY_BEFORE_CREATE_STREAM :
NetworkTransaction層的狀態(tài)機狀態(tài)碼有以下幾種,比之前的cache層要少很多
順著這個狀態(tài)碼整理NetworkTransaction層的大致流程如下:
DoCreateStream —> DoInitStream —> DoGenerateProxyAuthToken —> DoGenerateServerAuthToken —> DoInitRequestBody —> DoBuildRequest —> DoSendRequest —> DoReadHeaders —> DoReadBody
用文字解釋一下就是:創(chuàng)建strem,初始化參數(shù),建立連接,初始化請求主體,發(fā)送請求,并且讀取相應的返回信息,最后根據(jù)需要處理連接。
這里要提一下的是DoSendRequest方法,它實際上調(diào)用的是HttpBasicStream::SendRequest的方法,而HttpBasicStream則調(diào)用HttpStreamParser中的SendRequest方法來實現(xiàn),其大致思路就是創(chuàng)建連接,構造消息體,調(diào)用tcp socket進行消息頭和數(shù)據(jù)的傳輸,最終將結果返回給NetworkTransaction層,有興趣的同學可以去看一下代碼的具體實現(xiàn),這部分代碼位于 http_stream_parser.cc 文件中。
介紹完NetworkTransaction層的網(wǎng)絡傳輸層以后,還會做一個動作,那就是將得到的數(shù)據(jù)寫入到cache中去,這個我們在之前已經(jīng)提到過,這里就不再贅述了。
關于HTTP的解析就先介紹到這里,簡單總結一下,對于一個URL請求,當解析為HTTP請求時,先去cache層中查找,如果找到了,則構造數(shù)據(jù)并返回;如果沒找到,再去調(diào)用network層發(fā)起網(wǎng)絡請求,通過調(diào)用底層的tcp socket實現(xiàn)數(shù)據(jù)的傳輸,當完成加載以后,再將其寫入到cache層中。當然,這只是最常見的一種流程,關于HTTP還有許多需要探索和學習的地方,這些都留在以后的章節(jié)吧。