OkHttp Interceptors(四)

weixin_34357887發表於2018-05-18

本文中原始碼基於OkHttp 3.6.0

這篇文章分析 OkHttp 請求流程中的最後一個 Interceptor 節點 - CallServerInterceptor,它的任務就是向伺服器發起最終的請求,完成寫入請求,讀取響應的工作。 ####- CallServerInterceptor 來看請求執行的入口 intercept 函式。

public Response intercept(Chain chain) throws IOException {
  HttpCodec httpCodec = ((RealInterceptorChain) chain).httpStream();
  StreamAllocation streamAllocation = ((RealInterceptorChain) chain).streamAllocation();
  Request request = chain.request();

  long sentRequestMillis = System.currentTimeMillis();
  // 寫入請求 Header
  httpCodec.writeRequestHeaders(request);

  Response.Builder responseBuilder = null;
  // 如果請求允許攜帶 Body,寫入 Body
  if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
    // 如果請求 Header 中包含了 "Expect: 100-continue”,其表示客戶端希望伺服器告知自己是否允許攜帶 Body,
    // 如果伺服器返回狀態碼為100,則表示允許攜帶請求體,那麼 readResponseHeaders 返回 null。
    if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
    // 重新整理緩衝區,執行請求
      httpCodec.flushRequest();
      responseBuilder = httpCodec.readResponseHeaders(true);
    }

    // 寫入請求 Body
    if (responseBuilder == null) {
      Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
      BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
      request.body().writeTo(bufferedRequestBody);
      bufferedRequestBody.close();
    }
  }

  // 重新整理緩衝區,執行請求
  httpCodec.finishRequest();

  // 讀取響應 Header,
  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 {
    // 寫入響應 Body
    response = response.newBuilder()
        .body(httpCodec.openResponseBody(response))
        .build();
  }

  // 如果 Header 中設定了 “Connection:close”,關閉連線
  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;
}
複製程式碼

CallServerInterceptor執行請求過程主要分為4步,利用在上一個節點中建立的HttpCodec實現與伺服器的互動:

  1. 寫入 Request Header;
  2. 寫入 Request Body;
  3. 讀取 Response Header;
  4. 讀取 Response Body。

至此, Request 請求過程中上游部分就結束了,一個原始的 Response 就被建立出來了,這個 Response 將按請求鏈上的反向路徑一步步返回到上一個 Interceptor 節點,供其繼續處理從伺服器返回的 Response,直到最後返回給使用者,完成一次網路請求。

相關文章