【Android】OkHttp系列(六):CallServerInterceptor

該系列OkHttp源碼分析基於OkHttp3.14.0版本

概述

用於向服務器寫數據以及從服務器讀取數據。

整個攔截器的主要邏輯是這樣的,首先向服務器發送請求頭,如果有請求體的話就向服務器發送請求體,然後就開始讀取服務器的返回。同樣的,首先讀取服務器返回的響應頭,然後根據狀態碼以及是否是websocket協議判斷是繼續讀取響應頭還是開始讀取響應體。最後按照責任鏈,向上返回響應。

而且根據源碼可以看到,所有與服務器的讀寫操作都是由Exchange進行的,而根據上面連接攔截器ConnectInterceptor所提及的,Exchange中實際與服務器進行IO交互的是ExchangeCodec,而ExchangeCodec中封裝了Okio的IO操作。

源碼分析

發送請求

@Override public Response intercept(Chain chain) throws IOException {
  RealInterceptorChain realChain = (RealInterceptorChain) chain;
  Exchange exchange = realChain.exchange();
  Request request = realChain.request();

  long sentRequestMillis = System.currentTimeMillis();

  exchange.writeRequestHeaders(request);//發送請求頭

  boolean responseHeadersStarted = false;
  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.

    // 如果請求上有一個“Expect: 100-continue”標頭,則在發送請求正文之前,等待“ HTTP / 1.1 100繼續”響應。
    // 如果沒有得到,請返回我們得到的結果(例如4xx響應),而無需傳輸請求主體。
    if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
      exchange.flushRequest();
      responseHeadersStarted = true;
      exchange.responseHeadersStart();
      responseBuilder = exchange.readResponseHeaders(true);
    }

    if (responseBuilder == null) {//這裏判斷的是是否有請求頭返回
      // 下面開始發送請求體
      if (request.body().isDuplex()) {
        // Prepare a duplex body so that the application can send a request body later.

        // 準備一個雙工主體,以便應用程序以後可以發送請求主體。
        exchange.flushRequest();
        BufferedSink bufferedRequestBody = Okio.buffer(
            exchange.createRequestBody(request, true));
        request.body().writeTo(bufferedRequestBody);
      } else {
        // Write the request body if the "Expect: 100-continue" expectation was met.
        // 如果滿足“Expect: 100-continue”的期望,寫請求體。
        BufferedSink bufferedRequestBody = Okio.buffer(
            exchange.createRequestBody(request, false));
        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
      }
    } else {
      exchange.noRequestBody();
      if (!exchange.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.
        // 如果未達到“Expect: 100-continue”的期望,請防止HTTP / 1連接被重用。
        // 否則,我們仍然有義務傳輸請求主體,以使連接保持一致狀態。
        exchange.noNewExchangesOnConnection();
      }
    }
  } else {
    exchange.noRequestBody();
  }

  if (request.body() == null || !request.body().isDuplex()) {
    exchange.finishRequest();
  }

  ...省略部分代碼
}

整個代碼邏輯就如之前所說的,首先調用exchange.writeRequestHeaders()發送請求頭,然後調用request.body().writeTo(bufferedRequestBody)發送請求體。這裏有點特殊,發送請求體並不是由ExchangeCodec進行的,而是由BufferedSink進行的。至於爲啥要這麼做,emm,我也很好奇。

讀取響應

@Override public Response intercept(Chain chain) throws IOException {
  RealInterceptorChain realChain = (RealInterceptorChain) chain;
  Exchange exchange = realChain.exchange();
  Request request = realChain.request();

  long sentRequestMillis = System.currentTimeMillis();

  exchange.writeRequestHeaders(request);//發送請求頭

  boolean responseHeadersStarted = false;
  Response.Builder responseBuilder = null;
  
    ...省略部分代碼

  if (!responseHeadersStarted) {
    exchange.responseHeadersStart();
  }

  if (responseBuilder == null) {
    //獲得返回頭
    responseBuilder = exchange.readResponseHeaders(false);
  }

  //構建響應返回
  Response response = responseBuilder
      .request(request)
      .handshake(exchange.connection().handshake())
      .sentRequestAtMillis(sentRequestMillis)
      .receivedResponseAtMillis(System.currentTimeMillis())
      .build();

  int code = response.code();
  if (code == 100) {//100表示需要繼續想服務端發送數據
    // server sent a 100-continue even though we did not request one.
    // try again to read the actual response

    // 即使我們沒有請求,服務器仍發送了100-continue。 再次嘗試讀取返回
    response = exchange.readResponseHeaders(false)
        .request(request)
        .handshake(exchange.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    code = response.code();
  }

  exchange.responseHeadersEnd(response);

  if (forWebSocket && code == 101) {//判斷是否是websocket或者服務端返回要求切換協議
    // 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(exchange.openResponseBody(response))//這裏去拿到返回體
        .build();
  }

  if ("close".equalsIgnoreCase(response.request().header("Connection"))
      || "close".equalsIgnoreCase(response.header("Connection"))) {
    exchange.noNewExchangesOnConnection();//結束請求
  }

  if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
    //這裏應該屬於服務端的錯誤,204和205表示沒有返回體,但是這裏卻發現有返回體
    throw new ProtocolException(
        "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
  }

  return response;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章