源碼地址:https://github.com/square/okhttp
不知不覺已經來到了最後一個攔截器,前面做了各種處理,也建立了連接。接下來的CallServerInterceptor,應該就是對數據進行交換、讀取以及構建結果的類了。
不過其實在這個攔截器之前,還可以自定義networkInterceptors。
在Okhttp的攔截器鏈條裏面有兩個地方可以自定義攔截:
-
最開始的時候(Interceptors):對發出去的請求做最初的處理,以及在拿到最後Reponse時候做最後的處理;
-
最後數據交換前(networkInterceptors):對發出去的請求做最後的處理,以及在拿到結果時候做最初的處理。
我們可以自定義攔截器,去處理我們需要做的事情。
下面言歸正傳:
intercept
和其他攔截器一樣,最重要的方法就是這個intercept方法,攔截方法。
這個攔截方法主要步驟:
-
獲取幾個前面已經創建的重要類(httpCodec, streamAllocation, request);
-
先向sink(OutputStream)中寫頭信息(sink, 在創建連接時候已經創建好);
-
判斷是否有請求體,如有,走4,5的操作,沒有直接到6;
-
如果頭部添加了"100-continue", 相對於一次見到的握手操作,只有拿到服務的結果再繼續;
-
當“100-continue”成功或者不需要這個簡單握手的,寫入請求實體;
-
finishRequest( 實際是調用了 sink.flush(), 來刷數據 )
-
讀取頭部信息(通過source(InputStream), 讀取頭部信息,狀態碼等)
-
構建Response, 寫入原請求,握手情況,請求時間,得到的結果時間
-
通過Response 狀態碼判斷以及是否webSocket判斷,是否返回一個空的body, 或者讀取Body信息(通過 source(InputStream) 讀取);
-
讀取到請求時的連接close ,或者服務器返回的 close, 進行斷開操作;
-
對於204,205的特殊狀態碼進行處理。
** 有兩個比較重要的類,就是sink以及source, 這個兩個是Okio裏面的一個重要概念,相當於我們熟悉的 OutputStream 以及 InputStream輸入輸出流。** 這裏就不展開,有興趣的可以瞭解一下 Okio這個 I/O 庫。這兩個類都是在創建連接的時候就已經建立,而在個攔截器裏面就是對數據進行交換。
下面是源碼閱讀:
@Override public Response intercept(Chain chain) throws IOException {
// 1.獲取幾個前面攔截方法創建的,重要類
HttpCodec httpCodec = ((RealInterceptorChain) chain).httpStream();
StreamAllocation streamAllocation = ((RealInterceptorChain) chain).streamAllocation();
Request request = chain.request();
long sentRequestMillis = System.currentTimeMillis();
//2. 先向sink(OutputStream)中寫頭信息
httpCodec.writeRequestHeaders(request);
Response.Builder responseBuilder = null;
// 3.判斷是否有請求實體的請求,用method判斷
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
// If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
// Continue" response before transmitting the request body. If we don't get that, return what
// we did get (such as a 4xx response) without ever transmitting the request body.
//4. 如果頭部添加了"100-continue", 相對於一次見到的握手操作,只有拿到服務的結果再繼續
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
responseBuilder = httpCodec.readResponseHeaders(true);
}
//5. 當前面的"100-continue",需要握手,但又握手失敗,這個時候responseBuilder不是空的
// Write the request body, unless an "Expect: 100-continue" expectation failed.
if (responseBuilder == null) {
Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
// 回調RequestBody的writeTo,寫相應的數據
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}
}
//6. 這裏也是調用了一次 sink.flush()
httpCodec.finishRequest();
//7. 讀取頭部信息,狀態碼,信息等
if (responseBuilder == null) {
responseBuilder = httpCodec.readResponseHeaders(false);
}
//8. 構建Response, 寫入原請求,握手情況,請求時間,得到的結果時間
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
int code = response.code();
//9. 通過狀態碼判斷以及是否webSocket判斷,是否返回一個空的body
if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
//讀取Body信息
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
//10 .如果設置了連接 close ,斷開連接
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
//11. HTTP 204(no content) 代表響應報文中包含若干首部和一個狀態行,但是沒有實體的主體內容。
//HTTP 205(reset content) 表示響應執行成功,重置頁面(Form表單),方便用戶下次輸入
//這裏做了同樣的處理,就是拋出協議異常。
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
總結:
這個攔截器主要是通過連接好的通道進行數據的交換,有些細節沒有展開,只對基本的流程進行了學習記錄。留下兩個可研究的點:
-
HttpCodec是一個接口,其實有兩個實現類,分別是HttpCodec1、HttpCodec2, 對應着http1 的讀寫數據和 http2的 讀寫數據。兩者是有差異的;
-
還有前面提到的Okio,可以去了解一下。
系列(簡書地址):
OKhttp源碼學習(一)—— 基本請求流程
OKhttp源碼學習(二)—— OkHttpClient
OKhttp源碼學習(三)—— Request, RealCall
OKhttp源碼學習(四)—— RetryAndFollowUpInterceptor攔截器
OKhttp源碼學習(五)—— BridgeInterceptor攔截器
OKhttp源碼學習(六)—— CacheInterceptor攔截器
OKhttp源碼學習(七)—— ConnectInterceptor攔截器
OKhttp源碼學習(九)—— 任務管理(Dispatcher)