Okhttp之CallServerInterceptor拦截器原理及解析

在开始之前拓展一个http的知识:

1、关键字100-continue介绍

http 100-continue用于客户端在发送POST数据给服务器前,征询服务器情况,看服务器是否处理POST的数据,如果不处理,客户端则不上传POST数据,如果处理,则POST上传数据。在现实应用中,通过在POST大数据时,才会使用100-continue协议。

2、客户端策略

  • 如果客户端有POST数据要上传,可以考虑使用100-continue协议。加入头{“Expect”:”100-continue”}
  • 如果没有POST数据,不能使用100-continue协议,因为这会让服务端造成误解。
  • 并不是所有的Server都会正确实现100-continue协议,如果Client发送Expect:100-continue消息后,在timeout时间内无响应,Client需要立马上传POST数据。
  • 有些Server会错误实现100-continue协议,在不需要此协议时返回100,此时客户端应该忽略。

3、服务端策略

  • 正确情况下,收到请求后,返回100或错误码。
  • 如果在发送100-continue前收到了POST数据(客户端提前发送POST数据),则不发送100响应码(略去)。

回到CallServerInterceptor核心Intercept方法:

  @Override public Response intercept(Chain chain) throws IOException {
    ...

    //封装请求头,即conent-length,method,编码等等
    realChain.eventListener().requestHeadersStart(realChain.call());
    httpCodec.writeRequestHeaders(request);
    realChain.eventListener().requestHeadersEnd(realChain.call(), request);

    //当前request需要请求体,即post请求等方式,如果有,则进行封装
    Response.Builder responseBuilder = null;
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      // 判断服务器是否允许发送body
      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        httpCodec.flushRequest();
        realChain.eventListener().responseHeadersStart(realChain.call());
        responseBuilder = httpCodec.readResponseHeaders(true);
      }

      if (responseBuilder == null) {
        // 向服务器发送requestbody,
        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();
        realChain.eventListener()
            .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
        ....
    }

    //结束请求
    httpCodec.finishRequest();

    if (responseBuilder == null) {
      realChain.eventListener().responseHeadersStart(realChain.call());
      responseBuilder = httpCodec.readResponseHeaders(false);
    }

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

      //100的状态码的处理继续发送请求,继续接受数据
    int code = response.code();
    if (code == 100) {
      responseBuilder = httpCodec.readResponseHeaders(false);

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

    ...
    //返回为空的处理
    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
      throw new ProtocolException(
          "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
    }
    return response;
  }

简单概述一下流程:

1、获取到 ConnectInterceptor 拦截器中的httpCodec和realChain,至于ConnectInterceptor中做了什么,可以看之前写的《Okhttp之ConnectInterceptor拦截器原理及解析》

2、写入http请求头信息,判断是否允许上传requestBody

  1. 判断header中Expect域是否为100-continue,这个请求头字段的作用是在发送RequestBody前向服务器确认是否接受RequestBody,如果没有,则正常请求。如果有,则相当于一次简单的握手操作,则等待服务器返回的ResponseHeaders之后再继续,如果服务器接收RequestBody,会返回null。

  2. 根据3的判断,如果RequestBuilder为null,说明Expect不为100-continue或者服务器同意接收RequestBody。这时就开始向流中写入RequestBody。

  3. 读取响应头信息 构建Response,写入原请求、握手情况、请求时间、得到结果的时间等

  4. 针对204/205状态码处理

  5. 返回Response

    原文作者:OneXzgj
    原文地址: https://www.jianshu.com/p/be204b73c1ee
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞