Okhttp-CallServerInterceptor(请求服务器拦截器)源码分析

200 阅读3分钟
关键代码:
@Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    //①获取拦截器链中的HttpCodec、StreamAllocation、RealConnection对象
    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());
    //②调用httpCodec.writeRequestHeaders(request)将请求头写入缓存
    httpCodec.writeRequestHeaders(request);
    realChain.eventListener().requestHeadersEnd(realChain.call(), request);

    Response.Builder responseBuilder = null;
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      //③判断是否有请求体,如果有,请求头通过携带特殊字段 Expect:100-continue来询问服务器是否愿意接受请求体。(一般用于上传大容量请求体或者需要验证)
      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        //调用真正发送给服务器
        httpCodec.flushRequest();
        realChain.eventListener().responseHeadersStart(realChain.call());
        //服务端返回100,表示愿意接受请求体responseBuilder为null(见下面Http2Codec的readResponseHeaders方法)
        responseBuilder = httpCodec.readResponseHeaders(true);
      }

      if (responseBuilder == null) {
        //服务器愿意会响应100(responseBuilder 即为nul)。这时候才能够继续发送剩余请求数据。
        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);
      } else if (!connection.isMultiplexed()) {
        //服务器不愿意接受请求体,调用noNewStreams关闭相关socket
        streamAllocation.noNewStreams();
      }
    }
        //④结束请求
    httpCodec.finishRequest();
  
  /**代码走到这里的responseBuilder情况为:
    * 1.post请求,请求头包含Expect,服务端允许接受请求体,并且已经发出了请求体,responseBuilder为null
    * 2.post请求,请求头包含Expect,服务端不允许接受请求体,responseBuilder不为null
    * 3.post请求,没有请求体,responseBuilder为null
    * 4.get请求,responseBuilder为null
    */
  
    if (responseBuilder == null) {
      realChain.eventListener().responseHeadersStart(realChain.call());
    //根据服务器返回的数据构建 responseBuilder对象  (传入的expectContinue为false,不会return null)
      responseBuilder = httpCodec.readResponseHeaders(false);
    }

    //⑤构建Response对象
    Response response = responseBuilder
        .request(request)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();
        //第一次查询服务器是否支持接受请求体的,而不是真正的请求对应的结果响应。所以接着:
    int code = response.code();
    if (code == 100) {
      //如果响应了100,这代表了请求Expect: 100-continue成功响应,需要马上再次读取一份响应头,这才是真正的请求对应的响应头。
      responseBuilder = httpCodec.readResponseHeaders(false);
            //⑤构建Response对象
      response = responseBuilder
              .request(request)
              .handshake(streamAllocation.connection().handshake())
              .sentRequestAtMillis(sentRequestMillis)
              .receivedResponseAtMillis(System.currentTimeMillis())
              .build();

      code = response.code();
    }

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

    if (forWebSocket && code == 101) {
      //WebSocket请求
      response = response.newBuilder()
          .body(Util.EMPTY_RESPONSE)
          .build();
    } else {
      //读取响应体数据
      response = response.newBuilder()
          .body(httpCodec.openResponseBody(response))
          .build();
    }
        //客户端或者服务端不希望长连接,那么就关闭socket
    if ("close".equalsIgnoreCase(response.request().header("Connection"))
        || "close".equalsIgnoreCase(response.header("Connection"))) {
      streamAllocation.noNewStreams();
    }
        //如果服务器返回204/205(表示没有响应体),但是解析Content-Lenght>0(表示响应体字节长度),出现冲突,抛出协议异常
    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
      throw new ProtocolException(
          "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
    }
        //⑥返回response
    return response;
  }

Http2Codec.java(写数据和读取数据)

 private Http2Stream stream;

@Override public void writeRequestHeaders(Request request) throws IOException {
  if (stream != null) return;
  boolean hasRequestBody = request.body() != null;
  List<Header> requestHeaders = http2HeadersList(request);
  stream = connection.newStream(requestHeaders, hasRequestBody);
  stream.readTimeout().timeout(chain.readTimeoutMillis(), TimeUnit.MILLISECONDS);
  stream.writeTimeout().timeout(chain.writeTimeoutMillis(), TimeUnit.MILLISECONDS);
}

@Override public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {
    List<Header> headers = stream.takeResponseHeaders();
    Response.Builder responseBuilder = readHttp2HeadersList(headers);
    if (expectContinue && Internal.instance.code(responseBuilder) == HTTP_CONTINUE) {
      //注意传过来的expectContinue
      //服务端返回100,表示愿意接受请求体 responseBuilder为null
      return null;
    }
    return responseBuilder;
  }
大致流程
  • 获取拦截器链中的HttpCodec、StreamAllocation、RealConnection对象
  • 调用httpCodec.writeRequestHeaders(request)将请求头写入缓存
  • 判断是否有请求体,如果有,请求头通过携带特殊字段 Expect:100-continue来询问服务器是否愿意接受请求体。(一般用于上传大容量请求体或者需要验证)
  • 通过httpCodec.finishRequest()结束请求
  • 通过responseBuilder构建Response
  • 返回Response

总结:CallServerInterceptor完成HTTP协议报文的封装和解析。