okhttp源碼分析(五)-CallServerInterceptor過(guò)濾器

1.okhttp源碼分析(一)——基本流程(超詳細(xì))
2.okhttp源碼分析(二)——RetryAndFollowUpInterceptor過(guò)濾器
3.okhttp源碼分析(三)——CacheInterceptor過(guò)濾器
4.okhttp源碼分析(四)——ConnectInterceptor過(guò)濾器
5.okhttp源碼分析(五)——CallServerInterceptor過(guò)濾器

前言

終于到最后一個(gè)過(guò)濾器了,其實(shí)閱讀源碼的過(guò)程中有過(guò)想要放棄的想法,因?yàn)橥ㄟ^(guò)查看OkHttp的源碼隨著逐步深入會(huì)逐漸發(fā)現(xiàn)OkHttp其實(shí)相較于Volley來(lái)說(shuō)更像一個(gè)對(duì)于Http協(xié)議封裝優(yōu)化的一個(gè)網(wǎng)絡(luò)框架,從請(qǐng)求的開(kāi)始一直延伸到到Socket的建立和通信,閱讀起來(lái)給人感覺(jué)更枯燥。

從過(guò)濾器的責(zé)任鏈一直到這個(gè)過(guò)濾器,這個(gè)過(guò)濾器的功能其實(shí)就是負(fù)責(zé)網(wǎng)絡(luò)通信最后一個(gè)步驟:數(shù)據(jù)交換,也就是負(fù)責(zé)向服務(wù)器發(fā)送請(qǐng)求數(shù)據(jù)、從服務(wù)器讀取響應(yīng)數(shù)據(jù)(實(shí)際網(wǎng)絡(luò)請(qǐng)求)。

分析

1.宏觀流程

直接上簡(jiǎn)化過(guò)的代碼吧

@Override public Response intercept(Chain chain) throws IOException {
    //開(kāi)始寫(xiě)入header
    httpCodec.writeRequestHeaders(request);
    //當(dāng)Header為Expect: 100-continue時(shí),只發(fā)送請(qǐng)求頭
    if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
      httpCodec.flushRequest();
      responseBuilder = httpCodec.readResponseHeaders(true);
    }
    //寫(xiě)入請(qǐng)求體
     request.body().writeTo(bufferedRequestBody);
    //寫(xiě)入完成
    ...
    //結(jié)束請(qǐng)求
    httpCodec.finishRequest();

    //得到響應(yīng)頭
    responseBuilder = httpCodec.readResponseHeaders(false);
    //構(gòu)建初步響應(yīng)
    Response response = responseBuilder
        .request(request)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();
    ...
      //構(gòu)建響應(yīng)體
      response = response.newBuilder()
          .body(httpCodec.openResponseBody(response))
          .build();
    }
    。。。
    //返回響應(yīng)
    return response;
  }

這里流程上其實(shí)就比較清楚了,這個(gè)過(guò)濾器的作用是數(shù)據(jù)交換,也就會(huì)說(shuō)發(fā)送請(qǐng)求和得到響應(yīng)。
這里大致流程分為:

1.先寫(xiě)入請(qǐng)求Header
2.如果請(qǐng)求頭的Expect: 100-continue時(shí),只發(fā)送請(qǐng)求頭,執(zhí)行3,不然執(zhí)行4
3.根據(jù)后臺(tái)返回的結(jié)果判斷是否繼續(xù)請(qǐng)求流程
4.寫(xiě)入請(qǐng)求體,完成請(qǐng)求
5.得到響應(yīng)頭,構(gòu)建初步響應(yīng)
6.構(gòu)建響應(yīng)體,完成最終響應(yīng)
7.返回響應(yīng)

2.過(guò)程細(xì)節(jié)

1.寫(xiě)入Header
    //開(kāi)始寫(xiě)入header
    realChain.eventListener().requestHeadersStart(realChain.call());
    httpCodec.writeRequestHeaders(request);
    //寫(xiě)入結(jié)束
    realChain.eventListener().requestHeadersEnd(realChain.call(), request);

其實(shí)通過(guò)事件回調(diào)就可以看出來(lái)這個(gè)步驟的作用是寫(xiě)入Header,這里調(diào)用了HttpCodec的writeRequestHeaders的方法,看過(guò)前面分析的應(yīng)該知道HttpCodec其實(shí)是一個(gè)接口,對(duì)應(yīng)的使用策略模式分別根據(jù)是Http還是Http/2請(qǐng)求,這里就看一下Http1Codec的實(shí)現(xiàn)吧。

@Override public void writeRequestHeaders(Request request) throws IOException {
    String requestLine = RequestLine.get(
        request, streamAllocation.connection().route().proxy().type());
    //寫(xiě)入header
    writeRequest(request.headers(), requestLine);
  }


//RequestLine.java
  /**
   * Returns the request status line, like "GET / HTTP/1.1". This is exposed to the application by
   * {@link HttpURLConnection#getHeaderFields}, so it needs to be set even if the transport is
   * HTTP/2.
   */
  //構(gòu)建"GET / HTTP/1.1"形式的請(qǐng)求狀態(tài)行
  public static String get(Request request, Proxy.Type proxyType) {
    StringBuilder result = new StringBuilder();
    result.append(request.method());
    result.append(' ');

    if (includeAuthorityInRequestLine(request, proxyType)) {
      result.append(request.url());
    } else {
      result.append(requestPath(request.url()));
    }

    result.append(" HTTP/1.1");
    return result.toString();
  }

其實(shí)代碼還是比較好理解的,這里可以看到首先調(diào)用RequestLine的get方法獲得一個(gè)請(qǐng)求行,這里方法源碼也放上來(lái)了,其實(shí)代碼很好理解,可以根據(jù)注釋或者自己看代碼,都會(huì)發(fā)現(xiàn)最后返回的是類(lèi)似于"GET / HTTP/1.1"的字符串。最后調(diào)用writeRequest方法寫(xiě)入Header信息。

/** Returns bytes of a request header for sending on an HTTP transport. */
  public void writeRequest(Headers headers, String requestLine) throws IOException {
    //寫(xiě)入header
    if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);
    //Okio中的sink,底層是socket使用的是okio優(yōu)化
    sink.writeUtf8(requestLine).writeUtf8("\r\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;
  }

可以看到這里其實(shí)沒(méi)有什么復(fù)雜的邏輯,就是遍歷Header,然后通過(guò)前一個(gè)過(guò)濾器建立的連接得到的Sink,來(lái)進(jìn)行寫(xiě)操作,這也是為什么OkHttp依賴于Okio。這里還有一個(gè)可以注意的地方,這里最后將state變量賦值為了STATE_OPEN_REQUEST_BODY,其實(shí)會(huì)發(fā)現(xiàn),隨著方法的 進(jìn)行,這個(gè)state變量會(huì)從上一個(gè)狀態(tài)變?yōu)橄乱粋€(gè)狀態(tài),這里其實(shí)用到了狀態(tài)模式的思想,雖然沒(méi)有那么細(xì)致將每個(gè)狀態(tài)分出來(lái),但是狀態(tài)模式的思想,狀態(tài)的改變還是用到的。

2.對(duì)于“Expect:100-continue”情況的處理
    Response.Builder responseBuilder = null;
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        httpCodec.flushRequest();
        realChain.eventListener().responseHeadersStart(realChain.call());
        responseBuilder = httpCodec.readResponseHeaders(true);
      }

這里先說(shuō)明一下是什么情況:

當(dāng)Header為Expect: 100-continue時(shí),只發(fā)送請(qǐng)求頭

  1. 發(fā)送一個(gè)請(qǐng)求, 包含一個(gè)Expect:100-continue, 詢問(wèn)Server使用愿意接受數(shù)據(jù)
  2. 接收到Server返回的100-continue應(yīng)答以后, 才把數(shù)據(jù)POST給Server

所以對(duì)應(yīng)注意幾個(gè)點(diǎn):
1.Response.Builder responseBuilder = null;首先構(gòu)建了一個(gè)null的responseBuilder。
2.執(zhí)行httpCodec.flushRequest()刷新請(qǐng)求。
3.執(zhí)行httpCodec.readResponseHeaders(true);讀取response的header信息,并返回一個(gè)responseBuilder賦值給responseBuilder。
這里看一下readResponseHeaders方法。

@Override public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {
    if (state != STATE_OPEN_REQUEST_BODY && state != STATE_READ_RESPONSE_HEADERS) {
      throw new IllegalStateException("state: " + state);
    }

    try {
      StatusLine statusLine = StatusLine.parse(readHeaderLine());
    //構(gòu)建一個(gè)reponseBuilder
      Response.Builder responseBuilder = new Response.Builder()
          .protocol(statusLine.protocol)
          .code(statusLine.code)
          .message(statusLine.message)
          .headers(readHeaders());
    //如果得到的返回碼是可以繼續(xù)訪問(wèn),返回null
      if (expectContinue && statusLine.code == HTTP_CONTINUE) {
        return null;
      }

      state = STATE_OPEN_RESPONSE_BODY;
    //不然返回構(gòu)建出來(lái)的reponseBuilder
      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;
    }
  }

這里其實(shí)就看我標(biāo)注的三處注釋,可以看到得到Response的Header后,第一步:構(gòu)建了一個(gè)ResponseBulder。第二步:通過(guò)返回的狀態(tài)碼判斷是否請(qǐng)求可以繼續(xù)進(jìn)行(“Expect:100-continue”情況的處理),如果可以返回null.第三步:否則,也就是不可以,返回構(gòu)建的ResponseBuilder。

所以綜上就可以看出來(lái),對(duì)于“Expect:100-continue”情況的處理總體為:
1.如果可以繼續(xù)請(qǐng)求,則responseBuilder=null
2.如果不行,則responseBuilder不為空,并且為返回的Header

3.寫(xiě)入請(qǐng)求體
if (responseBuilder == null) {
        //得到響應(yīng)后,根據(jù)Resposne判斷是否寫(xiě)入請(qǐng)求體
        // Write the request body if the "Expect: 100-continue" expectation was met.
        //寫(xiě)入請(qǐng)求體
        realChain.eventListener().requestBodyStart(realChain.call());
        long contentLength = request.body().contentLength();
        CountingSink requestBodyOut =
            new CountingSink(httpCodec.createRequestBody(request, contentLength));
        BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);

        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
        //寫(xiě)入完成
        realChain.eventListener()
            .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
      } 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();
      }

上面的步驟2看完后,這一步就很好理解了,如果可以繼續(xù)請(qǐng)求,則Responsebuilder=null,執(zhí)行if判斷里的內(nèi)容,可以看到就是對(duì)于請(qǐng)求體的寫(xiě)入操作,當(dāng)然任然是使用Okio進(jìn)行寫(xiě)入操作。

4.構(gòu)建響應(yīng)頭
//結(jié)束請(qǐng)求
    httpCodec.finishRequest();

    if (responseBuilder == null) {
      //得到響應(yīng)頭
      realChain.eventListener().responseHeadersStart(realChain.call());
      responseBuilder = httpCodec.readResponseHeaders(false);
    }

    Response response = responseBuilder
        .request(request)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    realChain.eventListener()
        .responseHeadersEnd(realChain.call(), response);

延續(xù)上面的,當(dāng)寫(xiě)入請(qǐng)求體后,當(dāng)然就是得到響應(yīng)了,由于這里的responseBuilder仍然為null,所以執(zhí)行的還是我們上面步驟2分析過(guò)的方法httpCodec.readResponseHeaders(false);,這時(shí)再通過(guò)返回得到的responseBuilder構(gòu)建攜帶有響應(yīng)頭的Reponse。

5.構(gòu)建響應(yīng)體,返回響應(yīng)
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 {
      //構(gòu)建響應(yīng)體
      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());
    }
    //返回響應(yīng)
    return response;

剩下的其實(shí)就是對(duì)弈返回碼的判斷,對(duì)應(yīng)正常的返回碼的話,構(gòu)建響應(yīng)體到Response中,最后將Response返回。

終于---------------------------------------------------------------------------------->結(jié)束

總結(jié):
歷經(jīng)5篇博客,初步把okHttp的過(guò)濾器全部發(fā)全部分析完了,期間有過(guò)想要放棄,因?yàn)檎嬲催^(guò)源碼的應(yīng)該會(huì)明白我的感受吧。OkHttp其實(shí)從層面上來(lái)說(shuō)給我的感覺(jué)相較于Volley更底層一些,級(jí)別類(lèi)同于HttpURLConnection,將網(wǎng)絡(luò)協(xié)議用代碼進(jìn)行實(shí)現(xiàn),并層層優(yōu)化,直到最后的Socket用了Okio進(jìn)行替換。
有時(shí)會(huì)有將Volley,retrofit,Okhttp進(jìn)行比較優(yōu)劣的,其實(shí)給我的感覺(jué),Volley和Retrofit屬于一個(gè)層面,OkHttp相較于前兩個(gè)屬于更深的層面,完全可以替換一下Volley底層執(zhí)行網(wǎng)絡(luò)協(xié)議的內(nèi)核,替換為OkHttp,retrofit更不用說(shuō),底層就是okHttp,但是retrofit和volley相比哪,拋開(kāi)okHttp這個(gè)因素,其實(shí)各有優(yōu)劣,根據(jù)自己的需要吧,如果想要rxjava,耦合度極低,適合拓展,接口RESTful,當(dāng)然retrofit歡迎你。
通過(guò)看OkHttp的源碼除了原理外,其實(shí)可以發(fā)現(xiàn)許多其他的東西:
1.首先發(fā)現(xiàn)了計(jì)算機(jī)網(wǎng)絡(luò)的重要性,Http協(xié)議,準(zhǔn)備后面入一本書(shū),再回顧學(xué)習(xí)一下。
2.線程安全各種方式,各種數(shù)據(jù)結(jié)構(gòu)。
3.設(shè)計(jì)模式,粗略回顧一下:建造者模式,責(zé)任鏈模式,策略模式...其實(shí)不用強(qiáng)行使用設(shè)計(jì)模式,其實(shí)主要是設(shè)計(jì)思想
4.面向接口編程,這個(gè)不用說(shuō),越看越重要。
5.方法的命名,其實(shí)我感覺(jué)挺有趣,也挺講究的。
6.注釋,其實(shí)和5差不多,基本上就是編程風(fēng)格了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容