OkHttp源码之getResponseWithInterceptorChain()

目录

一、拦截器

1、用户自定义的拦截器

2、RetryAndFollowUpInterceptor

3、BridgeInterceptor

4、CacheInterceptor

5、 ConnectInterceptor

6、用户自定义的拦截器

7、CallServerInterceptor

 8、RealInterceptorChain

二、实现过程

扫描二维码关注公众号,回复: 5707014 查看本文章

1、举例分析

2、综述

三、简单分析几个比较重要的拦截器

1、ConnectInterceptor

2、CallServerInterceptor

四、HttpCodec

1、获得HttpCodec对象

2、写request的header

3、读response的header

4、执行流的刷新

5、读取response的内容


主要是去请求服务器返回对应的Response,对应代码如下: 

  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }

从代码中可以看到,其中就是加了一些不同功能的拦截器。

一、拦截器

1、用户自定义的拦截器

用在与服务器建立链接之前进行拦截

nterceptors.addAll(client.interceptors());

2、RetryAndFollowUpInterceptor

重试和失败重定向拦截器。

    interceptors.add(retryAndFollowUpInterceptor);

3、BridgeInterceptor

桥接和适配拦截器。主要补充用户创建请求当中的一些请求头Content-Type等

   interceptors.add(new BridgeInterceptor(client.cookieJar()));

4、CacheInterceptor

主要处理缓存。 

    interceptors.add(new CacheInterceptor(client.internalCache()));

5、 ConnectInterceptor

与服务器建立链接。创建可以用的RealConnection(对java.io和java.nio进行了封装)

    interceptors.add(new ConnectInterceptor(client))

6、用户自定义的拦截器

用在与服务器建立链接之后进行拦截。只有非socket进行设置

   if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }

7、CallServerInterceptor

向服务器发送请求和接收数据。将请求写入IO流,再从IO流中读取响应数据

    interceptors.add(new CallServerInterceptor(forWebSocket));

 8、RealInterceptorChain

主要就是将上述几个拦截器顺序执行。

从执行RetryAndFollowUpInterceptor->执行BridgeInterceptor->执行CacheInterceptor->执行ConnectInterceptor->执行CallServerInterceptor->依次将响应一一返回->返回到ConnectInterceptor->返回到CacheInterceptor->返回到BridgeInterceptor->返回到RetryAndFollowUpInterceptor。

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);

二、实现过程

1、举例分析

实例化RealInterceptorChain,调用RealInterceptorChain的proceed()方法。

  public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
      HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call,
      EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout) {
    this.interceptors = interceptors;
    //。。。省略代码
    this.index = index;
    //。。。省略代码
}

将interceptors和0传入到RealInterceptorChain。进入到proceed()方法中,查看关键代码。先去执行集合中的第一个拦截器。。

  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
// 。。。。 省略代码
    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
// 。。。。 省略代码
    return response;
}

我们从第一行代码中可以看到index+1进行访问下一个拦截器;

第二行代码中找到该第一个拦截器;

第三句代码就是调用第一个拦截器的intercept(),将创建的第二个拦截器传入当前第一个拦截器,然后该的intercept()的返回的第二个拦截器的response返回。

进入到第一个拦截器RetryAndFollowUpInterceptor中进行查看代码,可以看到

 public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
// 。。。。省略代码
        response = realChain.proceed(request, streamAllocation, null, null);

// 。。。。省略代码
}

最后return就是返回的第二个拦截器的response。

2、综述

每个拦截器都是这种流程,当前一个拦截器调用下一个,然后返回下一个的response。

三、简单分析几个比较重要的拦截器

1、ConnectInterceptor

将request和建立的链接connect传到了CallServerInterceptor拦截器中


  @Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    StreamAllocation streamAllocation = realChain.streamAllocation();
    // 。。。省略代码
    RealConnection connection = streamAllocation.connection();
    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }

2、CallServerInterceptor

向服务器发送请求,将返回的response进行返回到上一个interceptor

1)首先就是从传过来的RealInterceptorChain得到httpCodec、request、connect

    RealInterceptorChain realChain = (RealInterceptorChain) chain;
//1、返回一个HttpCodec:Http的链接方式。根据不同链接方式返回Http2Codec或者Http1Codec。具体实现过程是在ConnectInterceptor中实例化。
//最终代码实现是在RealConnect中
    HttpCodec httpCodec = realChain.httpStream();
    // 。。。。省略代码
    RealConnection connection = (RealConnection) realChain.connection();
//获取request请求
    Request request = realChain.request();

2)向服务器写入header

//2、开始向服务器写入头信息,查看Http2Codec或者Http1Codec
    realChain.eventListener().requestHeadersStart(realChain.call());
    httpCodec.writeRequestHeaders(request);
    realChain.eventListener().requestHeadersEnd(realChain.call(), request);

    Response.Builder responseBuilder = null;

3)首先会先去判断是否为100-continue,如果接收的话,返回responseBuild 为null;

//3、判断是否有请求实体?
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
//4、100-continue的情况:在发送requestBody前向服务器确认是否接收request。只有拿到服务器的结果之后在继续
      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        httpCodec.flushRequest();
        realChain.eventListener().responseHeadersStart(realChain.call());
        responseBuilder = httpCodec.readResponseHeaders(true);
      }

4)如果100-continue继续接收或者不是100-continue,则将数据写入到requestBody中

//5、开始写"Expect: 100-continue"的response的内容。如果100-continue需要握手但是握手失败的话,
//返回的responseBuilder不为空

      if (responseBuilder == null) {
        // 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()) {
        //。。。省略代码
      }
    }

5)执行流的刷新

//6、结束写request
    httpCodec.finishRequest();

6)开始根据request构建response,然后根据不同的code来决定是否进行将服务器返回的数据写入到response中


    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
      responseBuilder = httpCodec.readResponseHeaders(false);

      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) {
  //。。。省略代码
    } else {
//读取返回的IO数据流
      response = response.newBuilder()
          .body(httpCodec.openResponseBody(response))
          .build();
    }

   //。。。。省略代码

    return response;

备注:

(1)http 100-continue用户客户端在发送post数据给服务器前,先确认服务器是否处理post数据,如果不处理,则不上传。常用于post大数据。

(2)客户端如果有post数据需要上传,则可以考虑使用100-continue协议。只要在头加入{“Expect”:”100-continue”} ,如果没有post数据,不能使用该协议

(3)并不是所有的server都会实现该协议,若超出时间无响应,则客户端需要立刻上传

四、HttpCodec

从CallServerInterceptor中可以看到,HttpCodec提供I/O操作,完成对服务器发送和接收数据 。

1、获得HttpCodec对象

    HttpCodec httpCodec = realChain.httpStream();

进入到RealInterceptorChain,看到该对象是从ConnectInterceptor中传入的。在进入到ConnectInterceptor中可以看到

  HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);

最终跟到RealConnection中,可以看到就是根据不同的情况实例化了Http1Codec或者Http2Codec,对应分别Http1.1协议以及Http2.0协议


  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());
      source.timeout().timeout(chain.readTimeoutMillis(), MILLISECONDS);
      sink.timeout().timeout(chain.writeTimeoutMillis(), MILLISECONDS);
      return new Http1Codec(client, streamAllocation, source, sink);
    }
  }

2、写request的header

httpCodec.writeRequestHeaders(request);

进入到代码中,拿Http1Codec举例

  @Override public void writeRequestHeaders(Request request) throws IOException {
    String requestLine = RequestLine.get(
        request, streamAllocation.connection().route().proxy().type());
    writeRequest(request.headers(), requestLine);
  }
  /** Returns bytes of a request header for sending on an HTTP transport. */
  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;
  }

写入请求头 

3、读response的header

 responseBuilder = httpCodec.readResponseHeaders(true);

进入到Http1Codec

  @Override public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {
    if (state != STATE_OPEN_REQUEST_BODY && state != STATE_READ_RESPONSE_HEADERS) {
      throw new IllegalStateException("state: " + state);
    }

    try {
      StatusLine statusLine = StatusLine.parse(readHeaderLine());

      Response.Builder responseBuilder = new Response.Builder()
          .protocol(statusLine.protocol)
          .code(statusLine.code)
          .message(statusLine.message)
          .headers(readHeaders());

     //。。。省略代码 根据不同的情况来返回responseBuilder
  }
 

4、执行流的刷新

 httpCodec.finishRequest();

进入到Http1Codec 

  @Override public void finishRequest() throws IOException {
    sink.flush();
  }

5、读取response的内容

     response = response.newBuilder()
          .body(httpCodec.openResponseBody(response))
          .build();

进入到Http1Codec


  @Override public ResponseBody openResponseBody(Response response) throws IOException {
    streamAllocation.eventListener.responseBodyStart(streamAllocation.call);
    String contentType = response.header("Content-Type");

    if (!HttpHeaders.hasBody(response)) {
      Source source = newFixedLengthSource(0);
      return new RealResponseBody(contentType, 0, Okio.buffer(source));
    }

    if ("chunked".equalsIgnoreCase(response.header("Transfer-Encoding"))) {
      Source source = newChunkedSource(response.request().url());
      return new RealResponseBody(contentType, -1L, Okio.buffer(source));
    }

    long contentLength = HttpHeaders.contentLength(response);
    if (contentLength != -1) {
      Source source = newFixedLengthSource(contentLength);
      return new RealResponseBody(contentType, contentLength, Okio.buffer(source));
    }

    return new RealResponseBody(contentType, -1L, Okio.buffer(newUnknownLengthSource()));
  }

猜你喜欢

转载自blog.csdn.net/nihaomabmt/article/details/84339607