CallServerInterceptor是okhttp中的最后一個(gè)攔截器,用來(lái)向服務(wù)器發(fā)送客戶端的請(qǐng)求數(shù)據(jù),并且封裝服務(wù)器返回來(lái)的Response。開(kāi)始分析代碼:
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
//獲取到HttpCodec
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
realChain.eventListener().requestHeadersStart(realChain.call());
//向服務(wù)器中寫(xiě)入請(qǐng)求頭
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(realChain.call(), request);
Response.Builder responseBuilder = null;
//如果請(qǐng)求中有請(qǐng)求體
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.
//如果請(qǐng)求頭中包含Expect:100-continue的請(qǐng)求頭
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
realChain.eventListener().responseHeadersStart(realChain.call());
//讀取服務(wù)端返回的響應(yīng)
//readResponseHeaders(true)這個(gè)方法里面做的操作如下:
//1、讀取服務(wù)器返回的響應(yīng)頭
//2、根據(jù)響應(yīng)碼和傳入的參數(shù)判斷返回的respone.builer是否為空
// (當(dāng)傳入的參數(shù)為true(這個(gè)可以理解為客戶端希望繼續(xù)發(fā)送請(qǐng)求體)并且
// 服務(wù)端返回的狀態(tài)碼也是希望我們繼續(xù)傳入請(qǐng)求體的時(shí)候,就返回null)
responseBuilder = httpCodec.readResponseHeaders(true);
}
//如果responseBuilder為空,說(shuō)明可以繼續(xù)傳入請(qǐng)求體
if (responseBuilder == null) {
// Write the request body if the "Expect: 100-continue" expectation was met.
realChain.eventListener().requestBodyStart(realChain.call());
long contentLength = request.body().contentLength();
//下面兩句代碼可以理解為獲取到向服務(wù)端發(fā)送請(qǐng)求體的輸出流
CountingSink requestBodyOut =
new CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
//開(kāi)始向服務(wù)端寫(xiě)入請(qǐng)求體
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
realChain.eventListener()
.requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
// 這里的else if表示不需要傳入請(qǐng)求體,那么這次請(qǐng)求就算是結(jié)束了,
// 如果不是http2協(xié)議的話,這次連接就可以關(guān)閉掉了
} 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();
}
}
//執(zhí)行到這里,不管有沒(méi)有發(fā)送請(qǐng)求體,本次請(qǐng)求就算是結(jié)束了
httpCodec.finishRequest();
由于在之前的攔截器中已經(jīng)和服務(wù)器的連接打通了,在這個(gè)攔截器中只負(fù)責(zé)數(shù)據(jù)的傳輸。上面的代碼就是向服務(wù)端發(fā)送請(qǐng)求的全部了,可以看到發(fā)送請(qǐng)求一共做了如下幾步:
1、獲取到httpCodec,這是一個(gè)接口,主要是封裝了request的寫(xiě)入和response的讀取,可以理解為通過(guò)這個(gè)接口向外發(fā)送請(qǐng)求和接收數(shù)據(jù)。這個(gè)實(shí)體類(lèi)是在之前的攔截器中生成的,我們直接拿過(guò)來(lái)用便好。其實(shí)現(xiàn)類(lèi)為Http1Codec和Http2Codec,分別對(duì)應(yīng)了HTTP/1.1和HTTP/2
2、向服務(wù)端發(fā)送請(qǐng)求頭
//向服務(wù)器中寫(xiě)入請(qǐng)求頭
httpCodec.writeRequestHeaders(request);
這里使用Http1Codec中的代碼,其中用到的sinkOkIO的Sink對(duì)象(該對(duì)象可以看做Socket的OutputStream對(duì)象)
@Override public void writeRequestHeaders(Request request) throws IOException {
String requestLine = RequestLine.get(
request, streamAllocation.connection().route().proxy().type());
writeRequest(request.headers(), requestLine);
}
/** Returns bytes of a request header for sending on an HTTP transport. */
public void writeRequest(Headers headers, String requestLine) throws IOException {
if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);
sink.writeUtf8(requestLine).writeUtf8("\r\n");
//把頭部的鍵值對(duì)寫(xiě)進(jìn)輸出流中
for (int i = 0, size = headers.size(); i < size; i++) {
sink.writeUtf8(headers.name(i))
.writeUtf8(": ")
.writeUtf8(headers.value(i))
.writeUtf8("\r\n");
}
sink.writeUtf8("\r\n");
state = STATE_OPEN_REQUEST_BODY;
}
3、判斷是否需要向服務(wù)器發(fā)送請(qǐng)求體,主要通過(guò)以下幾方面判斷:
1)HttpMethod.permitsRequestBody(request.method()) && request.body() != null
通過(guò)判斷請(qǐng)求方式(比如get請(qǐng)求是不需要請(qǐng)求體的,那么就不需要發(fā)送請(qǐng)求體)和請(qǐng)求體是否為空來(lái)判斷。
2)在符合上面條件的情況下,再次判斷請(qǐng)求頭中是否有Expect:100-continue,如果有,那么就獲取獲取到請(qǐng)求頭的響應(yīng)狀態(tài)碼,判斷服務(wù)端是否想要接收請(qǐng)求體。如果服務(wù)端返回的狀態(tài)碼是100,表示接收請(qǐng)求體,那么就通過(guò)readResponseHeaders()方法返回的響應(yīng)體responseBuilder為空。為什么返回空呢?在后面的代碼中是通過(guò)responseBuilder是否為空來(lái)判斷是否需要發(fā)送請(qǐng)求體的。
//1、讀取服務(wù)器返回的響應(yīng)頭
//2、根據(jù)響應(yīng)碼和傳入的參數(shù)判斷返回的respone.builer是否為空
// (當(dāng)傳入的參數(shù)為true(這個(gè)可以理解為客戶端希望繼續(xù)發(fā)送請(qǐng)求體)并且
// 服務(wù)端返回的狀態(tài)碼也是希望我們繼續(xù)傳入請(qǐng)求體的時(shí)候,就返回null)
responseBuilder = httpCodec.readResponseHeaders(true);
Http1Codec中的代碼
@Override public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {
。。。。。
try {
StatusLine statusLine = StatusLine.parse(readHeaderLine());
//這里生成了responseBuilder
Response.Builder responseBuilder = new Response.Builder()
.protocol(statusLine.protocol)
.code(statusLine.code)
.message(statusLine.message)
.headers(readHeaders());
//但是如果code為100并且expectContinue為true(從上面?zhèn)鬟M(jìn)來(lái)的true)時(shí),返回null
//需要請(qǐng)求體,暫時(shí)不返回響應(yīng)體
if (expectContinue && statusLine.code == HTTP_CONTINUE) {
return null;
} else if (statusLine.code == HTTP_CONTINUE) {
state = STATE_READ_RESPONSE_HEADERS;
return responseBuilder;
}
state = STATE_OPEN_RESPONSE_BODY;
return responseBuilder;
} catch (EOFException e) {
// Provide more context if the server ends the stream before sending a response.
IOException exception = new IOException("unexpected end of stream on " + streamAllocation);
exception.initCause(e);
throw exception;
}
}
4、發(fā)送請(qǐng)求體
從3中判斷如果返回的responseBuilder為空,那么就發(fā)送請(qǐng)求體
//如果responseBuilder為空,說(shuō)明可以繼續(xù)傳入請(qǐng)求體
if (responseBuilder == null) {
// Write the request body if the "Expect: 100-continue" expectation was met.
realChain.eventListener().requestBodyStart(realChain.call());
long contentLength = request.body().contentLength();
//下面兩句代碼可以理解為獲取到向服務(wù)端發(fā)送請(qǐng)求體的輸出流
CountingSink requestBodyOut =
new CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
//開(kāi)始向服務(wù)端寫(xiě)入請(qǐng)求體
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
realChain.eventListener()
.requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
// 這里的else if表示不需要傳入請(qǐng)求體,那么這次請(qǐng)求就算是結(jié)束了,
// 如果不是http2協(xié)議的話,這次連接就可以關(guān)閉掉了
} 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();
}
}
// 執(zhí)行到這里,不管有沒(méi)有發(fā)送請(qǐng)求體,本次請(qǐng)求就算是結(jié)束了
httpCodec.finishRequest();
代碼中的注釋?xiě)?yīng)該已經(jīng)比較清楚了,主要是通過(guò)BufferedSink把body寫(xiě)入輸出流傳送給服務(wù)器。
寫(xiě)到這里,發(fā)送請(qǐng)求就算完成了,下面來(lái)看獲取響應(yīng)體。
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
//有請(qǐng)求體的情況下獲取responseBuilder
responseBuilder = httpCodec.readResponseHeaders(false);
}
//封裝respone響應(yīng) 寫(xiě)入原請(qǐng)求,握手情況,請(qǐng)求時(shí)間,得到的結(jié)果時(shí)間
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
上面的代碼主要是通過(guò)httpCodec.readResponseHeaders(false)獲取到response,這時(shí)的response并不包含請(qǐng)求體。然后再根據(jù)響應(yīng)碼來(lái)做不同的處理
int code = response.code();
if (code == 100) {
//如果響應(yīng)碼是100,重新讀取一遍響應(yīng)頭
// server sent a 100-continue even though we did not request one.
// try again to read the actual response
responseBuilder = httpCodec.readResponseHeaders(false);
response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
code = response.code();
}
//code=101 Switching Protocols 服務(wù)器將遵從客戶的請(qǐng)求轉(zhuǎn)換到另外一種協(xié)議
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附上了服務(wù)器返回的body
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
//根據(jù)connection值關(guān)閉連接
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
//? 204 - No Content 沒(méi)有新文檔,瀏覽器應(yīng)該繼續(xù)顯示原來(lái)的文檔。如果用戶定期地刷新頁(yè)/面,而Servlet可以確定用戶文檔足夠新,這個(gè)狀態(tài)代碼是很有用的。
//? 205 - Reset Content 沒(méi)有新的內(nèi)容,但瀏覽器應(yīng)該重置它所顯示的內(nèi)容。用來(lái)強(qiáng)制瀏覽器清除表單輸入內(nèi)容(HTTP 1.1新)。
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
關(guān)于獲取響應(yīng)體到這里就結(jié)束了,一共分兩步:
1)組裝到響應(yīng)體的頭部
2)根據(jù)頭部的響應(yīng)碼,來(lái)判斷有沒(méi)有響應(yīng)體,并組裝響應(yīng)體
到這里整個(gè)CallServerInterceptor攔截器就介紹完了,主要做了這么幾件事:
1、發(fā)送請(qǐng)求頭
2、根據(jù)請(qǐng)求方式和響應(yīng)頭來(lái)判斷是否發(fā)送請(qǐng)求體
3、如果需要發(fā)送請(qǐng)求體,通過(guò)BufferedSink寫(xiě)入請(qǐng)求體
4、封裝響應(yīng)頭
5、根據(jù)響應(yīng)碼組裝響應(yīng)體