OKHttp3-- 请求服务器拦截器CallServerInterceptor源码分析 【十一】

系列

OKHttp3–详细使用及源码分析系列之初步介绍【一】
OKHttp3–流程分析 核心类介绍 同步异步请求源码分析【二】
OKHttp3–Dispatcher分发器源码解析【三】
OKHttp3–调用对象RealCall源码解析【四】
OKHttp3–拦截器链RealInterceptorChain源码解析【五】
OKHttp3–重试及重定向拦截器RetryAndFollowUpInterceptor源码解析【六】
OKHttp3–桥接拦截器BridgeInterceptor源码解析及相关http请求头字段解析【七】
OKHttp3–缓存拦截器CacheInterceptor源码解析【八】
OKHttp3-- HTTP缓存机制解析 缓存处理类Cache和缓存策略类CacheStrategy源码分析 【九】
通过ConnectInterceptor源码掌握OKHttp3网络连接原理 呕心沥血第十弹【十】

前言

这篇文章将对OKHttp最后一个拦截器进行解析,总算快要结束了;上一篇文章讲到连接拦截器,即客户端已经与服务端进行了连接,那接下来的操作自然就是发送接收数据了,看看官网的注释

这是拦截器链上的最后一个拦截器,向服务器发起网络访问

那接下来就从源码看看它是如何实现发送请求数据,接收响应数据的

CallServerInterceptor

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

    long sentRequestMillis = System.currentTimeMillis();
    httpCodec.writeRequestHeaders(request);

    Response.Builder responseBuilder = null;
    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"))) {
        httpCodec.flushRequest();
        responseBuilder = httpCodec.readResponseHeaders(true);
      }

      if (responseBuilder == null) {
        // Write the request body if the "Expect: 100-continue" expectation was met.
        Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
        BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
      } 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();
      }
    }

    httpCodec.finishRequest();

    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 {
      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());
    }

    return response;
  }

这里代码我们分开看

第一部分:获取网络组件

    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    HttpCodec httpCodec = realChain.httpStream();
    StreamAllocation streamAllocation = realChain.streamAllocation();
    RealConnection connection = (RealConnection) realChain.connection();
    Request request = realChain.request();

这里通过拦截器链拿到了四个对象

  • HttpCodec : 封装I/O操作的对象,根据HTTP/1.x,HTTP/2版本,有两个对应的实现类Http1Codec、Http2Codec,可以简单理解为编码我们的请求,解码返回的响应
  • StreamAllocation:封装了一次网络操作需要的组件,为一个请求分配一个stream
  • RealConnection: 封装了一次Socket与服务器连接的对象,在OKHttp中将一次连接抽象为Connection,而它是其实现类
  • Request:封装用户的请求信息的对象

第二部分:发送请求

    long sentRequestMillis = System.currentTimeMillis();
    httpCodec.writeRequestHeaders(request);

    Response.Builder responseBuilder = null;
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        httpCodec.flushRequest();
        responseBuilder = httpCodec.readResponseHeaders(true);
      }

      if (responseBuilder == null) {
        Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
        BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
      } else if (!connection.isMultiplexed()) {
        streamAllocation.noNewStreams();
      }
    }

    httpCodec.finishRequest();

这部分主要是用来发送请求

  • 获取发送请求的时间戳,并将请求头信息写入
  • 检测是否为有body的请求方法,接下来有两个判断,分开讲述

第一个if判断的意思是:如果请求头中包含【Expect: 100- Continue】,那么就发送100-continue的请求,然后获取服务器的响应,看服务器是否同意接受请求体,如果服务器的响应码是HTTP_CONTINUE,即100,那这个responseBuilder 即为null

第二个判断:上面一个判断可以知道如果服务器同意接受请求体数据,那么responseBuilder 为null,接下来就构建输出流BufferedSink对象,将请求体数据写到其缓冲区

第三个判断:也就是responseBuilder 不为null,意思是服务器不同意接受该请求体,且同时这个连接不支持多路复用,也就是说这是一个Http/1.x的请求,那么我们就需要标记该连接不能再被复用,同时关闭相关的Socket

接下来就是将缓存区的数据写到Socket中,发送出去,到这里发送请求已经完成

第三部分:构建响应

    if (responseBuilder == null) {
      responseBuilder = httpCodec.readResponseHeaders(false);
    }

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

首先获取响应头部信息,然后构建响应Response 对象

第四部分:处理响应

    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 {
      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());
    }

    return response;
  • 如果使用websocket且响应码是101,那么构建一个空响应体的response 对象;否则就根据响应体构建真正的响应对象
  • 如果请求头或者响应头部包含【Connection: close】,意味着该连接不需要保活,那就关闭该连接以及释放相应的资源
  • 如果响应码是204或者205,且响应体长度大于0,也就意味着该次请求成功,但是服务器只返回了响应行和响应头信息,没有响应体,可是这里拿到的响应体长度居然大于0,那就说明出问题了,所以需要抛出一个协议异常

总结

可以看到最后一个拦截器的代码比较简单,当然了你要是没有看前面的系列文章,可能会觉得有点懵;拦截器链上的五个拦截器分析完了,后续的文章将会介绍下OKHttp中有关WebSocket的知识点

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值