Network architecture-cache interceptor of okhttp interceptor

Network architecture-cache interceptor of okhttp interceptor

CacheInterceptor (Cache Interceptor)

public final class CacheInterceptor implements Interceptor {
    final InternalCache cache;

    public CacheInterceptor(InternalCache cache) {
        this.cache = cache;
    }

/**
* 
*/
    @Override
    public Response intercept(Chain chain) throws IOException {
       //todo  url md5     GET 
        Response cacheCandidate = cache != null
                ? cache.get(chain.request())
                : null;

        long now = System.currentTimeMillis();

       //todo  : ( )   
        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.
        }

       //todo  
       //If we're forbidden from using the network and the cache is insufficient, fail.
        if (networkRequest == null && cacheResponse == null) {
            return new Response.Builder()
                    .request(chain.request())
                    .protocol(Protocol.HTTP_1_1)
                    .code(504)
                    .message("Unsatisfiable Request (only-if-cached)")
                    .body(Util.EMPTY_RESPONSE)
                    .sentRequestAtMillis(-1L)
                    .receivedResponseAtMillis(System.currentTimeMillis())
                    .build();
        }

       //todo  
       //If we don't need the network, we're done.
        if (networkRequest == null) {
            return cacheResponse.newBuilder()
                    .cacheResponse(stripBody(cacheResponse))
                    .build();
        }

       //todo  
        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) {
           //todo  304 
            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();

               //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());
            }
        }

       //todo    
        Response response = networkResponse.newBuilder()
                .cacheResponse(stripBody(cacheResponse))
                .networkResponse(stripBody(networkResponse))
                .build();
       //todo  
        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;
    }

 

CacheStrategy cache strategy factory

public Factory(long nowMillis, Request request, Response cacheResponse) {
            this.nowMillis = nowMillis;
            this.request = request;
            this.cacheResponse = cacheResponse;

            if (cacheResponse != null) {
                this.sentRequestMillis = cacheResponse.sentRequestAtMillis();
                this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis();
                Headers headers = cacheResponse.headers();
                for (int i = 0, size = headers.size(); i < size; i++) {
                    String fieldName = headers.name(i);
                    String value = headers.value(i);
                    if ("Date".equalsIgnoreCase(fieldName)) {
                        servedDate = HttpDate.parse(value);
                        servedDateString = value;
                    } else if ("Expires".equalsIgnoreCase(fieldName)) {
                        expires = HttpDate.parse(value);
                    } else if ("Last-Modified".equalsIgnoreCase(fieldName)) {
                        lastModified = HttpDate.parse(value);
                        lastModifiedString = value;
                    } else if ("ETag".equalsIgnoreCase(fieldName)) {
                        etag = value;
                    } else if ("Age".equalsIgnoreCase(fieldName)) {
                        ageSeconds = HttpHeaders.parseSeconds(value, -1);
                    }
                }
            }
        }

       /**
         * Returns a strategy to satisfy {@code request} using the a cached response {@code
         * response}.
         */
        public CacheStrategy get() {
            CacheStrategy candidate = getCandidate();
           //todo  networkRequest null networkRequest null gg( 504)
            if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
               //We're forbidden from using the network and the cache is insufficient.
                return new CacheStrategy(null, null);
            }

            return candidate;
        }

       /**
         * Returns a strategy to use assuming the request can use the network.
         */
        private CacheStrategy getCandidate() {
           //No cached response.
           //todo 1 , 
            if (cacheResponse == null) {
                return new CacheStrategy(request, null);
            }
           //todo 2 https , 
           //Drop the cached response if it's missing a required handshake.
            if (request.isHttps() && cacheResponse.handshake() == null) {
                return new CacheStrategy(request, null);
            }

           //todo 3 
           //If this response shouldn't have been stored, it should never be used
           //as a response source. This check should be redundant as long as the
           //persistence store is well-behaved and the rules are constant.
            if (!isCacheable(cacheResponse, request)) {
                return new CacheStrategy(request, null);
            }

            CacheControl requestCaching = request.cacheControl();
           //todo 4   CacheControl:no-cache  
           //  If-Modified-Since   lastModified data  304( )
           //  If-None-Match Etag Etag 304
           // 
            if (requestCaching.noCache() || hasConditions(request)) {
                return new CacheStrategy(request, null);
            }

           //todo 5  Cache-Control:immutable  , 
            CacheControl responseCaching = cacheResponse.cacheControl();
            if (responseCaching.immutable()) {
                return new CacheStrategy(null, cacheResponse);
            }

           //todo 6       
           //6.1 
            long ageMillis = cacheResponseAge();
           //todo
           //6.2 
            long freshMillis = computeFreshnessLifetime();
            if (requestCaching.maxAgeSeconds() != -1) {
               //todo   max-age  
                freshMillis = Math.min(freshMillis,
                        SECONDS.toMillis(requestCaching.maxAgeSeconds()));
            }
           //todo
           //6.3    Cache-Control:min-fresh=[ ]     
            long minFreshMillis = 0;
            if (requestCaching.minFreshSeconds() != -1) {
                minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds());
            }

           //todo
           //6.4
           // 6.4.1 Cache-Control:must-revalidate  
           // 6.4.2 Cache-Control:max-stale=[ ]     
           // max-stale
            long maxStaleMillis = 0;
            if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) {
                maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds());
            }

           //todo
           //6.5   &&  +     + 
           // 
            if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
                Response.Builder builder = cacheResponse.newBuilder();
               //todo    
                if (ageMillis + minFreshMillis >= freshMillis) {
                    builder.addHeader("Warning", "110 HttpURLConnection/"Response is stale\"");
                }
               //todo  
                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.
           //todo 7 
            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.
            }
           //todo   If-None-Match/If-Modified-Since  304( ) , 
            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);
        }

       /**
         * Returns the number of milliseconds that the response was fresh for, starting from the
         * served
         * date.
         */
        private long computeFreshnessLifetime() {
            CacheControl responseCaching = cacheResponse.cacheControl();
           /**
             * todo
             *  max-age/
             *  expires: ( , )
             *     Data( )  expires-data 
             *     Data   expires-    
             *  Last-Modified:     
             *     Data( )  data-lastModified  
             *     Data    -lastModified   
             */
            if (responseCaching.maxAgeSeconds() != -1) {
                return SECONDS.toMillis(responseCaching.maxAgeSeconds());
            } else if (expires != null) {
                long servedMillis = servedDate != null
                        ? servedDate.getTime()
                        : receivedResponseMillis;
                long delta = expires.getTime() - servedMillis;
                return delta > 0 ? delta : 0;
            } else if (lastModified != null
                    && cacheResponse.request().url().query() == null) {
               //As recommended by the HTTP RFC and implemented in Firefox, the
               //max age of a document should be defaulted to 10% of the
               //document's age at the time it was served. Default expiration
               //dates aren't used for URIs containing a query.
                long servedMillis = servedDate != null
                        ? servedDate.getTime()
                        : sentRequestMillis;
                long delta = servedMillis - lastModified.getTime();
                return delta > 0 ? (delta/10) : 0;
            }
            return 0;
        }

       /**
         * Returns the current age of the response, in milliseconds. The calculation is specified
         * by RFC
         * 7234, 4.2.3 Calculating Age.
         */
        private long cacheResponseAge() {
           //todo
           // Data 
           // Age :  
           // :apparentReceivedAge
           // apparentReceivedAge  ageSeconds receivedAge  
           // responseDuration
           // residentDuration
           // 
            long apparentReceivedAge = servedDate != null
                    ? Math.max(0, receivedResponseMillis - servedDate.getTime())
                    : 0;
            long receivedAge = ageSeconds != -1
                    ? Math.max(apparentReceivedAge, SECONDS.toMillis(ageSeconds))
                    : apparentReceivedAge;
            long responseDuration = receivedResponseMillis - sentRequestMillis;
            long residentDuration = nowMillis - receivedResponseMillis;
            return receivedAge + responseDuration + residentDuration;
        }

       /**
         * Returns true if computeFreshnessLifetime used a heuristic. If we used a heuristic to
         * serve a
         * cached response older than 24 hours, we are required to attach a warning.
         */
        private boolean isFreshnessLifetimeHeuristic() {
            return cacheResponse.cacheControl().maxAgeSeconds() == -1 && expires == null;
        }

       /**
         * Returns true if the request contains conditions that save the server from sending a
         * response
         * that the client has locally. When a request is enqueued with its own conditions, the
         * built-in
         * response cache won't be used.
         */
        private static boolean hasConditions(Request request) {
            return request.header("If-Modified-Since") != null || request.header("If-None-Match") != null;
        }
 

There are only 2 core codes