OkHttp(三)拦截器之ConnectionInterceptor 与CallServerInterceptor

ConnectionInterceptor 连接拦截器

用来(建立可用的链接)获取connection对象 以及Httpcodec对象

/*打开与目标服务器的连接,然后继续执行下一个拦截器。*/
public final class ConnectInterceptor implements Interceptor {
  
  @Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    StreamAllocation streamAllocation = realChain.streamAllocation();

    // We need the network to satisfy this request. Possibly for validating a conditional GET.
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
     // 1 创建httpcodec 对象 对应http1 和http2
     // httpcodec 对HTTP请求进行编码并解码HTTP响应。 创建httpcodec是重点
     // 因为再下一个拦截器里CallServiceInterceptor里进行网络访问
    HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
      // 2 创建 connection 连接
    RealConnection connection = streamAllocation.connection();
	//调用下一个拦截器  一般是CallServerInterceptor 
    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }
}

  • 这里调用了 重定向拦截器中传递过来的streamAllocation的newStream(分配流)创建了一个 HttpCodec 的对象。

    • HttpCodec 是一个抽象类,其实现类分别是 Http1Codec 和 Http2Codec 。相对应的就是 HTTP/1.1 和 HTTP/2.0 。
  • 我们来看下 streamAllocation.newStream 做了什么:

public HttpCodec newStream(
      OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
    int connectTimeout = chain.connectTimeoutMillis();
    int readTimeout = chain.readTimeoutMillis();
    int writeTimeout = chain.writeTimeoutMillis();
    int pingIntervalMillis = client.pingIntervalMillis();
    boolean connectionRetryEnabled = client.retryOnConnectionFailure();

    try {
    //在连接池里找到一个可用连接 然后创建一个httpcodec
      RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
          writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
      HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);

      synchronized (connectionPool) {
        codec = resultCodec;
        return resultCodec;
      }
    } catch (IOException e) {
      throw new RouteException(e);
    }
  }
  • 可看到是通过resultConnection.newCodec()创建httpcodec对象
    看一下这个方法里执行了什么
public HttpCodec newCodec(OkHttpClient client, Interceptor.Chain chain,
      StreamAllocation streamAllocation) throws SocketException {
    if (http2Connection != null) {
      return new Http2Codec(client, chain, streamAllocation, http2Connection);
    } else {
      socket.setSoTimeout(chain.readTimeoutMillis());
      //类似inputStream
      source.timeout().timeout(chain.readTimeoutMillis(), MILLISECONDS);
      //类似WriteStream
      sink.timeout().timeout(chain.writeTimeoutMillis(), MILLISECONDS);
      return new Http1Codec(client, streamAllocation, source, sink);
    }
  }
  • ​ 在这里通过source 和sink 创建 http1codec 对象
    • source 类似inputStream
    • sink 类似WriteStream

总结

  • 连接拦截器作用
    • 通过streamAllocation 获取httpcodec 和 connection连接
    • 调用proceed方法执行下一个拦截器,获取response 并返回
    • 此时就到了 CallServerInterceptor(真正联网的步骤)

CallServerInterceptor 呼叫服务拦截器

  • CallServerInterceptor(真正联网的步骤)

  • 将http请求写进网络的IO流当中,并且从网络IO流当中读取服务端返回给客户端的数据。
    看看里面都做了什么

/** This is the last interceptor in the chain. It makes a network call to the server. */
public final class CallServerInterceptor implements Interceptor {
 ....简化代码
  @Override public Response intercept(Chain chain) throws IOException {
    final RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Call call = realChain.call();
    final HttpCodec httpCodec = realChain.httpStream();
    StreamAllocation streamAllocation = realChain.streamAllocation();
    RealConnection connection = (RealConnection) realChain.connection();
    Request request = realChain.request();

    long sentRequestMillis = System.currentTimeMillis();

    realChain.eventListener().requestHeadersStart(call);
     // 整理请求头并写入 还记得上一步创建的httpcodec吗  这里的写入最终就是通过sink
    httpCodec.writeRequestHeaders(request);
    realChain.eventListener().requestHeadersEnd(call, request);

    Response.Builder responseBuilder = null;
    //检查是否为有 body 的请求方法
    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.
       // 如果有 Expect: 100-continue 在请求头中,那么要等服务器的响应
      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        httpCodec.flushRequest();
        realChain.eventListener().responseHeadersStart(call);
        responseBuilder = httpCodec.readResponseHeaders(true);
      }

      if (responseBuilder == null) {
        if (request.body() instanceof DuplexRequestBody) {
          // Prepare a duplex body so that the application can send a request body later.
          httpCodec.flushRequest();
          CountingSink requestBodyOut = new CountingSink(httpCodec.createRequestBody(request, -1L));
            // 写入请求体
          BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
          request.body().writeTo(bufferedRequestBody);
        } else {
          // Write the request body if the "Expect: 100-continue" expectation was met.
          realChain.eventListener().requestBodyStart(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(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();
      }
    }

    if (!(request.body() instanceof DuplexRequestBody)) {
      httpCodec.finishRequest();
    }
    // 得到响应头
    if (responseBuilder == null) {
      realChain.eventListener().responseHeadersStart(call);
      responseBuilder = httpCodec.readResponseHeaders(false);
    }
   // 构造 response
    responseBuilder
        .request(request)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis());
    Internal.instance.initCodec(responseBuilder, httpCodec);
    Response response = responseBuilder.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
      responseBuilder = httpCodec.readResponseHeaders(false);

      responseBuilder
          .request(request)
          .handshake(streamAllocation.connection().handshake())
          .sentRequestAtMillis(sentRequestMillis)
          .receivedResponseAtMillis(System.currentTimeMillis());
      Internal.instance.initCodec(responseBuilder, httpCodec);
      response = responseBuilder.build();

      code = response.code();
    }

    realChain.eventListener().responseHeadersEnd(call, response);
     // 如果为 web socket 且状态码是 101 ,那么 body 为空
    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 {
    //读取body
      response = response.newBuilder()
          .body(httpCodec.openResponseBody(response))
          .build();
    }
    // 如果请求头中有 close 那么断开连接
    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;
  }
}

作用总结

  • 该拦截器主要是将请求 写入 网络io流
  • 读取服务端返回给客户端的信息,构造response并返回response给上一个拦截器

Okhttp 底层实现

  • 在 CallServerInterceptor 中关于请求和响应部分都是通过 HttpCodec 来实现的。
  • 而在 HttpCodec 内部又是通过 sink 和 source 来实现的。
  • 所以说到底还是 IO 流在起作用。是okio 对Socket流操作的封装 Okhttp 其实是基于Socket进行通讯的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值