okHttp3源码解析(三)-CallServerInterceptor

本篇主要看下 CallServerInterceptor
, 关于他在整个请求中起到的作用, okHttp已经告诉我们, 可以看出它作为责任链中的最后一个环节, 承担了对服务端进行请求的工作.

This is the last interceptor in the chain. It makes a network call to the server.

正文

okHttp
对于对服务钱的请求与相应, 底层都是通过 okio
socket
进行操作.

老样子, 我们直接上代码

@Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    // 在 ConnectInterceptor创建
    HttpCodec httpCodec = realChain.httpStream();
    // 在 RetryAndFollowUpInterceptor创建
    StreamAllocation streamAllocation = realChain.streamAllocation();
    // 在 ConnectInterceptor获取
    RealConnection connection = (RealConnection) realChain.connection();
    Request request = realChain.request();
    // 发送请求时间戳为当前时间戳
    long sentRequestMillis = System.currentTimeMillis();

    realChain.eventListener().requestHeadersStart(realChain.call());
    // 发送请求头, 通过Okio
    httpCodec.writeRequestHeaders(request);
    realChain.eventListener().requestHeadersEnd(realChain.call(), request);

    Response.Builder responseBuilder = null;
    // GET or HEAD 不需要
    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.
      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        // 请求刷新, okio处理
        httpCodec.flushRequest();
        realChain.eventListener().responseHeadersStart(realChain.call());
        // 构建Response.Builder, 当response状态为100, 则返回null
        responseBuilder = httpCodec.readResponseHeaders(true);
      }

      if (responseBuilder == null) {
        // head成功响应的情况下
        // Write the request body if the "Expect: 100-continue" expectation was met.
        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()) {
        // HTTP/1请求协议, 而且初次握手失败
        // 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();
      }
    }

    // flush
    httpCodec.finishRequest();
    // 如果是GET请求, 或者需要'100- continue'握手成功的情况下
    if (responseBuilder == null) {
      realChain.eventListener().responseHeadersStart(realChain.call());
      // 构建responseBuilder
      responseBuilder = httpCodec.readResponseHeaders(false);
    }
    // 获取响应
    Response response = responseBuilder
        // 原请求
        .request(request)
        // 握手情况
        .handshake(streamAllocation.connection().handshake())
        // 请求时间
        .sentRequestAtMillis(sentRequestMillis)
        // 响应时间
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    int code = response.code();
    if (code == 100) {
      // server sent a 100-continue even though we did not request one.
      // try again to read the actual response
      // 即使我们没有请求, 服务端也会发送一个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) {
      // 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(httpCodec.openResponseBody(response))
          .build();
    }

    // 如果请求关闭连接, 则关闭
    if ("close".equalsIgnoreCase(response.request().header("Connection"))
        || "close".equalsIgnoreCase(response.header("Connection"))) {
      streamAllocation.noNewStreams();
    }
    // 抛出协议异常
    // 204: No Content
    // 205: Reset Content
    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
      throw new ProtocolException(
          "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
    }

    return response;
  }

请求

我们来一步步看下代码

RealInterceptorChain realChain = (RealInterceptorChain) chain;
    // 在 ConnectInterceptor创建
    HttpCodec httpCodec = realChain.httpStream();
    // 在 RetryAndFollowUpInterceptor创建
    StreamAllocation streamAllocation = realChain.streamAllocation();
    // 在 ConnectInterceptor获取
    RealConnection connection = (RealConnection) realChain.connection();
    Request request = realChain.request();

CallServerInterceptor
在, 核心工具类就是 HttpCodec
, 他的初始化我们在上篇 ConnectInterceptor解析
中可以看到.这里的步骤其实就是工具的准备.

httpCodec.writeRequestHeaders(request);

我们以HTTP/1.1协议来看, 那么具体要看 Http1Codec
中的实现

@Override public void writeRequestHeaders(Request request) throws IOException {
    // requestLine 就是我们请求报文内容的首行, 譬如 "GET / HTTP/1.1"
    String requestLine = RequestLine.get(
        request, streamAllocation.connection().route().proxy().type());
    writeRequest(request.headers(), requestLine);
  }

  final BufferedSink sink;
  public void writeRequest(Headers headers, String requestLine) throws IOException {
    if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);
    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;
  }

可以看到在 writeRequest
中的方法, 就是针对 BufferedSink
对象的写操作, 在上篇 ConnectInterceptor
中进行三次握手连接的时候, 会进行初始化的工作, 我们会发现他是针对 Socket
的包装, 可以看做是 Socket
的输出流, 所以这里相当于是 Socket
的写入动作, 可以看出来, 这里会请求发送 header
.

if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        // 请求刷新, okio处理
        httpCodec.flushRequest();
        realChain.eventListener().responseHeadersStart(realChain.call());
        // 构建Response.Builder, 当response状态为100, 则返回null
        responseBuilder = httpCodec.readResponseHeaders(true);
      }

      if (responseBuilder == null) {
        // head成功响应的情况下
        // Write the request body if the "Expect: 100-continue" expectation was met.
        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()) {
        // HTTP/1请求协议, 而且初次握手失败
        // 禁止同主机请求新流的分配
        streamAllocation.noNewStreams();
      }
    }

当请求首部字段包含 Expect:100-continue
, 一般在请求上传大容量body或者是需要验证的时候, 这样避免大文件传送失败带来的带宽浪费。所以需要判断请求体不为空的情况下, 并且请求首部包含该字段的情况下, 刷新请求, 构建 responseBuilder
对象, 如果这个时候服务端响应成功, 则 responseBuilder
对象为null, 并进行body的请求;而如果第一次请求头响应失败的情况下, 那么如果是HTTP/1的请求协议下, 就会禁止同host的请求新流的分配.

响应

以上就是请求的过程, 然后看响应

// 如果是GET请求, 或者需要'100- continue'握手成功的情况下
    if (responseBuilder == null) {
      // 构建responseBuilder
      responseBuilder = httpCodec.readResponseHeaders(false);
    }
    // 获取响应
    Response response = responseBuilder
        // 原请求
        .request(request)
        // 握手情况
        .handshake(streamAllocation.connection().handshake())
        // 请求时间
        .sentRequestAtMillis(sentRequestMillis)
        // 响应时间
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

在请求成功的情况下, 会重新构建 responseBuilder
对象, 通过它来构建响应报文.

int code = response.code();
    if (code == 100) {
      // 即使我们没有请求, 服务端也会发送一个100-continue
      // 重新读取真正的响应
      responseBuilder = httpCodec.readResponseHeaders(false);
      // 构建response
      response = responseBuilder
              .request(request)
              .handshake(streamAllocation.connection().handshake())
              .sentRequestAtMillis(sentRequestMillis)
              .receivedResponseAtMillis(System.currentTimeMillis())
              .build();

      code = response.code();
    }

如果客户端响应 100
状态码, 这个时候, 我们就需要重新读取获取真正的内容响应.

剩下的代码就是一些异常的处理, 这里就不做分析了.

原文 

https://yutiantina.github.io/2018/08/29/OkHttp源码解析(三)-CallServerInterceptor/

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » okHttp3源码解析(三)-CallServerInterceptor

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址