美文网首页
okhttp源码解析(二) 拦截器分析

okhttp源码解析(二) 拦截器分析

作者: digtal_ | 来源:发表于2020-03-15 16:39 被阅读0次

    前言

    上篇我们介绍了okhttp整体的流程执行,本篇来具体分析每个拦截器的执行,其中CacheInterceptor和ConnectInterceptor是里面的核心也是比较难的点。

    • 1.RetryAndFollowUpInterceptor,负责失败重连以及重定向
    @Override public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        Call call = realChain.call();
        EventListener eventListener = realChain.eventListener();
        //1.StreamAllocation
        StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
            createAddress(request.url()), call, eventListener, callStackTrace);
        this.streamAllocation = streamAllocation;
    
        int followUpCount = 0;
        Response priorResponse = null;
        //2.开启无限循环
        while (true) {
          if (canceled) {
            streamAllocation.release();
            throw new IOException("Canceled");
          }
    
          Response response;
          boolean releaseConnection = true;
          try {
    
            // 3.执行下一个拦截器,即BridgeInterceptor会将初始化好的连接对象传递给下一个拦截器
            response = realChain.proceed(request, streamAllocation, null, null);
            releaseConnection = false;
          } catch (RouteException e) {
            // The attempt to connect via a route failed. The request will not have been sent.
        //  4.如果有异常,判断是否要恢复
            if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
              throw e.getLastConnectException();
            }
            releaseConnection = false;
            continue;
          } catch (IOException e) {
            // An attempt to communicate with a server failed. The request may have been sent.
            boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
            if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
            releaseConnection = false;
            continue;
          } finally {
            // We're throwing an unchecked exception. Release any resources.
            if (releaseConnection) {
              streamAllocation.streamFailed(null);
              streamAllocation.release();
            }
          }
    
          //   5.priorResponse 是否为null
          if (priorResponse != null) {
            response = response.newBuilder()
                .priorResponse(priorResponse.newBuilder()
                        .body(null)
                        .build())
                .build();
          }
    //   6.重定向
          Request followUp = followUpRequest(response, streamAllocation.route());
    
          if (followUp == null) {
            if (!forWebSocket) {
              streamAllocation.release();
            }
            return response;
          }
    
          closeQuietly(response.body());
    
          if (++followUpCount > MAX_FOLLOW_UPS) {
            streamAllocation.release();
            throw new ProtocolException("Too many follow-up requests: " + followUpCount);
          }
    
          if (followUp.body() instanceof UnrepeatableRequestBody) {
            streamAllocation.release();
            throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
          }
    //7.检查是否有相同的链接,是:释放,重建创建
          if (!sameConnection(response, followUp.url())) {
            streamAllocation.release();
            streamAllocation = new StreamAllocation(client.connectionPool(),
                createAddress(followUp.url()), call, eventListener, callStackTrace);
            this.streamAllocation = streamAllocation;
          } else if (streamAllocation.codec() != null) {
            throw new IllegalStateException("Closing the body of " + response
                + " didn't close its backing stream. Bad interceptor?");
          }
    
          request = followUp;
          priorResponse = response;
        }
      }
    
    • 1.注释1处创建了一个StreamAllocation对象,这个对象很重要在建立连接网络的时候使用的,这里只是初始化用于传入后面的拦截器使用的,后面会讲到,这里根据url创建一个Address对象,初始化一个Socket连接对象,基于Okio用于构造StreamAllocation对象。
    • 2.开启了一个无限循环,如果取消streamAllocation释放再抛出异常
    • 3.是把request和streamAllocation传给了下一个拦截器及BridgeInterceptor
    • 4.如果有异常,判断是否要恢复
    • 5.如果priorResponse不为空,则说明前面已经获取到了响应,这里会结合当前获取的Response和先前的Response
    • 6.检查是否需要重定向,如果不需要重定向则返回当前请求,这里主要是根据响应码(code)和响应头(header),查看是否需要重定向
    • 7.检查是否有相同的链接,是就释放重建创建
    • 2.BridgeInterceptor 负责对Request和Response处理
    @Override public Response intercept(Chain chain) throws IOException {
       Request userRequest = chain.request();
       Request.Builder requestBuilder = userRequest.newBuilder();
       //1。request
       RequestBody body = userRequest.body();
       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");
       }
    
       // 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()) {
         requestBuilder.header("Cookie", cookieHeader(cookies));
       }
    
       if (userRequest.header("User-Agent") == null) {
         requestBuilder.header("User-Agent", "3.10");
       }
    
       Response networkResponse = chain.proceed(requestBuilder.build());
    
       HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
    
       Response.Builder responseBuilder = networkResponse.newBuilder()
           .request(userRequest);
    
       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();
     }
    
    • 这个拦截器比较简单主要负责request请求头的封装,cookie,gizp等,以及response响应的一些处理,就不再多说了。
    • 3.CacheInterceptor负责对response缓存的一些处理
    @Override public Response intercept(Chain chain) throws IOException {
        //1.获取缓存的response
        Response cacheCandidate = cache != null
            ? cache.get(chain.request())
            : null;
    
        long now = System.currentTimeMillis();
        //2.缓存策略
        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.
        }
    
        //  3.返回code=504
        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.
        //  4.从缓存里拿response
        if (networkRequest == null) {
          return cacheResponse.newBuilder()
              .cacheResponse(stripBody(cacheResponse))
              .build();
        }
    
        Response networkResponse = null;
        try {
          //  5.调用下一个拦截器去请求response
          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());
          }
        }
    
        //  6.根据条件判断是用cacheResponse还是networkResponse
        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());
          }
        }
        //  7.使用networkResponse
        Response response = networkResponse.newBuilder()
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
        //  8.存入缓存
        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,这里面缓存用到的是DiskLruCache来进行存储
    • 2.根据缓存策略获取networkRequest和cacheResponse

     /**
         * Returns a strategy to satisfy {@code request} using the a cached response {@code response}.
         */
        public CacheStrategy get() {
          CacheStrategy candidate = getCandidate();
          if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
            return new CacheStrategy(null, null);
          }
          return candidate;
        }
    
    • 这里调用get方法来获取缓存策略,而这个方法主要通过getCandidate()来获取,我们看下这个方法
    private CacheStrategy getCandidate() {
          // No cached response.
          //1.没有缓存
          if (cacheResponse == null) {
            return new CacheStrategy(request, null);
          }
        //2.三次握手失效
          if (request.isHttps() && cacheResponse.handshake() == null) {
            return new CacheStrategy(request, null);
          }
        //3 响应不能被缓存
          if (!isCacheable(cacheResponse, request)) {
            return new CacheStrategy(request, null);
          }
    
          CacheControl requestCaching = request.cacheControl();
          if (requestCaching.noCache() || hasConditions(request)) {
            return new CacheStrategy(request, null);
          }
    
          CacheControl responseCaching = cacheResponse.cacheControl();
          if (responseCaching.immutable()) {
            return new CacheStrategy(null, cacheResponse);
          }
    
          long ageMillis = cacheResponseAge();
          long freshMillis = computeFreshnessLifetime();
    
          if (requestCaching.maxAgeSeconds() != -1) {
            freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds()));
          }
    
          long minFreshMillis = 0;
          if (requestCaching.minFreshSeconds() != -1) {
            minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds());
          }
    
          long maxStaleMillis = 0;
          if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) {
            maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds());
          }
    
          if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
            Response.Builder builder = cacheResponse.newBuilder();
            if (ageMillis + minFreshMillis >= freshMillis) {
              builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"");
            }
            long oneDayMillis = 24 * 60 * 60 * 1000L;
            if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
              builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
            }
            return new CacheStrategy(null, builder.build());
          }
          // Find a condition to add to the request. If the condition is satisfied, the response body
          // will not be transmitted.
          String conditionName;
          String conditionValue;
          if (etag != null) {
            conditionName = "If-None-Match";
            conditionValue = etag;
          } else if (lastModified != null) {
            conditionName = "If-Modified-Since";
            conditionValue = lastModifiedString;
          } else if (servedDate != null) {
            conditionName = "If-Modified-Since";
            conditionValue = servedDateString;
          } else {
            return new CacheStrategy(request, null); // No condition! Make a regular request.
          }
    
          Headers.Builder conditionalRequestHeaders = request.headers().newBuilder();
          Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue);
    
          Request conditionalRequest = request.newBuilder()
              .headers(conditionalRequestHeaders.build())
              .build();
          return new CacheStrategy(conditionalRequest, cacheResponse);
        }
    
    • 没有缓存就换返回一个response为null的CacheStrategy,代表没有缓存,返回之后就会执行下一个拦截器去请求网络获取response。
    • 如果是https并且三次握手失效返回和1一样。
    • 代表响应不能被缓存
    • 之后就是根据CacheControl来控制返回的结果,用大量的if/else判断缓存是否失效

    • 3.回到我们CacheInterceptor的intercept方法的注释3,缓存失效且onlyIfCached为ture就执行这返回504.
    • 4.根据我们配置的CacheControl如果设置了ForceCache或者Max-age代表缓存未失效,则就会执行这,不去请求网络直接使用缓存。
    • 5.缓存失效,调用后面的拦截器获取Response。
    • 6.网络获取的networkResponse与cacheResponse做对比来判断使用哪个response,code==304代表缓存有效,则使用缓存并通过cache.update方法更新缓存。
    • 7.缓存过期了则使用networkResponse并存入缓存

    • 这里再说下DiskLruCache,用来做缓存的类
        //用于缓存的集合
        final LinkedHashMap<String, Entry> lruEntries = new LinkedHashMap<>(0, 0.75f, true);
        //用于清理缓存的线程池
        private final Executor executor;
        //用于清理缓存的任务
        private final Runnable cleanupRunnable = new Runnable() {
            public void run() {
                synchronized (DiskLruCache.this) {
                    if (!initialized | closed) {
                        return; // Nothing to do
                    }
                    try {
                        trimToSize();
                    } catch (IOException ignored) {
                        mostRecentTrimFailed = true;
                    }
    
                    try {
                        if (journalRebuildRequired()) {
                            rebuildJournal();
                            redundantOpCount = 0;
                        }
                    } catch (IOException e) {
                        mostRecentRebuildFailed = true;
                        journalWriter = Okio.buffer(Okio.blackhole());
                    }
                }
            }
        };
    
    • 用lruEntries 也就是lru算法执行删除操作,通过BufferedSource以及BufferedSink进行读写操作大大提升了效率。

    • 运用线程池去执行缓存的清理的任务

    • 每一个url请求cache有四个文件,两个状态(DIRY,CLEAN),每个状态对应两个文件:一个0文件对应存储meta数据,一个文件存储body数据。

    • 这里面的逻辑很复杂有兴趣的可以去具体去了解下。

    • CacheInterceptor核心还是http协议缓存知识来配置我们的CacheControl来控制缓存的策略。

    • 4.ConnectInterceptor,向服务器发起连接
      @Override public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        Request request = realChain.request();
        //1.得到streamAllocation 
        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.得到connection 
        RealConnection connection = streamAllocation.connection();
        return realChain.proceed(request, streamAllocation, httpCodec, connection);
      }
    
    • 1.得到streamAllocation对象 也就是RetryAndFollowUpInterceptor中初始化的在这里使用到了
    • 2.通过streamAllocation.newStream得到了httpCodec

      private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
          int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled,
          boolean doExtensiveHealthChecks) throws IOException {
        while (true) {
          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;
            }
          }
          if (!candidate.isHealthy(doExtensiveHealthChecks)) {
            noNewStreams();
            continue;
          }
          return candidate;
        }
      }
    
    
     private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
          int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
      //.........
    
          if (newRouteSelection) {
            // Now that we have a set of IP addresses, make another attempt at getting a connection from
            // the pool. This could match due to connection coalescing.
            List<Route> routes = routeSelection.getAll();
            for (int i = 0, size = routes.size(); i < size; i++) {
              Route route = routes.get(i);
              Internal.instance.get(connectionPool, address, this, route);
              if (connection != null) {
                foundPooledConnection = true;
                result = connection;
                this.route = route;
                break;
              }
            }
          }
    
          if (!foundPooledConnection) {
            if (selectedRoute == null) {
              selectedRoute = routeSelection.next();
            }
    
            // Create a connection and assign it to this allocation immediately. This makes it possible
            // for an asynchronous cancel() to interrupt the handshake we're about to do.
            route = selectedRoute;
            refusedStreamCount = 0;
            result = new RealConnection(connectionPool, selectedRoute);
            acquire(result, false);
          }
        }
    
        // If we found a pooled connection on the 2nd time around, we're done.
        if (foundPooledConnection) {
          eventListener.connectionAcquired(call, result);
          return result;
        }
    
        // 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;
    
          // Pool the connection.
          Internal.instance.put(connectionPool, result);
    
      //.........
        return result;
      }
    
    • StreamAllocation的newStream()内部其实是通过findHealthyConnection()方法获取一个RealConnection,而在findHealthyConnection()里面通过一个while(true)死循环不断去调用findConnection()方法去再ConnectionPool中找RealConnection,找不到则直接new一个RealConnection。然后开始握手,握手结束后,把连接加入连接池,如果在连接池有重复连接,和合并连接。
    public void connect(int connectTimeout, int readTimeout, int writeTimeout,
          int pingIntervalMillis, boolean connectionRetryEnabled, Call call,
          EventListener eventListener) {
     //........
    
        while (true) {
          try {
            if (route.requiresTunnel()) {
              connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener);
              if (rawSocket == null) {
                // We were unable to connect the tunnel but properly closed down our resources.
                break;
              }
            } else {
              connectSocket(connectTimeout, readTimeout, call, eventListener);
            }
            establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener);
            eventListener.connectEnd(call, route.socketAddress(), route.proxy(), protocol);
            break;
          } catch (IOException e) {
        //...
    
      private void connectTunnel(int connectTimeout, int readTimeout, int writeTimeout, Call call,
          EventListener eventListener) throws IOException {
        Request tunnelRequest = createTunnelRequest();
        HttpUrl url = tunnelRequest.url();
        for (int i = 0; i < MAX_TUNNEL_ATTEMPTS; i++) {
          connectSocket(connectTimeout, readTimeout, call, eventListener);
          tunnelRequest = createTunnel(readTimeout, writeTimeout, tunnelRequest, url);
    
          if (tunnelRequest == null) break; // Tunnel successfully created.
    
          // The proxy decided to close the connection after an auth challenge. We need to create a new
          // connection, but this time with the auth credentials.
          closeQuietly(rawSocket);
          rawSocket = null;
          sink = null;
          source = null;
          eventListener.connectEnd(call, route.socketAddress(), route.proxy(), null);
        }
      }
    
      private void connectSocket(int connectTimeout, int readTimeout, Call call,
          EventListener eventListener) throws IOException {
        Proxy proxy = route.proxy();
        Address address = route.address();
    
        rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
            ? address.socketFactory().createSocket()
            : new Socket(proxy);
    
        eventListener.connectStart(call, route.socketAddress(), proxy);
        rawSocket.setSoTimeout(readTimeout);
        try {
          Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
        } catch (ConnectException e) {
          ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
          ce.initCause(e);
          throw ce;
        }
        try {
          source = Okio.buffer(Okio.source(rawSocket));
          sink = Okio.buffer(Okio.sink(rawSocket));
        } catch (NullPointerException npe) {
          if (NPE_THROW_WITH_NULL.equals(npe.getMessage())) {
            throw new IOException(npe);
          }
        }
      }
    
      private void establishProtocol(ConnectionSpecSelector connectionSpecSelector,
          int pingIntervalMillis, Call call, EventListener eventListener) throws IOException {
        if (route.address().sslSocketFactory() == null) {
          protocol = Protocol.HTTP_1_1;
          socket = rawSocket;
          return;
        }
    
        eventListener.secureConnectStart(call);
        connectTls(connectionSpecSelector);
        eventListener.secureConnectEnd(call, handshake);
    
        if (protocol == Protocol.HTTP_2) {
          socket.setSoTimeout(0); // HTTP/2 connection timeouts are set per-stream.
          http2Connection = new Http2Connection.Builder(true)
              .socket(socket, route.address().url().host(), source, sink)
              .listener(this)
              .pingIntervalMillis(pingIntervalMillis)
              .build();
          http2Connection.start();
        }
      }
    
    • RealConnection的connect()这个方法很重要。RealConnection的connect()是StreamAllocation调用的。在RealConnection的connect()的方法里面也是一个while(true)的循环,里面判断是隧道连接还是普通连接,如果是隧道连接就走connectTunnel(),如果是普通连接则走connectSocket(),最后建立协议。connectSocket()里面就是通过okio获取source与sink。establishProtocol()方法建立连接,里面判断是是HTTP/1.1还是HTTP/2.0。如果是HTTP/2.0则通过Builder来创建一个Http2Connection对象,并且调用Http2Connection对象的start()方法。所以判断一个RealConnection是否是HTTP/2.0其实很简单,判断RealConnection对象的http2Connection属性是否为null即可,因为只有HTTP/2的时候http2Connection才会被赋值。
    • connectSocket()具体实现是AndroidPlatform.java里面的connectSocket(),通过socket建立连接

    • 3.通过streamAllocation得到connection,最后再传入到下一个拦截器

    • 5.CallServerInterceptor,负责向服务器发送请求以及接受请求
     @Override public Response intercept(Chain chain) throws IOException {
       RealInterceptorChain realChain = (RealInterceptorChain) chain;
       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.通过httpCodec写入请求头
       httpCodec.writeRequestHeaders(request);
       realChain.eventListener().requestHeadersEnd(realChain.call(), request);
    
       Response.Builder responseBuilder = null;
       if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      
         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.
        //2.写入请求体
           realChain.eventListener().requestBodyStart(realChain.call());
           long contentLength = request.body().contentLength();
           CountingSink requestBodyOut =
               new CountingSink(httpCodec.createRequestBody(request, contentLength));
           BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
    
           request.body().writeTo(bufferedRequestBody);
           bufferedRequestBody.close();
           realChain.eventListener()
               .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
         } else if (!connection.isMultiplexed()) {
           streamAllocation.noNewStreams();
         }
       }
    
       httpCodec.finishRequest();
    //3.读响应头
       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) {
         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);
    //3.读响应体
       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();
       }
    
    //.....
       return response;
     }
    
    • 该拦截器比较简单,看代码注释就能懂,就是发送请求头请求体,然后读取响应头及响应体。


      5713484-a89f541f0f7797a6.png
    • 感谢 https://www.jianshu.com/p/6166d28983a2

    相关文章

      网友评论

          本文标题:okhttp源码解析(二) 拦截器分析

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