2019獨角獸企業重金招聘Python工程師標準>>>
1.前言
? ? ?ASIHttprequest 是基于CFNetwork的,由于CFNetwork是比較底層的http庫,功能比較少,因此,在ASIHttprequest中實現了http協議中比較多的功能,包括代理、gzip、認證、緩存等等。目前,雖然ASIHTTPRequest已經不如前兩年那么流行,但是分析一下其代碼,對掌握CFNetwork庫和HTTP協議還是有好處的,本文將簡單分析一下ASIHTTPRequest中幾個主要函數的流程。
?
2.處理HTTP Request的主要函數
ASIHTTPRequest::main流程如下(只列舉了主要工作):
{
? ? ?(1)若允許后臺運行,則調用beginBackgroundTaskWithExpirationHandler允許程序后臺運行十分鐘
? ? ?(2) 調用buildPostBody函數構造post的body部分,該函數有兩個,一個是基類中的,主要負責壓縮body數據,另一個是派生類ASIFormDataRequest中,分別針對post表單和post文件的方式,分別設置content-type為application/x-www-form-urlencoded和multipart/form-data。
? ? ?(3)根據url和請求的方法來創建CFHTTPMessageRef對象。
? ? ?(4)調用buildRequestHeaders函數來構造header部分,這里只是簡單的將各個header字段放到一個NSDictionary變量中。
? ? ?(5)如果設置了緩存,并且允許從緩存取數據,則從緩存中讀取數據,然后返回。
? ? ?(6)調用applyAuthorizationHeader向header字典中添加HTTP認證相關的字段。
? ? ?(7)調用CFHTTPMessageSetHeaderFieldValue來將header數據添加到CFHTTPMessageRef對象中。
? ? ?(8)調用configureProxies來配置代理。
? ? ?(9)調用startRequest來發送請求。
}
?
ASIHTTPRequest::startRequest
{ ??
? ? ? (1)向主線程發送requestStarted消息
? ? ? (2)如果存在body,且需要post本地文件,則將本地文件讀到postBodyReadStream對象中。然后調用CFReadStreamCreateForStreamedHTTPRequest函數,傳入之前創建的CFHTTPMessageRef對象和postBodyReadStream對象,來創建一個用來讀取response 的CFReadStream對象。
? ? ? ? ? ? ?如果是post數據,則先根據shouldCompressRequestBody的值來判斷是否要壓縮,然后根據postBody的數據來創建一個NSInputStream對象,并賦給postBodyReadStream對象,然后調用CFReadStreamCreateForStreamedHTTPRequest,傳入之前的header和stream對象,來創建NSReadStream對象。
? ? ? ? ? ? ?如果不存在body,則直接通過CFReadStreamCreateForHTTPRequest函數來創建NSReadStream對象。
? ? (3)針對https的情況,調用CFReadStreamSetProperty進行設置
? ? (4)如果請求中設置了代理,則調用CFReadStreamSetProperty對stream進行代理相關的設置
? ? ?(5)處理http持久連接相關的設置
? ? (6)調用scheduleInRunLoop,將readStream對象放入runloop中
? ? (7)調用CFReadStreamSetClient函數來將readStream關聯到一個回調函數ReadStreamClientCallBack中,并使用CFReadStreamOpen打開readStream對象
? (8)調用進度通知相關的函數
? ?(9)創建一個計時器,用來調用updateStatus函數來更新進度,并將計時器放入當前runloop。
}
?
3.處理HTTP Response的主要函數?
ASIHTTPRequest::handleNetworkEvent (該函數用來處理回調事件)
{
? ? (1)當收到kCFStreamEventHasBytesAvailable 事件時 ,調用handleBytesAvailable(此時表示下層已經讀到了response里的數據,這數據可能包含全部的header也可能header尚未讀完)
? ? (2)kCFStreamEventEndEncountered,調用handleStreamComplete,此時表示全部的數據包括header和body都已經讀完,而且對應chunked數據,底層也已經將其合并完。
? ? (3)kCFStreamEventErrorOccurred事件,調用handleStreamError處理錯誤
}
?
ASIHTTPRequest::handleBytesAvailable
{
? ? (1)如果responseHeader對象尚未賦值,則調用readResponseHeaders讀取header
? ? (2)申請一塊buffer,讀取readStream對象里面的數據,此時如果能讀到數據,則表示header已經讀完了,當前讀到的是body里面數據(因為header不是用read方法讀的),如果讀不到數據,則表示還沒有收到body,則返回。
? ? (3)讀取到數據之后,如果header里面顯示數據是壓縮過的,則進行解壓縮
? ? (4)解壓出數據之后有三種處理方式:
? ? ? ? ? ? ?如果用戶設置了didReceiveDataSelector或者dataReceivedBlock,這就表示用戶希望自己處理每次得到的data,則向主線程發送passOnReceivedData消息。
? ? ? ? ? ? ?如果用戶在request中設置了下載路徑,則將數據寫到文件中
? ? ? ? ? ? ?如果以上都不滿足,則將數據append到rawResponseData中。
}
ASIHTTPRequest::readResponseHeaders
{
? ?(1)使用CFReadStreamCopyProperty從readStream對象中讀取header,創建一個CFHTTPMessageRef對象,并且使用CFHTTPMessageIsHeaderComplete檢查該對象,判斷header是否已經讀完,若沒有讀完,則銷毀該對象并返回
? ?(2)?使用CFHTTPMessageCopyAllHeaderFields從CFHTTPMessageRef讀出header到一個dictionary中
? ? (3)如果有緩存,且允許讀取緩存,則從緩存中讀取header并返回。
? ?(4)根據header中的狀態碼來判斷是否需要進行http認證,如果需要則處理認證相關的工作
? ?(5) 從header中content-type,用于對body進行解碼,如沒有該字段,則使用默認的解碼方式對content進行解碼
? ?(6)處理cookie相關的工作
? ?(7)如果不需要重定向,則從header中讀取content-length,然后根據length做相關處理
? ?(8)處理keepalive相關的工作
? ?(9)最后向主線程發送requestReceivedResponseHeaders通知
}
ASIHTTPRequest::handleStreamComplete
{
? ? ?該函數做的事情比較簡單,主要就是設置各種下載結束的標志、設置讀取到的文件大小并發送通知消息、移動下載的臨時文件、保存cache等等。
}