OkHttp interceptor

Interceptors are a powerful mechanism provided in OkHttp that enables network listening, request and response rewriting, and retry of request failures.

As shown in the figure above, this is the interceptor that OkHttp provides to us internally, which is that when we initiate an HTTP request, OkHttp executes the HTTP request through this chain of interceptors. These include:

  • RetryAndFollowUpInterceptor retry and redirect the interceptor
  • BridgeInterceptor: Bridge and adapter interceptor
  • CacheInterceptor: CacheInterceptor
  • ConnectInterceptor: link interceptor
  • CallServerInterceptor: Request and processing response interceptor

BridgeInterceptor and CacheInterceptor are primarily intended to complement some of the required HTTP request headers and processing caches that are missing from user request creation.

The ConnectInterceptor is mainly responsible for establishing usable links, while the CallServerInterceptor is mainly responsible for writing HTTP requests into the NETWORK I/O stream and reading data returned by the server to the client from the NETWORK I/O stream.

Source code analysis

getResponseWithInterceptorChain()

Article OkHttpClient source code analysis (a) – synchronous and asynchronous request execution process and source code analysis is mentioned to a very important method getResponseWithInterceptorChain (), synchronous request, This method is called from the excute() method of the RealCall class. Asynchronous requests are called from the Excute () method of the AsyncCal inner class of RealCall.

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);
}
Copy the code

This method initializes an Interceptor collection, adds the interceptors configured in OkHttpClient, and then adds the five interceptors mentioned above.

Create a RealInterceptorChain and perform the proceed() method on the chain.

Check out the proceed() method of the RealInterceptorChain class:

 public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream,
      Connection connection) throws IOException {
      
      ...
      
      // Call the next interceptor inthe chain. RealInterceptorChain next = new RealInterceptorChain( interceptors, streamAllocation, httpStream, connection, index + 1, request); Interceptor interceptor = interceptors.get(index); Response response = interceptor.intercept(next); . }Copy the code

The RealInterceptorChain object is created again, passing in the next chain of interceptors, index + 1, and passing in the next chain of interceptors by calling the current Interceptor’s Intercept () method. The Response object is obtained, and the Intercept () method of the interceptor is examined below.

RetryAndFollowUpInterceptor

Reconnection role is largely responsible for the network request failure, it is important to note that not all the requests after failure can be heavy, it has certain limits, will help us to detect network anomalies OkHttp internal and judgment of the response code, if are within the limits of it, will be for network reconnection.

Intercept ()

@Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); streamAllocation = new StreamAllocation( client.connectionPool(), createAddress(request.url())); int followUpCount = 0; Response priorResponse = null; . }Copy the code

This creates a StreamAllocation object. The StreamAllocation object is used to set up the network components needed to execute the Http request. As its name indicates, this object is used to allocate streams. It is mainly used to obtain the connection to the server and the input and output streams for data transmission with the server.

The logic is detailed in the while loop in the Intercept () method. We won’t go into detail here, but we’ll focus on this one:

 while (true) {... try { response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null); releaseConnection =false; }...if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release();
        throw new ProtocolException("Too many follow-up requests: "+ followUpCount); }... }Copy the code

The RealInterceptorChain calls the proceed() method, which creates a RealInterceptorChain object (index + 1, the next interceptor chain), and uses the index to retrieve the interceptor currently executed. Call the intercept() method of the interceptor. In the Intercept () method, the proceed() method of the RealInterceptorChain is called again to form recursion.

MAX_FOLLOW_UPS is a constant with a value of 20, that is, 20 retries at most. The StreamAllocation object is released and a ProtocolException is thrown.

Conclusion:

  1. Create the StreamAllocation object
  2. Requests for network call RealInterceptorChain. Proceed ()
  3. Determine whether to rerequest based on the abnormal result or response result
  4. Calls the next interceptor, processes the response, and returns it to the previous interceptor

BridgeInterceptor

Intercept () :

@Override public Response intercept(Chain chain) throws IOException { Request userRequest = chain.request(); Request.Builder requestBuilder = userRequest.newBuilder(); .if (userRequest.header("Host") == null) {
      requestBuilder.header("Host", hostHeader(userRequest.url(), false));
    }

    if (userRequest.header("Connection") == null) {
      requestBuilder.header("Connection"."Keep-Alive"); }... Response networkResponse = chain.proceed(requestBuilder.build()); .if (transparentGzip
        && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding")) && HttpHeaders.hasBody(networkResponse)) { 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();
  }
Copy the code

The main operation is to add the Request header to the initiated Request, which also calls the proceed() method to recursively invoke the next interceptor, followed by decompressing the gzip-compressed Response. Check whether gzip compression is supported and whether the “content-encoding” value in the request header is “gzip” to determine whether gzip decompression is required.

Conclusion:

  1. Responsible for converting user-constructed Request requests into network access requests;
  2. Make a network Request to the Request that meets the network Request;
  3. Convert the corresponding Response returned by the network request into the Response available to the user

The next article will introduce OkHttp’s caching mechanism. If you are interested, you can read on:

OkHttpClient source code analysis (three) — caching mechanism introduction