okhttp之CallServerInterceptor 分析

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)體

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容