CallServerInterceptor
系列
OKHttp3–詳細使用及源碼分析系列之初步介紹【一】
OKHttp3–流程分析 核心類介紹 同步異步請求源碼分析【二】
OKHttp3–Dispatcher分發器源碼解析【三】
OKHttp3–調用對象RealCall源碼解析【四】
OKHttp3–攔截器鏈RealInterceptorChain源碼解析【五】
OKHttp3–重試及重定向攔截器RetryAndFollowUpInterceptor源碼解析【六】
OKHttp3–橋接攔截器BridgeInterceptor源碼解析及相關http請求頭字段解析【七】
OKHttp3–緩存攔截器CacheInterceptor源碼解析【八】
OKHttp3-- HTTP緩存機制解析 緩存處理類Cache和緩存策略類CacheStrategy源碼分析 【九】
通過ConnectInterceptor源碼掌握OKHttp3網絡連接原理 嘔心瀝血第十彈【十】
前言
這篇文章將對OKHttp最後一個攔截器進行解析,總算快要結束了;上一篇文章講到連接攔截器,即客戶端已經與服務端進行了連接,那接下來的操作自然就是發送接收數據了,看看官網的註釋
這是攔截器鏈上的最後一個攔截器,向服務器發起網絡訪問
那接下來就從源碼看看它是如何實現發送請求數據,接收響應數據的
CallServerInterceptor
@Override
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
httpCodec.writeRequestHeaders(request);
Response.Builder responseBuilder = null;
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.
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
responseBuilder = httpCodec.readResponseHeaders(true);
}
if (responseBuilder == null) {
// Write the request body if the "Expect: 100-continue" expectation was met.
Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
} else if (!connection.isMultiplexed()) {
// If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection from
// being reused. Otherwise we're still obligated to transmit the request body to leave the
// connection in a consistent state.
streamAllocation.noNewStreams();
}
}
httpCodec.finishRequest();
if (responseBuilder == null) {
responseBuilder = httpCodec.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
int code = response.code();
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 {
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
這裏代碼我們分開看
第一部分:獲取網絡組件
RealInterceptorChain realChain = (RealInterceptorChain) chain;
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
這裏通過攔截器鏈拿到了四個對象
- HttpCodec : 封裝I/O操作的對象,根據HTTP/1.x,HTTP/2版本,有兩個對應的實現類Http1Codec、Http2Codec,可以簡單理解爲編碼我們的請求,解碼返回的響應
- StreamAllocation:封裝了一次網絡操作需要的組件,爲一個請求分配一個stream
- RealConnection: 封裝了一次Socket與服務器連接的對象,在OKHttp中將一次連接抽象爲Connection,而它是其實現類
- Request:封裝用戶的請求信息的對象
第二部分:發送請求
long sentRequestMillis = System.currentTimeMillis();
httpCodec.writeRequestHeaders(request);
Response.Builder responseBuilder = null;
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
responseBuilder = httpCodec.readResponseHeaders(true);
}
if (responseBuilder == null) {
Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
} else if (!connection.isMultiplexed()) {
streamAllocation.noNewStreams();
}
}
httpCodec.finishRequest();
這部分主要是用來發送請求
- 獲取發送請求的時間戳,並將請求頭信息寫入
- 檢測是否爲有body的請求方法,接下來有兩個判斷,分開講述
第一個if判斷的意思是:如果請求頭中包含【Expect: 100- Continue】,那麼就發送100-continue的請求,然後獲取服務器的響應,看服務器是否同意接受請求體,如果服務器的響應碼是HTTP_CONTINUE,即100,那這個responseBuilder 即爲null
第二個判斷:上面一個判斷可以知道如果服務器同意接受請求體數據,那麼responseBuilder 爲null,接下來就構建輸出流BufferedSink對象,將請求體數據寫到其緩衝區
第三個判斷:也就是responseBuilder 不爲null,意思是服務器不同意接受該請求體,且同時這個連接不支持多路複用,也就是說這是一個Http/1.x的請求,那麼我們就需要標記該連接不能再被複用,同時關閉相關的Socket
接下來就是將緩存區的數據寫到Socket中,發送出去,到這裏發送請求已經完成
第三部分:構建響應
if (responseBuilder == null) {
responseBuilder = httpCodec.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
首先獲取響應頭部信息,然後構建響應Response 對象
第四部分:處理響應
int code = response.code();
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 {
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
- 如果使用websocket且響應碼是101,那麼構建一個空響應體的response 對象;否則就根據響應體構建真正的響應對象
- 如果請求頭或者響應頭部包含【Connection: close】,意味着該連接不需要保活,那就關閉該連接以及釋放相應的資源
- 如果響應碼是204或者205,且響應體長度大於0,也就意味着該次請求成功,但是服務器只返回了響應行和響應頭信息,沒有響應體,可是這裏拿到的響應體長度居然大於0,那就說明出問題了,所以需要拋出一個協議異常
總結
可以看到最後一個攔截器的代碼比較簡單,當然了你要是沒有看前面的系列文章,可能會覺得有點懵;攔截器鏈上的五個攔截器分析完了,後續的文章將會介紹下OKHttp中有關WebSocket的知識點