掌握Okhttp(3) :拦截器机制及大致流程分析

拦截器是 OkHttp 的核心。OkHttp 的网络请求的过程就是依赖于各种拦截器(Interceptor)实现的,本文将学习okhttp的拦截器机制。

本文源码分析若无特殊说明,均指 3.14.0

一. 拦截器机制

我们先看看 Interceptor 的定义:

Interceptor .class

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 实际上是一个接口,里面只有一个方法 intercept 以及一个接口 Chain

Interceptor

其中,intercept 方法往往是如下的结构:

@Override 
public Response intercept(Chain chain) throws IOException {
    
    
    Request request = chain.request();
    // Request阶段,该拦截器在Request阶段负责做的事情

    // 调用RealInterceptorChain.proceed(),其实是在递归调用下一个拦截器的intercept()方法
    response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);

    // Response阶段,完成了该拦截器在Response阶段负责做的事情,然后返回到上一层的拦截器。
    return response;     
}

这里先调用了 chain.request 方法获取到了本次请求的 Request 对象,

之后调用了 chain.proceed 方法递归调用下一个拦截器的 interceptor 方法。

最后返回了 chain.proceed 方法所返回的 Response

上面简单的三行代码将整个 intercept 过程分为了两个阶段:

  • Request 阶段:执行一些该拦截器在 Request 阶段所负责的事情

  • Response 阶段:完成该拦截器在 Response 阶段所负责的事情

这其实是采用了一种递归的设计,类似我们计算机网络中的分层模型,将 OkHttp 的请求分为了几个阶段,分别代表了不同的拦截器,不同拦截器分别会在这个递归的过程中有两次对该请求的处理的可能,一次是在 Request 之前,一次是在 Response 之后,中间的过程中若出现了错误,则通过抛出异常来通知上层。

预置的 Interceptor 有如下几种:

  • RetryAndFollowUpInterceptor:负责实现重定向功能
  • BridgeInterceptor:将用户构造的请求转换为向服务器发送的请求,将服务器返回的响应转换为对用户友好的响应
  • CacheInterceptor:读取缓存、更新缓存
  • ConnectInterceptor:建立与服务器的连接
  • CallServerInterceptor:从服务器读取响应

可以看出,整个网络请求的过程由各个拦截器互相配合从而实现,通过这种拦截器的机制,可以很方便地调节网络请求的过程及先后顺序,同时也能够很方便地使用户对其进行扩展。

其中用户可以在两个时机插入 Interceptor

  • 网络请求前后:通过 OkHttpClient.addInterceptor 方法添加
  • 读取响应前后:通过 OkHttpClient.addNetworkInterceptor 方法添加

其整体流程如图所示:

在这里插入图片描述

RealInterceptorChain

我们再看看是如何通过 RealInterceptorChain 将整个拦截器的调用过程连接起来的,我们先看看其构造过程:

RealInterceptorChain.class

public RealInterceptorChain(List interceptors, Transmitter transmitter,
                            @Nullable Exchange exchange, int index, Request request, Call call,
                            int connectTimeout, int readTimeout, int writeTimeout) {
    
    
    this.interceptors = interceptors;
    this.transmitter = transmitter;
    this.exchange = exchange;
    this.index = index;
    this.request = request;
    this.call = call;
    this.connectTimeout = connectTimeout;
    this.readTimeout = readTimeout;
    this.writeTimeout = writeTimeout;
}

这里只是一些赋值过程,我们接着看到 chain.proceed 方法,看看它是如何执行的:

RealInterceptorChain.class

public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
        throws IOException {
    
    
    // ...

    //再新建一个RealInterceptorChain,这里注意index加1,
    RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
            index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
    //获取interceptors列表中的下一个拦截器,并执行intercept方法
    Interceptor interceptor = interceptors.get(index);
    //调用下一个拦截器的intercept(Chain)方法,传入刚才新建的RealInterceptorChain,返回Response
    Response response = interceptor.intercept(next);
    // ...
    return response;
}

这里省略了一些异常处理,可以看到它首先构造了下一个拦截器对应的 Chain,之后获取到了当前的拦截器并调用了其 intercept 方法获取其结果,在 intercept 方法的参数中传入的就是下一个拦截器对应的 Chain

proceed()方法中再次新建了一个RealInterceptorChain,传入了index + 1,而获取拦截器时是通过index获取,这样每次都能获取到下一个拦截器,然后调用下一个拦截器的intercept(Chain)方法,intercept(Chain)方法中就是拦截器的主要功能实现,里面会继续调用传入的RealInterceptorChainproceed()方法,这样又会重复上述逻辑,我们把拦截器看作一条链中的节点,这样每个拦截器就通过一个个RealInterceptorChain连接起来,形成一条链,这就是典型的责任链模式,从节点的首部开始把请求传递下去,每一个拦截器都有机会处理这个请求,这又像是一个递归的过程,直到最后一个拦截器器处理完请求后,才开始逐层返回Resquese,拦截器才是Okhttp核心功能所在。

小结

OkHttp 在读取响应的过程中采用了一种责任链模式,预置了多个负责不同功能的拦截器,将它们通过责任链连接在一起,采用了一种递归的方式进行调用,从而使得每一层在请求前和响应后都能对本次请求作出不同的处理,通过各个拦截器的协调合作,最终完成了整个网络请求的过程。

二.自定义拦截器的使用

从一中可知,Interceptor由两部分组成:intercept(Chain)方法和内部接口Chain,下面是自定义一个拦截器的通用逻辑,如下:

public class MyInterceptor implements Interceptor {
    
       
    @Override
    public Response intercept(Chain chain) throws IOException {
    
    
        
        //1、通过传进来的Chain获取Request
        Request request = chain.request();
        
      	//2、 处理Request,逻辑自己写
        //...
        
        //3、调用Chain的proceed(Request)方法处理请求,得到Response
        Response response = chain.proceed(request);
     
        //4、 处理Response,逻辑自己写
        //...
        
        //5、返回Response
        return response;
    }
}

上述就是一个拦截器的通用逻辑,首先我们继承Interceptor实现intercept(Chain)方法,完成我们自己的拦截逻辑,即根据需要进行1、2、3、4、5步,不管是自定义拦截器还是后面介绍的okhttp默认的拦截器大概都是这个模板实现,定义完拦截器后,我们在构造OkhttpChient时就可以通过addInterceptor(Interceptor)或addNetworkInterceptor(Interceptor)添加自定义拦截器,如下:

OkHttpClient client = new OkHttpClient.Builder()
     .addInterceptor(new MyInterceptor())
     .build();

或

OkHttpClient client = new OkHttpClient.Builder()
     .addNetworkInterceptor(new MyInterceptor())
     .build();

这样okhttp在链式调用拦截器处理请求时就会调用到我们自定义的拦截器,那么addInterceptor(Interceptor)和addNetworkInterceptor(Interceptor)有什么不一样呢?它们一个是添加应用拦截器,一个是添加网络拦截器,主要是调用的时机不一样,更多区别可以参考官方WIKI文档Okhttp-wiki 之 Interceptors 拦截器点击跳转,当我们平时做应用开发使用addInterceptor(Interceptor)就行了。
上述是我们自定义的拦截器,下面我们来看看okhttp默认的拦截器都干了什么。

三.大致流程分析

1. RetryAndFollowUpInterceptor

对该类的描述如下:

/**
 * This interceptor recovers from failures and follows redirects as necessary. It may throw an
 * {@link IOException} if the call was canceled.
 * 
 * 此拦截器从故障中恢复,并根据需要进行重定向。如果呼叫被取消,则可能抛出IOException .
 */

我们看到 RetryAndFollowUpInterceptor.intercept 方法


private static final int MAX_FOLLOW_UPS = 20;

@Override
public Response intercept(Chain chain) throws IOException {
    
    
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    // 获取transmitter (发射机)
    Transmitter transmitter = realChain.transmitter();
    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {
    
    
        // 进行一些连接前的准备工作
        transmitter.prepareToConnect(request);
        // 处理取消事件
        if (transmitter.isCanceled()) {
    
    
            throw new IOException("Canceled");
        }
        Response response;
        boolean success = false;
        try {
    
    
            // 调用chain的proceed方法获取下层得到的结果
            response = realChain.proceed(request, transmitter, null);
            success = true;
        } catch (RouteException e) {
    
    
            // 若不满足重定向的条件,抛出异常
            if (!recover(e.getLastConnectException(), transmitter, false, request)) {
    
    
                throw e.getFirstConnectException();
            }
            // 满足重定向条件,重试
            continue;
        } catch (IOException e) {
    
    
            boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
            // 不满足重定向条件,抛出异常
            if (!recover(e, transmitter, requestSendStarted, request)) throw e;
            // 满足重定向条件,重试
            continue;
        } finally {
    
    
            if (!success) {
    
    
                // 若抛出了异常,释放资源
                transmitter.exchangeDoneDueToException();
            }
        }
        // 在本次response中设置上一次的response,其body为空
        if (priorResponse != null) {
    
    
            response = response.newBuilder()
                    .priorResponse(priorResponse.newBuilder()
                            .body(null)
                            .build())
                    .build();
        }
        Exchange exchange = Internal.instance.exchange(response);
        Route route = exchange != null ? exchange.connection().route() : null;
        // 根据response code获取重定向后的request
        Request followUp = followUpRequest(response, route);
        if (followUp == null) {
    
    
            // 不再需要重定向,停止timeout计时并返回response
            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();
        }
        // 重定向不超过20次,否则抛出异常
        if (++followUpCount > MAX_FOLLOW_UPS) {
    
    
            throw new ProtocolException("Too many follow-up requests: " + followUpCount);
        }
        // 修改下次重定向的request
        request = followUp;
        // 记录上一次的response
        priorResponse = response;
    }
}

可以看到,这里外部通过一个循环,实现不断重定向,可以看一下循环内主要做了什么:

  • 1.进行一些预处理
  • 2.调用 chain.proceed 方法进行请求获取 Response
  • 3.过程中若下层抛出异常,则尝试重定向
  • 4.若不满足重定向条件,则抛出异常
  • 5.若出现其他未知的异常,则通过抛出异常释放资源
  • 6.在本次 Response 中设置上一次的 Response priorResponse,且body为空
  • 7.根据 Response 中的 response code 进行重定向,调用 followUpRequest 方法获取重定向后的
    request followUp
  • 8.若重定向后的 followUpnull,说明不再需要重定向,停止 timeout 计时并返回 Response
  • 9.若重定向超过指定次数(默认 20 次),则抛出异常。
  • 10.若仍未返回,则需要下一次重定向,对下一次的 request 等变量进行赋值。

让我们看看 followUpRequest 方法做了什么:

/**
   * Figures out the HTTP request to make in response to receiving {@code userResponse}. This will
   * either add authentication headers, follow redirects or handle a client request timeout. If a
   * follow-up is either unnecessary or not applicable, this returns null.

    找出响应收到{@code userResponse}而发出的HTTP请求。 将添加身份验证标头,遵循重定向或处理客户端请求超时。如果后续跟踪是不必要的或不适用的,则返回null。
   */

private Request followUpRequest(Response userResponse, @Nullable Route route) throws IOException {
    
    
    if (userResponse == null) throw new IllegalStateException();
    int responseCode = userResponse.code();
    final String method = userResponse.request().method();
    switch (responseCode) {
    
    
        case HTTP_PROXY_AUTH:       // 407
            // ...
            // 代理身份认证
        case HTTP_UNAUTHORIZED:     // 401
            // ...
            // 身份认证
        case HTTP_PERM_REDIRECT:    // 308
        case HTTP_TEMP_REDIRECT:    // 307
            // 307、308 两种状态码不对 GET、HEAD 以外的请求重定向
            if (!method.equals("GET") && !method.equals("HEAD")) {
    
    
                return null;
            }
        case HTTP_MULT_CHOICE:      // 300
        case HTTP_MOVED_PERM:       // 301
        case HTTP_MOVED_TEMP:       // 302
        case HTTP_SEE_OTHER:        // 303
            // 若客户端关闭了重定向,则直接返回 null
            if (!client.followRedirects()) return null;
            // 获取LocationHeader以获取重定向目标
            String location = userResponse.header("Location");
            if (location == null) return null;
            HttpUrl url = userResponse.request().url().resolve(location);
            // ...
            Request.Builder requestBuilder = userResponse.request().newBuilder();
            // 处理重定向使用的method
            if (HttpMethod.permitsRequestBody(method)) {
    
    
                final boolean maintainBody = HttpMethod.redirectsWithBody(method);
                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");
                }
            }
            // 重新构建request
            return requestBuilder.url(url).build();
        case HTTP_CLIENT_TIMEOUT:   // 408
            // 408 说明需要重新发送一次相同的请求
            // ...
            return userResponse.request();
        case HTTP_UNAVAILABLE:      // 503
            // ...
            return null;
        default:
            return null;
    }
}

可以看到,主要是针对重定向的几个状态码进行特殊处理,从中取出 Location 字段,构造重定向后的 request

2. BridgeInterceptor

对该类的描述如下:

/**
 * Bridges from application code to network code. First it builds a network request from a user
 * request. Then it proceeds to call the network. Finally it builds a user response from the network
 * response.
 * 
/ ** *从应用程序代码到网络代码的桥梁。首先,它根据用户请求建立一个网络请求。然后,它继续呼叫网络。最后,它从网络响应建立用户响应。 * /
 */

BridgeInterceptor 的名字取的非常形象,它就像一座桥梁,连接了用户与服务器。在用户向服务器发送请求时,它会把用户所构建的请求转换为向服务器请求的真正的 Request,而在服务器返回了响应后,它又会将服务器所返回的响应转换为用户所能够使用的 Response

让我们看到 BridgeInterceptor.intercept 方法:

@Override
public Response intercept(Chain chain) throws IOException {
    
    
    Request userRequest = chain.request();
    Request.Builder requestBuilder = userRequest.newBuilder();
    RequestBody body = userRequest.body();
    // 将一些userRequest中的属性设置进builder中
    if (body != null) {
    
    
        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");
    }
    boolean transparentGzip = false;
    // 若未设置Accept-Encoding,自动设置gzip
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
    
    
        transparentGzip = true;
        requestBuilder.header("Accept-Encoding", "gzip");
    }
    // 将userRequest中的cookies设置进builder
    List cookies = cookieJar.loadForRequest(userRequest.url());
    if (!cookies.isEmpty()) {
    
    
        requestBuilder.header("Cookie", cookieHeader(cookies));
    }
    // 设置user-agent
    if (userRequest.header("User-Agent") == null) {
    
    
        requestBuilder.header("User-Agent", Version.userAgent());
    }
    // 读取服务端响应
    Response networkResponse = chain.proceed(requestBuilder.build());
    // 对响应的header进行处理
    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
    // 根据服务端的响应构建新的Response,并将userRequest设置为其request
    Response.Builder responseBuilder = networkResponse.newBuilder()
            .request(userRequest);
    // 若之前设置了gzip压缩且response中也包含了gzip压缩,则进行gzip解压
    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();
}

可以看到,这里主要对 Header 进行处理,将一些原来 request 中的 Header 进行处理后设置进了新 request,并用其进行请求。其中若调用者未设置 Accept-Encoding,则它会默认设置 gzip

而在对 response 处理时,若之前设置了 gzip,则进行 gzip 解压。这种自动解压会自动将 Content-LengthContent-Encoding 字段从 Header 中移除,因此上层可能会获取到 -1

3.CacheInterceptor

对该类的描述如下:

/** Serves requests from the cache and writes responses to the cache. */
/ **处理来自缓存的请求,并将响应写入缓存。 * /

CacheInterceptor 主要负责了对缓存的读取以及更新,让我们看看其 intercept 方法:

@Override
public Response intercept(Chain chain) throws IOException {
    
    
    // 尝试获取缓存的cache
    Response cacheCandidate = cache != null
            ? cache.get(chain.request())
            : null;
    long now = System.currentTimeMillis();
    // 传入当前时间、request以及从缓存中取出的cache,构建缓存策略
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    // 通过缓存策略获取新的request
    Request networkRequest = strategy.networkRequest;
    // 通过缓存策略获取缓存中取出的response
    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.
    }
    // 根据缓存策略若不能使用网络且没有缓存,则请求失败,构建一个请求失败的Response并返回
    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 (networkRequest == null) {
    
    
        return cacheResponse.newBuilder()
                .cacheResponse(stripBody(cacheResponse))
                .build();
    }
    Response networkResponse = null;
    try {
    
    
        // 网络请求获取response
        networkResponse = chain.proceed(networkRequest);
    } finally {
    
    
        // 如果IO的过程中出现了crash,回收资源
        if (networkResponse == null && cacheCandidate != null) {
    
    
            closeQuietly(cacheCandidate.body());
        }
    }
    // 如果缓存中有缓存,并且请求的code为304,则结合缓存及网络请求结果后返回,并且更新缓存中的内容
    if (cacheResponse != null) {
    
    
        if (networkResponse.code() == HTTP_NOT_MODIFIED) {
    
      // 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());
        }
    }
    // 构建response
    Response response = networkResponse.newBuilder()
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
    // 对请求响应进行缓存
    if (cache != null) {
    
    
        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;
}

可以看到,这里主要是以下步骤

  1. 尝试从缓存中获取了缓存的 response
  2. 根据 当前时间、request、缓存的response 构建缓存策略。
  3. 若缓存策略不能使用网络(networkRequest == null),且无缓存(cacheResponse ==null),则直接请求失败。
  4. 若缓存策略不能使用网络,由于前面有判断所以可以确定有缓存,直接构建缓存的 response 并返回。
    调用 chain.proceed 网络请求获取 response
  5. code 304 作出处理,结合本地及网络返回数据构建 response 并返回
  6. 构建网络请求的所获得的 response ,并且由于该网络请求并未进行过缓存,进行缓存并返回结果

而关于缓存相关的具体实现这里先不过多做介绍,这里主要以流程为主。

4.ConnectInterceptor

对该类的描述如下:

/** Opens a connection to the target server and proceeds to the next interceptor. */
/ **打开与目标服务器的连接,然后进入下一个拦截器。 * /

ConnectInterceptor 主要负责的是与服务器的连接的建立,它的代码非常短:

@Override
public Response intercept(Chain chain) throws IOException {
    
    
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    Transmitter transmitter = realChain.transmitter();
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    // 构建Exchange
    Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);
    return realChain.proceed(request, transmitter, exchange);
}

这里主要是调用 transmitter.newExchange 构建一个 Exchange,之后调用了 realChain.proceed(request, transmitter, exchange) 方法。

这个 Exchange 类究竟是什么呢?我们看到它的 JavaDoc:

Transmits a single HTTP request and a response pair. This layers connection management and events on {
    
    @link ExchangeCodec}, which handles the actual I/O. 

也就是说 Exchange 类可以将 ExchangeCodec 这个类的连接管理及事件进行分层,而 ExchangeCodec 是一个真正执行 I/O 的类,看来这个类主要是进行一些连接管理的事务。在 newExchange 的过程中可能就创建/复用了客户与服务器的连接。

这里具体的连接获取过程我们暂时先不做介绍,此篇文章更偏向整体流程的讲解。

5.CallServerInterceptor

对该类的描述如下:

/** This is the last interceptor in the chain. It makes a network call to the server. */
/ **这是链中的最后一个拦截器。它对服务器进行网络呼叫。 * /

CallServerInterceptor 是整个网络请求链的最后一个拦截器,它真正实现了对服务器 Response 的读取,让我们看看它的实现:

@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) {
    
    
        // 对 100-continue 这一 header 做特殊处理
        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;
}

这里代码量非常多,但其实核心是下面几步:

  1. 写入Request Header
  2. 写入Request Body
  3. 读取Response Header
  4. 读取Response Body

到了这里整个责任链的大体流程我们就分析完了。

推荐阅读:

  1. okhttp3源码分析之请求流程

猜你喜欢

转载自blog.csdn.net/gaolh89/article/details/104339688