美文网首页Android程序员Android开源框架源码解析
Okhttp3源码分析(3) Interceptor拦截器

Okhttp3源码分析(3) Interceptor拦截器

作者: 导火索 | 来源:发表于2018-05-26 09:01 被阅读31次

    okhttp3源码分析基于okhttp3.10.0。

    在前面章节里提到过,okhttp不管是同步请求还是异步请求,最终都是通过RealCall.getResponseWithInterceptorChain()方法获取请求响应的,该方法的核心功能就是要在本章节介绍的okhttp的Interceptor拦截器的工作机制。

    Interceptor介绍

    关于okhttp的Interceptor拦截器在官网也有具体说明:https://github.com/square/okhttp/wiki/Interceptors

    Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls.

    上面这句话是okhttp官网中对Interceptor功能的总结,翻译过来的意思就是 拦截器是一种强大的机制,可以监视,重写,重试调用



    为了更清晰的理解拦截器的作用,下面通过两个实例来说明Interceptor的具体作用,以便于后面更好的理解okhttp内置的五大拦截器。

    自定义拦截器

    要想实现一个自定义拦截器只需要实现Interceptor接口并重写intercept(Chain)接口即可。

    自定义日志拦截器

    这里引用官网的自定义日志拦截器LoggingInterceptor,主要实现请求信息和响应信息的输出,这个在实际开发中是很有意义的,通过在控制台输出请求响应,能提高接口调试的效率。

    LoggingInterceptor如下:

        class LoggingInterceptor implements Interceptor {
            @Override public Response intercept(Interceptor.Chain chain) throws IOException {
                Request request = chain.request();
    
                // 打印请求信息:请求url,头部信息,连接信息
                long t1 = System.nanoTime();
                logger.info(String.format("Sending request %s on %s%n%s",
                        request.url(), chain.connection(), request.headers()));
    
                // 通过调用该方法获取响应
                Response response = chain.proceed(request);
    
                // 打印响应信息:请求url,请求时间,请求头
                long t2 = System.nanoTime();
                logger.info(String.format("Received response for %s in %.1fms%n%s",
                        response.request().url(), (t2 - t1) / 1e6d, response.headers()));
    
                return response;
            }
        }
    
    

    如果想要在okhttp中使用该拦截器,只需要为OkHttpClient实例设置该拦截器即可,如下:

    OkHttpClient client = new OkHttpClient.Builder()
        // 设置一个拦截器
        .addInterceptor(new LoggingInterceptor())
        // 设置一个网络拦截器
        .addaddNetworkInterceptor(new LogingInterceptor())
        .build();
    

    为OkHttpClient实例设置拦截器有两种方式:一个通过addInterceptor()方法,一个是addaddNetworkInterceptor()方法。

    关于这两种拦截器官网也有说明,这里仅仅展示一张图,更多的可以查看官网介绍。

    自定义加解密拦截器

    自定义编解码拦截器可以体现Interceptor的一大功能点:重写

    关于重写的介绍,可以查看官网介绍:

    实际需求:对于App中的支付功能,对传输安全的要求比较高,一般在网络交互过程都应该避免明文传入一些敏感的数据,例如支付金额,账户信息等,以防止支付信息被篡改从而导致损失。

    基于上述的需求,这里可以通过实现一个自定义加解密Interceptor来实现请求信息的加密和响应信息的解密。之所以将加解密的过程通过拦截器来实现,最大的好处就是可以让业务层更专注于数据的创建,对开发者屏蔽密文便于开发过程中的调试。

    下面看下该拦截器的具体实现。

    /**
     * 实现自定义加解密拦截器
     */
    public abstract class ParseInterceptor implements Interceptor {
        @Override
        public Response intercept(Interceptor.Chain chain) throws IOException {
    
            // 加密请求
            Request encrypt = encrypt(chain.request());
    
            Response response = chain.proceed(encrypt);
    
            // 解密响应
            Response decrypt = decrypt(response);
            return decrypt;
        }
    
        /**
         * 加密
         */
        private Request encrypt(Request originalRequest) {
            try {
                // 1、原 请求体,获取请求体中的具体值
                RequestBody originalBody = originalRequest.body();
                Buffer buffer = new Buffer();
                originalBody.writeTo(buffer);
                String originalStr = buffer.readUtf8();
    
                // 2、实现具体的请求体几加密
                String encryptStr = encrypt(originalStr);
                RequestBody newRequestBody = RequestBody.create(originalBody.contentType(), encryptStr);
    
                // 3、返回新的请求携带新的请求体
                return originalRequest.newBuilder()
                        .method(originalRequest.method(), newRequestBody)
                        .build();
    
            } catch (Exception e) {
    
            }
            return originalRequest;
        }
    
        /**
         * 解密
         */
        private Response decrypt(Response originalResponse) {
            try {
    
                ResponseBody originalResponseBody = originalResponse.body();
                if (originalResponseBody == null || originalResponseBody.contentType() == null) {
                    return originalResponse;
                }
    
                String decrypt = decrypt(originalResponseBody.string());
    
                ResponseBody newResponseBody = ResponseBody.create(originalResponseBody.contentType(), decrypt);
    
                return originalResponse.newBuilder()
                        .body(newResponseBody)
                        .build();
            } catch (Exception e) {
    
            }
    
            return originalResponse;
    
        }
        
        /**
         * 加密请求
         * 省略具体加密细节,例如MD5、RSA、DES对字符串进行加密
         */
        public abstract String encrypt(String response);
    
        /**
         * 解密响应
         * 省略具体解密细节,例如MD5、RSA、DES对字符串进行解密
         */
        public abstract String decrypt(String response);
        
    }
    

    该拦截器的使用方式也和日志拦截器一样,这里就不重复了。

    Intercepteror工作流程

    通过上述两个自定义Interceptor,相信对Okhttp的Interceptor的功能有了基本的认识。

    现在,我们回到之前章节以及本章节开始提到的RealCall.getResponseWithInterceptorChain(),之前就提到过该方法就是用于获取网络响应的,其实该方法的作用远非如此简单。

    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());
    
            // 通过拦截器链的proceed方法运行整个拦截器
            return chain.proceed(originalRequest);
        }
    

    上面的getResponseWithInterceptorChain()方法,有两大作用:

    • 创建拦截器集合存放所有的拦截器,包括自定义拦截器和系统内部的五大拦截器。
    • 创建第一个拦截器链RealInterceptorChain实例,通过调用该方法的proceed(Request),让整个拦截器链运行起来。

    这个方法是拦截器工作的起点,通过在该方法中创建第一个RealInterceptorChain并通过proceed()方法,让拦截器集合中的所有拦截器将会按照顺序依次执行,最终获取网络响应。

    在上文中简述了拦截器及拦截器链的工作流程,下面就对拦截器及拦截器链做详细分析,内容如下:

    • 拦截器链Chain

      1. RealInterceptorChain
    • Okhttp3中的五大Interceptor

      1. RetryAndFollowUpInterceptor
      2. BridgeInterceptor
      3. CacheInterceptor
      4. ConnectInterceptor
      5. CallServerInterceptor

    这里先通过一张图展示拦截器的执行顺序(这里忽略了自定义拦截器):

    这里写图片描述

    Interceptor接口及Chain接口

    Interceptor接口比较简单,就定义了一个方法以及一个内部接口Chain,如下:

    public interface Interceptor {
      // 核心方法
      Response intercept(Chain chain) throws IOException;
    
      interface Chain {
        Request request();
        // 核心方法
        Response proceed(Request request) throws IOException;
    
        @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)。
    而对于Chain接口需要关注的方法也只有一个proceed(Request),这是让多个拦截器形成拦截器链工作的方法。

    RealInterceptorChain

    RealInterceptorChain是Interceptor.Chain接口的唯一实现类。

    所以这里需要看下该类的具体逻辑,这里我们只需要看下proceed(Request)方法即可,这个方法的核心代码就三行,这也是让拦截器以链的形式依次执行的关键代码,如下:

    public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    
        // 省略部分代码
        
            /**
             * 1、创建一个新的RealInterceptorChain,用于处理下一个Interceptor
             *  之所以是下一个这个通过(index+1)可知,每次调用依次都是在index基础上加1,这就可以构成一个拦截器链依次执行。
             这有点像递归的实现流程。
             */
            RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
                    connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
                    writeTimeout);
            /**
             * 2、获取当前RealInterceptorChain所需要处理的Interceptor
             *    
             *    第一次执行就是获取index为0的Interceptor,这个通过RealCall.getResponseWithInterceptorChain()方法中
             *    RealInterceptorChain实例的创建可知
             */
            Interceptor interceptor = interceptors.get(index);
            /**
             * 3、调用Interceptor的intercept(Chain)方法,这里会传入RealInterceptorChain,用户处理下一个Interceptor
             * 
             *  要让Interceptor完整的执行完成,在Interceptor中必须执行next.proceed(Request)
             */
            Response response = interceptor.intercept(next);
        // 省略部分代码
    
    return response;
    }
    

    五大内置Interceptor

    okhttp中五大Interceptor各自分工明确,依次执行,整个网络请求的过程从发起请求到最终获取响应都是在这五大Interceptor中完成的。

    1. RetryAndFollowUpInterceptor:重试拦截器,用于失败重连
    2. BridgeInterceptor:桥接拦截器,主要初始化请求头
    3. CacheInterceptor:本地缓存拦截器
    4. ConnectInterceptor:连接拦截器
    5. CallServerInterceptor:服务器交互拦截器

    在对这五大拦截器做详细分析时,只针对拦截器的intercept()方法的功能出发,如果涉及到一些其他类及方法只做一个说明,不会做深入分析。

    RetryAndFollowUpInterceptor

    该拦截器的功能如下:

    1. 创建一个StreamAllocation对象,这个会在后面的ConnectInterceptor使用到
    2. 调用RealInterceptorChain.proceed()方法获取Response
    3. 根据Response状态码判断是否需要重连
    4. 将Response返回给上一个拦截器

    intercept方法

        @Override 
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            RealInterceptorChain realChain = (RealInterceptorChain) chain;
            Call call = realChain.call();
            EventListener eventListener = realChain.eventListener();
    
            // StreamAllocation用于获取服务端的连接,并获取服务端的输入输出流,在后面的ConnectInterceptor拦截器中会使用到
            StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
                    createAddress(request.url()), call, eventListener, callStackTrace);
            this.streamAllocation = streamAllocation;
    
            while (true) { // 这是一个死循环,实现请求重试
    
                Response response;
                boolean releaseConnection = true;
                try {
                    // 获取网络响应
                    response = realChain.proceed(request, streamAllocation, null, null);
                    releaseConnection = false;
                }
    
                // followUpRequest方法根据获取响应的响应码,判断是否需要重连
                Request followUp = followUpRequest(response, streamAllocation.route());
    
                
                if (followUp == null) {
                    if (!forWebSocket) {
                        streamAllocation.release();
                    }
                    // 获取正常网络请求时,会在这里返回响应,这个网络请求流程结束
                    return response;
                }
    
                // 当重现次数大于MAX_FOLLOW_UPS(默认20),就会结束循环,不在重连
                if (++followUpCount > MAX_FOLLOW_UPS) {
                    streamAllocation.release();
                    throw new ProtocolException("Too many follow-up requests: " + followUpCount);
                }
            }
        }
    
    

    BridgeInterceptor

    该拦截器的功能如下:

    1. 为Request实例添加头部信息,将该Request构造成一个标准的能够被服务器识别的请求。
    2. 调用RealInterceptorChain.proceed()方法获取Response
    3. 如果Response支持GZIP,那么会对该Response进行处理
    4. 将Response返回给上一个拦截器

    这个拦截器功能比较简单这里就不针对代码做说明了。

    CacheInterceptor

    该拦截器的功能如下:

    1. 根据请求获取缓存响应结果
    2. 调用RealInterceptorChain.proceed()方法获取Response
    3. 将从网路上获取的Response缓存到本地
    4. 将Response返回给上一个拦截器

    Okhttp中缓存策略主要使用DiskLruCache。

    设置缓存

    在使用okhttp访问网络请求时,需要做如下配置:

    设置缓存路径及大小

    
    OkHttpClient client = new OkHttpClient.Builder()
            .cache(new Cache(new File("cacheDir"), 1024*1024*10))
            .build();
    
    

    设置Request请求缓存策略:

    Request request = new Request.Builder()
            .cacheControl(CacheControl.FORCE_CACHE)// 从缓存中获取
            .cacheControl(CacheControl.FORCE_NETWORK)// 从网络中获取
            .url("url")
            .build();
    

    CacheControl.FORCE_CACHE和CacheControl.FORCE_NETWORK分别表示从缓存中获取和从网络中获取。

    同时在CacheInterceptor中需要注意的时:okhttp只会将GET请求的响应缓存到本地。
    这个可以查看Cache.put方法(这个方法最终会在intercept()方法中被调用):

      @Nullable CacheRequest put(Response response) {
        String requestMethod = response.request().method();
    
        // 省略部分代码
        
        if (!requestMethod.equals("GET")) {
          // Don't cache non-GET responses. We're technically allowed to cache
          // HEAD requests and some POST requests, but the complexity of doing
          // so is high and the benefit is low.
          return null;
        }
         // 省略部分代码
    }
    

    intercept方法

    @Override
    public Response intercept(Interceptor.Chain chain) throws IOException {
            // 1、首先获取该Request的缓存响应,以作为备用(不管该request是否需要使用响应)
            Response cacheCandidate = cache != null
                    ? cache.get(chain.request())
                    : null;
    
            long now = System.currentTimeMillis();
    
            // 3、创建一个缓存策略,决定是否使用网络响应还是缓存响应
            // CacheStrategy中涉及到多种情况下关于是否使用缓存的判断,这里就不多描述了
            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.
            }
    
            // 被禁止从网络上获取数据,同时没有获取到缓存,这个时候创建一个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 {
                // 调用下一个拦截器获取响应
                networkResponse = chain.proceed(networkRequest);
            } 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) {
                    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();
                    
                    // 更新缓存数据
                    cache.trackConditionalCacheHit();
                    cache.update(cacheResponse, response);
                    return response;
                } else {
                    closeQuietly(cacheResponse.body());
                }
            }
    
            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.
                    // 将网络响应缓存到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;
    }
    

    ConnectInterceptor

    该拦截器的功能:

    1. 初始化网络连接的RealConnection对象,以及对网络请求做编解码的HttpCodec对象
    2. 调用RealInterceptorChain.proceed方法获取

    总结下来就一句话,打开一个到目标服务器的连接。

    其实这个连接器要处理的逻辑特别多,包括连接池ConnectionPool的一些操作,这里只针对拦截器

    intercept方法

    @Override
    public Response intercept(Chain chain) throws IOException {
            RealInterceptorChain realChain = (RealInterceptorChain) chain;
            Request request = realChain.request();
            // 1、获取streamAllocation,这个streamAllocation是在RetryAndFollowUpInterceptor初始化的
            StreamAllocation streamAllocation = realChain.streamAllocation();
    
            // We need the network to satisfy this request. Possibly for validating a conditional GET.
            boolean doExtensiveHealthChecks = !request.method().equals("GET");
            // 2、创建HttpCodec对象,该对象的主要作用就是编解码请求响应
            HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
            // 3、获取RealConnection对象,该对象的主要作用就是实现网络IO流输入输出的,这个对象在newStream()方法中初始化
            RealConnection connection = streamAllocation.connection();
            // 4、调用RealInterceptorChain方法获取响应
            return realChain.proceed(request, streamAllocation, httpCodec, connection);
    }
    

    这里我们看下通过streamAllocation.newStream()方法创建HttpCodec对象的具体细节,如下:

        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 {
                // 1、通过findHealthyConnection()方法获取一个RealConnection对象
                RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
                        writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
                // 2、创建HttpCodec对象并返回
                HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
    
                synchronized (connectionPool) {
                    codec = resultCodec;
                    return resultCodec;
                }
            } catch (IOException e) {
                throw new RouteException(e);
            }
        }
    
    
        /**
         * 寻找一个健康的连接
         */
        private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
                                                     int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled,
                                                     boolean doExtensiveHealthChecks) throws IOException {
    
            // 3、通过一个死循环,直到找到一个连接才跳出该循环
            while (true) {
                //4、通过findConnection创建一个连接
                RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
                        pingIntervalMillis, connectionRetryEnabled);
    
                // If this is a brand new connection, we can skip the extensive health checks.
                synchronized (connectionPool) {
                    if (candidate.successCount == 0) {
                        return candidate;
                    }
                }
    
                // 5、判断如果连接不是健康的就继续寻找下一个连接
                if (!candidate.isHealthy(doExtensiveHealthChecks)) {
                    noNewStreams();
                    continue;
                }
    
                return candidate;
            }
        }
    
        /**
         * Returns a connection to host a new stream. This prefers the existing connection if it exists,
         * then the pool, finally building a new connection.
         */
        private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
                                              int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
    
            // 省略部分代码
    
            synchronized (connectionPool) {
    
                if (result == null) {
                    // 6、从连接池中获取一个连接
                    Internal.instance.get(connectionPool, address, this, null);
                    if (connection != null) {
                        foundPooledConnection = true;
                        result = connection;
                    } else {
                        selectedRoute = route;
                    }
                }
            }
            // 省略部分代码
    
    
            // 开始执行一个连接
            // Do TCP + TLS handshakes. This is a blocking operation.
            result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
                    connectionRetryEnabled, call, eventListener);
            routeDatabase().connected(result.route());
    
            Socket socket = null;
            synchronized (connectionPool) {
                reportedAcquired = true;
    
                // 缓存连接
                Internal.instance.put(connectionPool, result);
    
                // If another multiplexed connection to the same address was created concurrently, then
                // release this connection and acquire that one.
                if (result.isMultiplexed()) {
                    socket = Internal.instance.deduplicate(connectionPool, address, this);
                    result = connection;
                }
            }
            closeQuietly(socket);
    
            eventListener.connectionAcquired(call, result);
            return result;
    }
    

    CallServerInterceptor

    该拦截器的功能如下:

    1. 向服务器发起一个Request
    2. 从服务器获取一个Response,并返回Response

    HttpCodec这个类非常关键,在该拦截器中所有的流程都是基于该类。详细流程如下:

    1. HttpCodec向socket写入请求头信息
    2. HttpCodec向socket写入请求体信息
    3. httpCodec调用finishRequest()方法,表示一个请求发送完成
    4. HttpCodec读取响应头信息
    5. HttpCodec读取请求体信息

    intercept方法

    @Override
    public Response intercept(Interceptor.Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        // httpCodec:编码请求,解码响应,输入输出流
        HttpCodec httpCodec = realChain.httpStream();
        // 用于分配网络所需要的资源
        StreamAllocation streamAllocation = realChain.streamAllocation();
        // 网络连接
        RealConnection connection = (RealConnection) realChain.connection();
        // 网络请求
        Request request = realChain.request();
    
        long sentRequestMillis = System.currentTimeMillis();
    
        realChain.eventListener().requestHeadersStart(realChain.call());
        // 1、向socket写入请求头信息
        httpCodec.writeRequestHeaders(request);
        realChain.eventListener().requestHeadersEnd(realChain.call(), request);
    
        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.
    
            // 于客户端在发送 post 数据给服务器时,征询服务器情况,看服务器是否处理 post 的数据,如果不处理,客户端则不上传 post 是数据,反之则上传。
            // 在实际应用中,通过 post 上传大数据时,才会使用到 100-continue 协议
            if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
                httpCodec.flushRequest();
                realChain.eventListener().responseHeadersStart(realChain.call());
                responseBuilder = httpCodec.readResponseHeaders(true);
            }
    
            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();
                CallServerInterceptor.CountingSink requestBodyOut =
                        new CallServerInterceptor.CountingSink(httpCodec.createRequestBody(request, contentLength));
                BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
                // 2、向socket写入请求体信息
                request.body().writeTo(bufferedRequestBody);
                bufferedRequestBody.close();
                realChain.eventListener()
                        .requestBodyEnd(realChain.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();
            }
        }
    
        // 结束写入请求信息
        httpCodec.finishRequest();
    
        // 开始读取响应
        if (responseBuilder == null) {
            realChain.eventListener().responseHeadersStart(realChain.call());
            // 读取响应头信息
            responseBuilder = httpCodec.readResponseHeaders(false);
        }
    
        // 初始化响应配置
        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) {
            // 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(httpCodec.openResponseBody(response))
                    .build();
        }
    
        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;
    }
    
    

    总结

    这样,关于Okhttp中五大内置拦截器的功能就分析到这里了。
    由于篇幅问题,本章节只是对这些拦截器的工作原理做了简单的分析,其中还有一些重要的内容没有提及到,包括:

    • Cache及DiskLruCahce如何实现okhttp中的缓存机制
    • StreamAllocation的工作原理
    • 连接池ConnectionPool的工作原理
    • HttpCodec的底层实现,如何通过Socket同服务器交互

    这些内容将会在以后进行补充。

    相关文章

      网友评论

        本文标题:Okhttp3源码分析(3) Interceptor拦截器

        本文链接:https://www.haomeiwen.com/subject/mkosjftx.html