1、前言
1 2 3 4 | < method > < request-url > < version > < headers > < entity-body > |
1 2 3 4 | < version > < status > < reason-phrase > < headers > < entity-body > |
1 2 3 4 5 | < method > 指请求方法,常用的主要是Get、 Post、Head 还有其他一些我们这里就不说了,有兴趣的可以自己查阅一下 < version > 指协议版本,现在通常都是Http/1.1了 < request-url > 请求地址 < status > 指响应状态码, 我们熟悉的200、404等等 < reason-phrase > 原因短语,200 OK 、404 Not Found 这种后面的描述就是原因短语,通常不必太关注。 |
01 02 03 04 05 06 07 08 09 10 | Date Cache-Control Last-Modified Etag Expires If-Modified-Since If-None-Match If-Unmodified-Since If-Range If-Match |
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 | final InternalCache cache; @Override public Response intercept(Chain chain) throws IOException { Response cacheCandidate = cache != null ? cache.get(chain.request()) : null ; long now = System.currentTimeMillis(); CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get(); Request networkRequest = strategy.networkRequest; Response cacheResponse = strategy.cacheResponse; ...... } |
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | CacheStrategy.Factory.java 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 ); } } } } |
01 02 03 04 05 06 07 08 09 10 | public CacheStrategy get() { CacheStrategy candidate = getCandidate(); 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; } |
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | private CacheStrategy getCandidate() { // No cached response. if (cacheResponse == null ) { return new CacheStrategy(request, null ); } // Drop the cached response if it's missing a required handshake. if (request.isHttps() && cacheResponse.handshake() == null ) { return new CacheStrategy(request, null ); } // 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(); if (requestCaching.noCache() || hasConditions(request)) { return new CacheStrategy(request, null ); } ...... } |
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | private CacheStrategy getCandidate() { ...... 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()); } ...... } |
ageMillis + minFreshMillis < freshMillis + maxStaleMillis
Cache-Control:min-fresh=xxx、Cache-Control:max-statle=xxx
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | private CacheStrategy getCandidate() { ...... // 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); } |
01 02 03 04 05 06 07 08 09 10 11 12 | // 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(); } |
1 2 3 4 5 6 | // If we don't need the network, we're done. if (networkRequest == null ) { return cacheResponse.newBuilder() .cacheResponse(stripBody(cacheResponse)) .build(); } |
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | 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(); // 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 = 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; |
联系客服