# Okhttp Detailed analytical -Interceptor

Detailed analytical -Interceptor Okhttp

Interceptor is arguably one of the essence of okhttp, Okhttp rewrite request / response, Retry, response caching and other operations, are basically done in each Interceptor, the last article analyzing running processes Okhttp simply passing, then this articles will be analyzed in detail under the interceptor and interceptor chain mechanism.

A, Interceptor and InterceptorChain

/**
 * Observes, modifies, and potentially short-circuits requests going out and the corresponding
 * responses coming back in. Typically interceptors add, remove, or transform headers on the request
 * or response.
 */
public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    /**
     * Returns the connection the request will be executed on. This is only available in the chains
     * of network interceptors; for application interceptors this is always null.
     */
    @Nullable Connection connection();

    Call call();

    int connectTimeoutMillis();

    Chain withConnectTimeout(int timeout, TimeUnit unit);

    int readTimeoutMillis();

    Chain withReadTimeout(int timeout, TimeUnit unit);

    int writeTimeoutMillis();

    Chain withWriteTimeout(int timeout, TimeUnit unit);
  }
}

Interceptor most important is its intercept () function, InterceptorChain Interceptor is an inner class, its main role is to call an orderly chain Interceptor.

Okhttp built five Interceptor they are RetryAndFollowUpInterceptor
, BridgeInterceptor, CacheInterceptor, ConnectInterceptor
, CallServerInterceptor. It is these five Interceptor complete rewrite, retry, cache, requests, etc., then we analyze them one by one role.

Two, Interceptor chained calls

Inlet interceptor is invoked in ReallCall getResponseWithInterceptorChain (the function).

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(new RetryAndFollowUpInterceptor(client));
    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, transmitter, null, 0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());//1

    boolean calledNoMoreExchanges = false;
    try {
      Response response = chain.proceed(originalRequest);//2
      if (transmitter.isCanceled()) {
        closeQuietly(response);
        throw new IOException("Canceled");
      }
      return response;
    } catch (IOException e) {
      calledNoMoreExchanges = true;
      throw transmitter.noMoreExchanges(e);
    } finally {
      if (!calledNoMoreExchanges) {
        transmitter.noMoreExchanges(null);
      }
    }
  }

We see that the function outset constructed a List and then add all of the Interceptor to the inside, including our custom Application Interceptor, Interceptor System Preferences, custom Network Interceptor and so on.
Then RealInterceptorChain example of the structure 1 comments and pass interceptor list just there to see what we passed in the fourth argument here is 0. This parameter indicates the next step is to call interceptor list in which the interceptor.
Finally, note at 2 calls chain.proceed () and passing in the original request.

@Override public Response proceed(Request request) throws IOException {
  return proceed(request, transmitter, exchange);
}

public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
    throws IOException {
  if (index >= interceptors.size()) throw new AssertionError();

  calls++;

  // If we already have a stream, confirm that the incoming request will use it.
  if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) {
    throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
        + " must retain the same host and port");
  }

  // If we already have a stream, confirm that this is the only call to chain.proceed().
  if (this.exchange != null && calls > 1) {
    throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
        + " must call proceed() exactly once");
  }

  // Call the next interceptor in the chain.
  RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
      index + 1, request, call, connectTimeout, readTimeout, writeTimeout);//1
  Interceptor interceptor = interceptors.get(index);//2
  Response response = interceptor.intercept(next);//3

  // Confirm that the next interceptor made its required call to chain.proceed().
  if (exchange != null && index + 1 < interceptors.size() && next.calls != 1) {
    throw new IllegalStateException("network interceptor " + interceptor
        + " must call proceed() exactly once");
  }

  // Confirm that the intercepted response isn't null.
  if (response == null) {
    throw new NullPointerException("interceptor " + interceptor + " returned null");
  }

  if (response.body() == null) {
    throw new IllegalStateException(
        "interceptor " + interceptor + " returned a response with no body");
  }

  return response;
}

proceed function we focus on operating three comments at.
Note in the configuration RealInterceptorChain 1, wherein the parameter becomes 4 The index + 1, index is passed before the getResponseWithInterceptorChain constructed RealInterceptorChain value.

Note the corresponding 2 taken from Interceptor Interceptor List by index.
Note 3 at the Interceptor call Intercept () method, a configuration of the parameters into RealInterceptorChain comment. Because Interceptor operations are in the intercept () function to complete, so the operation is completed the current call to the Interceptor. While the intercept of each Interceptor () function will be called next.proceed () This opens the next Interceptor calls, and so forth final same as the chain turn call Interceptor list all the Interceptor.

Third, explain each Interceptor

We analyzed the top Interceptor is called the order, the order here refers added to the Interceptor list in order. Sequence by the getResponseWithInterceptorChain () method can be invoked sequentially (not considering the case of adding custom Interceptor): RetryAndFollowUpInterceptor -> BridgeInterceptor -> CacheInterceptor -> ConnectInterceptor -> CallServerInterceptor.

RetryAndFollowUpInterceptor

The main role of RetryAndFollowUpInterceptor is retried and redirection.
Look at its Interceptor method.

@Override public Response intercept(Chain chain) throws IOException {
  Request request = chain.request();//获取传入的chain的request 此处的request是next的request
  RealInterceptorChain realChain = (RealInterceptorChain) chain;
  Transmitter transmitter = realChain.transmitter();

  int followUpCount = 0;
  Response priorResponse = null;
  while (true) {//开启一个无限循环
    transmitter.prepareToConnect(request);

    if (transmitter.isCanceled()) {//如果此时请求被cancel抛出异常
      throw new IOException("Canceled");
    }

    Response response;
    boolean success = false;
    try {
      response = realChain.proceed(request, transmitter, null);//调用next的proceed方法,即调用下一个Interceptor
      success = true;
    } catch (RouteException e) {//如果通过某个route连接失败则尝试恢复。注意此时请求尚未发送出去
      // The attempt to connect via a route failed. The request will not have been sent.
      if (!recover(e.getLastConnectException(), transmitter, false, request)) {
        throw e.getFirstConnectException();
      }
      continue;
    } catch (IOException e) {//如果连接server失败则尝试恢复,注意此时请求已发送。
      // An attempt to communicate with a server failed. The request may have been sent.
      boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
      if (!recover(e, transmitter, requestSendStarted, request)) throw e;
      continue;
    } finally {
      // The network call threw an exception. Release any resources.
      if (!success) {//执行不成功关闭
        transmitter.exchangeDoneDueToException();
      }
    }

    // Attach the prior response if it exists. Such responses never have a body.
    if (priorResponse != null) {//priorResponse不为空表示之前发生过重定向,此时为本次的response设置priorResponse
      response = response.newBuilder()
          .priorResponse(priorResponse.newBuilder()
                  .body(null)
                  .build())
          .build();
    }

    Exchange exchange = Internal.instance.exchange(response);
    Route route = exchange != null ? exchange.connection().route() : null;
    Request followUp = followUpRequest(response, route);//生成重定向的请求

    if (followUp == null) {//followUp == null说明无需重定向,返回当前respone
      if (exchange != null && exchange.isDuplex()) {
        transmitter.timeoutEarlyExit();
      }
      return response;
    }

    RequestBody followUpBody = followUp.body();
    if (followUpBody != null && followUpBody.isOneShot()) {
      return response;
    }

    closeQuietly(response.body());
    if (transmitter.hasExchange()) {
      exchange.detachWithViolence();
    }

    if (++followUpCount > MAX_FOLLOW_UPS) {//判断是否超过最大重定向次数
      throw new ProtocolException("Too many follow-up requests: " + followUpCount);
    }

    request = followUp;
    priorResponse = response;
  }
}

To, carding at the entire process:

  1. Open an infinite loop until there is no redirection or an exception is thrown will end.
  2. Processing cancel
  3. Call the next Interceptor get respone
  4. If an exception occurs in step 3, to try to recover retry request
  5. If an abnormality has not occurred in step 3, priorResponse not empty (before if redirection had priorResponse shown), is provided for the current respone priorResponse (note priorResponse body is null)
  6. The return code of the call followUpRequest generates a request redirection
  7. Step 6 generated request redirection indicates that no empty, return current respone, end loop
  8. Step 6 generates a request does not need to redirect showing empty, then redirects the +1 frequency and then determines whether the number exceeds the maximum redirect, does not exceed the jumps to step 2 for the next cycle begins.

Step 3 is the key to the interceptor chain can be invoked orderly.
Step 4 determines whether the energy recovery by Recover () function, particularly the Annotated

/**
 * Report and attempt to recover from a failure to communicate with a server. Returns true if
 * {@code e} is recoverable, or false if the failure is permanent. Requests with a body can only
 * be recovered if the body is buffered or if the failure occurred before the request has been
 * sent.
 */
private boolean recover(IOException e, Transmitter transmitter,
    boolean requestSendStarted, Request userRequest) {
  // The application layer has forbidden retries.
//应用设置禁止重试 返回false
  if (!client.retryOnConnectionFailure()) return false;

  // We can't send the request body again.
//request设置仅能执行一次 返回false
  if (requestSendStarted && requestIsOneShot(e, userRequest)) return false;

  // This exception is fatal.
//异常是致命的 返回false
  if (!isRecoverable(e, requestSendStarted)) return false;

  // No more routes to attempt.
//没有更多的route可供尝试 返回fasle
  if (!transmitter.canRetry()) return false;

  // For failure recovery, use the same route selector with a new connection.
  return true;
}

As can be seen the recovery operation is conditional, in certain conditions can not be restored. Another said above unrecoverable fatal exception has ProtocolException, InterruptedIOException, SSLHandshakeException, CertificateException
, SSLPeerUnverifiedException.

followUpRequest Step 6 can be said to redirect the heart, and look followUpRequest function

private Request followUpRequest(Response userResponse, @Nullable Route route) throws IOException {
  if (userResponse == null) throw new IllegalStateException();
  int responseCode = userResponse.code();//获取当前respone的返回code

  final String method = userResponse.request().method();
  switch (responseCode) {//根据responseCode的值分情况处理
    case HTTP_PROXY_AUTH://407
      Proxy selectedProxy = route != null
          ? route.proxy()
          : client.proxy();
      if (selectedProxy.type() != Proxy.Type.HTTP) {
        throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy");
      }
      return client.proxyAuthenticator().authenticate(route, userResponse);//代理验证

    case HTTP_UNAUTHORIZED://401
      return client.authenticator().authenticate(route, userResponse);//身份认证

    case HTTP_PERM_REDIRECT:
    case HTTP_TEMP_REDIRECT:
      // "If the 307 or 308 status code is received in response to a request other than GET
      // or HEAD, the user agent MUST NOT automatically redirect the request"
      if (!method.equals("GET") && !method.equals("HEAD")) {//307、308 两种code不对 GET、HEAD 以外的请求重定向
        return null;
      }
      // fall-through
    case HTTP_MULT_CHOICE://300
    case HTTP_MOVED_PERM://301
    case HTTP_MOVED_TEMP://302
    case HTTP_SEE_OTHER://303 
//以上这四种code是可以进行重定向的
      // Does the client allow redirects?
      if (!client.followRedirects()) return null;//客户端不允许重定向 返回null

      String location = userResponse.header("Location");//获取Location以确定重定向目标
      if (location == null) return null;//Response的Location为null 返回null
      HttpUrl url = userResponse.request().url().resolve(location);

      // Don't follow redirects to unsupported protocols.
      if (url == null) return null;

      // If configured, don't follow redirects between SSL and non-SSL.
      boolean sameScheme = url.scheme().equals(userResponse.request().url().scheme());
      if (!sameScheme && !client.followSslRedirects()) return null;

      // Most redirects don't include a request body.
      Request.Builder requestBuilder = userResponse.request().newBuilder();
      if (HttpMethod.permitsRequestBody(method)) {
        final boolean maintainBody = HttpMethod.redirectsWithBody(method);//是否带body重定向
        if (HttpMethod.redirectsToGet(method)) {
          requestBuilder.method("GET", null);
        } else {
          RequestBody requestBody = maintainBody ? userResponse.request().body() : null;
          requestBuilder.method(method, requestBody);
        }
        if (!maintainBody) {
          requestBuilder.removeHeader("Transfer-Encoding");
          requestBuilder.removeHeader("Content-Length");
          requestBuilder.removeHeader("Content-Type");
        }
      }

      // When redirecting across hosts, drop all authentication headers. This
      // is potentially annoying to the application layer since they have no
      // way to retain them.
      if (!sameConnection(userResponse.request().url(), url)) {
        requestBuilder.removeHeader("Authorization");
      }

      return requestBuilder.url(url).build();//返回构造的重定向request

    case HTTP_CLIENT_TIMEOUT://408 实际很少用到,一般需要重复发送一个相同的请求
      // 408's are rare in practice, but some servers like HAProxy use this response code. The
      // spec says that we may repeat the request without modifications. Modern browsers also
      // repeat the request (even non-idempotent ones.)
      if (!client.retryOnConnectionFailure()) {
        // The application layer has directed us not to retry the request.
        return null;
      }

      RequestBody requestBody = userResponse.request().body();
      if (requestBody != null && requestBody.isOneShot()) {
        return null;
      }

      if (userResponse.priorResponse() != null
          && userResponse.priorResponse().code() == HTTP_CLIENT_TIMEOUT) {
        // We attempted to retry and got another timeout. Give up.
        return null;
      }

      if (retryAfter(userResponse, 0) > 0) {
        return null;
      }

      return userResponse.request();

    case HTTP_UNAVAILABLE://503
      if (userResponse.priorResponse() != null
          && userResponse.priorResponse().code() == HTTP_UNAVAILABLE) {
        // We attempted to retry and got another timeout. Give up.
        return null;
      }

      if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {
        // specifically received an instruction to retry without delay
        return userResponse.request();
      }

      return null;

    default:
      return null;
  }
}

followUpRequest mainly deal with the situation based on code division returned, if the redirect request to allow the new structure is returned else null indicates no redirection.

BridgeInterceptor

BridgeInterceptor major role in bridging the interceptor is called our incoming request "rewritten" to request the actual request to the server, after receiving the respone "rewrite" respone. View of its intercept function

@Override public Response intercept(Chain chain) throws IOException {
  Request userRequest = chain.request();
  Request.Builder requestBuilder = userRequest.newBuilder();

  RequestBody body = userRequest.body();//获取请求body
  if (body != null) {//请求发送前“重写”headers
    MediaType contentType = body.contentType();
    if (contentType != null) {
      requestBuilder.header("Content-Type", contentType.toString());
    }

    long contentLength = body.contentLength();
    if (contentLength != -1) {
      requestBuilder.header("Content-Length", Long.toString(contentLength));
      requestBuilder.removeHeader("Transfer-Encoding");
    } else {
      requestBuilder.header("Transfer-Encoding", "chunked");
      requestBuilder.removeHeader("Content-Length");
    }
  }

  if (userRequest.header("Host") == null) {
    requestBuilder.header("Host", hostHeader(userRequest.url(), false));
  }

  if (userRequest.header("Connection") == null) {
    requestBuilder.header("Connection", "Keep-Alive");
  }

  // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
  // the transfer stream.
  boolean transparentGzip = false;
  if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
    transparentGzip = true;
    requestBuilder.header("Accept-Encoding", "gzip");
  }

  List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
  if (!cookies.isEmpty()) {//cookie不为空则添加cookie
    requestBuilder.header("Cookie", cookieHeader(cookies));
  }

  if (userRequest.header("User-Agent") == null) {//设置User-Agent
    requestBuilder.header("User-Agent", Version.userAgent());
  }

  Response networkResponse = chain.proceed(requestBuilder.build());//开启下一个拦截器的调用

  HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());//从这里开始是收到respone然后对其“重写”

  Response.Builder responseBuilder = networkResponse.newBuilder()
      .request(userRequest);

  if (transparentGzip
      && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
      && HttpHeaders.hasBody(networkResponse)) {//如果之前采用gzip进行压缩,那么需要对respone进行解压
    GzipSource responseBody = new GzipSource(networkResponse.body().source());
    Headers strippedHeaders = networkResponse.headers().newBuilder()
        .removeAll("Content-Encoding")
        .removeAll("Content-Length")
        .build();
    responseBuilder.headers(strippedHeaders);
    String contentType = networkResponse.header("Content-Type");
    responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
  }

  return responseBuilder.build();
}

BridgeInterceptor completed work can be divided into three steps:

  1. Before requesting transmits request "rewriting", request the rewritten request is actually going to request.
  2. Under chain.proceed open a call blocker call and get the respone
  3. Respone returned to a "rewrite", we get respone is to rewrite the

On request and respone of "rewriting" basically against their headers, such as Accept-EncodingOkhttp will not set you before sending a request to add to your cookie in the cookie that case, if you need to get decompress after respone Okhttp will be decompressed automatically for you.

CacheInterceptor

CacheInterceptor is a cache interceptor, the main role is defined Okhttp of caching mechanism.

@Override public Response intercept(Chain chain) throws IOException {
  Response cacheCandidate = cache != null
      ? cache.get(chain.request())
      : null;//若当前有cache则根据请求获取对应的缓存

  long now = System.currentTimeMillis();

  //构造缓存策略
  CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
  Request networkRequest = strategy.networkRequest;
  Response cacheResponse = strategy.cacheResponse;

  if (cache != null) {
    cache.trackResponse(strategy);
  }

  if (cacheCandidate != null && cacheResponse == null) {//有缓存但不可用关闭
    closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
  }

  // If we're forbidden from using the network and the cache is insufficient, fail.

  //如果设置禁止从网络获取响应且缓存不可用那么返回失败
  if (networkRequest == null && cacheResponse == null) {
    return new Response.Builder()
        .request(chain.request())
        .protocol(Protocol.HTTP_1_1)
        .code(504)
        .message("Unsatisfiable Request (only-if-cached)")
        .body(Util.EMPTY_RESPONSE)
        .sentRequestAtMillis(-1L)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();
  }

  // If we don't need the network, we're done.
  //从cache获取respone不使用网络
  if (networkRequest == null) {
    return cacheResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .build();
  }

  Response networkResponse = null;
  try {
    networkResponse = chain.proceed(networkRequest);//调用下一个拦截器,从网络获取respone
  } finally {
    // If we're crashing on I/O or otherwise, don't leak the cache body.
    if (networkResponse == null && cacheCandidate != null) {
      closeQuietly(cacheCandidate.body());
    }
  }

  // If we have a cache response too, then we're doing a conditional get.
  if (cacheResponse != null) {
    if (networkResponse.code() == HTTP_NOT_MODIFIED) {//从网络获取respone且缓存非空 如果返回码为304 则更新缓存然后返回
      Response response = cacheResponse.newBuilder()
          .headers(combine(cacheResponse.headers(), networkResponse.headers()))
          .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
          .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
          .cacheResponse(stripBody(cacheResponse))
          .networkResponse(stripBody(networkResponse))
          .build();
      networkResponse.body().close();

      // Update the cache after combining headers but before stripping the
      // Content-Encoding header (as performed by initContentStream()).
      cache.trackConditionalCacheHit();
      cache.update(cacheResponse, response);
      return response;
    } else {
      closeQuietly(cacheResponse.body());
    }
  }

  //没有缓存可供使用,读取网络响应构造respone
  Response response = networkResponse.newBuilder()
      .cacheResponse(stripBody(cacheResponse))
      .networkResponse(stripBody(networkResponse))
      .build();

  if (cache != null) {//cache不为空 把respone缓存到cache
    if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
      // Offer this request to the cache.
      CacheRequest cacheRequest = cache.put(response);
      return cacheWritingResponse(cacheRequest, response);
    }

    if (HttpMethod.invalidatesCache(networkRequest.method())) {
      try {
        cache.remove(networkRequest);
      } catch (IOException ignored) {
        // The cache cannot be written.
      }
    }
  }

  return response;
}

The whole process is as follows:

  1. Determine whether Cache, any attempt to get to the cache according to request
  2. Caching policy configuration
  3. Step 1 respone acquired buffer is not empty, but is not available, the connection is closed
  4. Setting prohibited acquisition response from the network is unavailable and the cache 504 is returned failure
  5. Obtained from the cache respone not use the network (steps 1-5 is in effect when the request first tries to get cached if it fails to acquire only from the network or from the local cache is returned)
  6. The next call interceptor, get respone from the network
  7. Get respone and from the network buffer is not empty, if the return code 304 to update the cache and then return
  8. No cache is available, the network in response to the read configuration respone
  9. cache buffer is not empty respone to the cache (step 6-9 respone role is acquired from a network and then locally cached obtained respone)

CacheInterceptor involved in two classes: Cache, CacheStrategy, not to analyze here first, and so do the analysis Okhttp caching mechanisms in detail.

ConnectInterceptor

ConnectInterceptor is connected to the interceptor, it's intercept function is very simple:

@Override public Response intercept(Chain chain) throws IOException {
  RealInterceptorChain realChain = (RealInterceptorChain) chain;
  Request request = realChain.request();
  Transmitter transmitter = realChain.transmitter();//获取Transmitter
  // We need the network to satisfy this request. Possibly for validating a conditional GET.
  boolean doExtensiveHealthChecks = !request.method().equals("GET");
  Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);//构造Exchange

  return realChain.proceed(request, transmitter, exchange);//开启下一个拦截器调用并传入Transmitter、Exchange。
}

Transmitter get it first, and then create a method Transmitter Exchange by newExchange of passing it to the next interceptor.
Transmitter is a bridge between applications and networks, configured by an Exchange example transmitter.newExchange

Exchange newExchange(Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
  synchronized (connectionPool) {
    if (noMoreExchanges) {
      throw new IllegalStateException("released");
    }
    if (exchange != null) {
      throw new IllegalStateException("cannot make a new request because the previous response "
          + "is still open: please call response.close()");
    }
  }

  ExchangeCodec codec = exchangeFinder.find(client, chain, doExtensiveHealthChecks);
  Exchange result = new Exchange(this, call, eventListener, exchangeFinder, codec);

  synchronized (connectionPool) {
    this.exchange = result;
    this.exchangeRequestDone = false;
    this.exchangeResponseDone = false;
    return result;
  }
}

newExchange mainly do two things: get a call ExchangeFinder.find ExchangeCodec, construct a Exchange.
ExchangeFinder is responsible for creating connections to create a good connection into the connection pool, the connection pool if the connection is already there, it is taken out directly reuse. ExchangeCodec is the HTTP request and HTTP response code
Exchange is used for sending and receiving HTTP request and respone.

CallServerInterceptor

CallServerInterceptor is the last interceptor chain blocker, such as interaction with the server, request and receive a response it is completed.

@Override public Response intercept(Chain chain) throws IOException {
  RealInterceptorChain realChain = (RealInterceptorChain) chain;
  Exchange exchange = realChain.exchange();
  Request request = realChain.request();

  long sentRequestMillis = System.currentTimeMillis();

  exchange.writeRequestHeaders(request);//向服务端写请求

  boolean responseHeadersStarted = false;
  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"))) {
      exchange.flushRequest();
      responseHeadersStarted = true;
      exchange.responseHeadersStart();
      responseBuilder = exchange.readResponseHeaders(true);
    }

    if (responseBuilder == null) {
      if (request.body().isDuplex()) {
        // Prepare a duplex body so that the application can send a request body later.
        exchange.flushRequest();
        BufferedSink bufferedRequestBody = Okio.buffer(
            exchange.createRequestBody(request, true));
        request.body().writeTo(bufferedRequestBody);
      } else {
        // Write the request body if the "Expect: 100-continue" expectation was met.
        BufferedSink bufferedRequestBody = Okio.buffer(
            exchange.createRequestBody(request, false));
        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
      }
    } else {
      exchange.noRequestBody();
      if (!exchange.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.
        exchange.noNewExchangesOnConnection();
      }
    }
  } else {
    exchange.noRequestBody();
  }

  if (request.body() == null || !request.body().isDuplex()) {
    exchange.finishRequest();
  }

  if (!responseHeadersStarted) {
    exchange.responseHeadersStart();//从服务端获取请求
  }

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

  Response response = responseBuilder
      .request(request)
      .handshake(exchange.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
    response = exchange.readResponseHeaders(false)
        .request(request)
        .handshake(exchange.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    code = response.code();
  }

  exchange.responseHeadersEnd(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(exchange.openResponseBody(response))
        .build();
  }

  if ("close".equalsIgnoreCase(response.request().header("Connection"))
      || "close".equalsIgnoreCase(response.header("Connection"))) {
    exchange.noNewExchangesOnConnection();
  }

  if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
    throw new ProtocolException(
        "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
  }

  return response;
}

In ConnectInterceptor We have established a connection to the server, acquiring the input and output streams, so CallServerInterceptor the intercept (Chain) logical method is to send the request to the server, and then acquires a response from the server.

send request

write request header by Exchange of writeRequestHeaders (request) method; If the request is not empty body, body write request by okio.

Get a response

Exchange Response header is read by the readResponseHeaders (boolean) method; read by the body in response to the openResponseBody Exchange (the Response) method.

As can be seen a transmission request acquisition response is carried out by exchange.

At this point the interceptor mechanism Okhttp we have finished the analysis, these are Okhttp already defined interceptors, in actual use, we can customize the interceptor to complete our intended function.

Guess you like

Origin www.cnblogs.com/Robin132929/p/12151889.html