View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.hc.client5.http.impl.cache;
28  
29  import java.time.Instant;
30  import java.util.concurrent.atomic.AtomicLong;
31  
32  import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
33  import org.apache.hc.client5.http.cache.HttpCacheEntry;
34  import org.apache.hc.client5.http.cache.ResourceIOException;
35  import org.apache.hc.core5.http.EntityDetails;
36  import org.apache.hc.core5.http.Header;
37  import org.apache.hc.core5.http.HttpHeaders;
38  import org.apache.hc.core5.http.HttpRequest;
39  import org.apache.hc.core5.http.HttpResponse;
40  import org.apache.hc.core5.http.HttpStatus;
41  import org.apache.hc.core5.http.Method;
42  
43  public class CachingExecBase {
44  
45      final AtomicLong cacheHits = new AtomicLong();
46      final AtomicLong cacheMisses = new AtomicLong();
47      final AtomicLong cacheUpdates = new AtomicLong();
48  
49      final ResponseCachingPolicy responseCachingPolicy;
50      final CacheValidityPolicy validityPolicy;
51      final CachedHttpResponseGenerator responseGenerator;
52      final CacheableRequestPolicy cacheableRequestPolicy;
53      final CachedResponseSuitabilityChecker suitabilityChecker;
54      final CacheConfig cacheConfig;
55  
56      CachingExecBase(
57              final CacheValidityPolicy validityPolicy,
58              final ResponseCachingPolicy responseCachingPolicy,
59              final CachedHttpResponseGenerator responseGenerator,
60              final CacheableRequestPolicy cacheableRequestPolicy,
61              final CachedResponseSuitabilityChecker suitabilityChecker,
62              final CacheConfig config) {
63          this.responseCachingPolicy = responseCachingPolicy;
64          this.validityPolicy = validityPolicy;
65          this.responseGenerator = responseGenerator;
66          this.cacheableRequestPolicy = cacheableRequestPolicy;
67          this.suitabilityChecker = suitabilityChecker;
68          this.cacheConfig = config != null ? config : CacheConfig.DEFAULT;
69      }
70  
71      CachingExecBase(final CacheConfig config) {
72          super();
73          this.cacheConfig = config != null ? config : CacheConfig.DEFAULT;
74          this.validityPolicy = new CacheValidityPolicy(config);
75          this.responseGenerator = new CachedHttpResponseGenerator(this.validityPolicy);
76          this.cacheableRequestPolicy = new CacheableRequestPolicy();
77          this.suitabilityChecker = new CachedResponseSuitabilityChecker(this.validityPolicy, this.cacheConfig);
78          this.responseCachingPolicy = new ResponseCachingPolicy(
79                  this.cacheConfig.isSharedCache(),
80                  this.cacheConfig.isNeverCacheHTTP10ResponsesWithQuery(),
81                  this.cacheConfig.isNeverCacheHTTP11ResponsesWithQuery());
82      }
83  
84      /**
85       * Reports the number of times that the cache successfully responded
86       * to an {@link HttpRequest} without contacting the origin server.
87       * @return the number of cache hits
88       */
89      public long getCacheHits() {
90          return cacheHits.get();
91      }
92  
93      /**
94       * Reports the number of times that the cache contacted the origin
95       * server because it had no appropriate response cached.
96       * @return the number of cache misses
97       */
98      public long getCacheMisses() {
99          return cacheMisses.get();
100     }
101 
102     /**
103      * Reports the number of times that the cache was able to satisfy
104      * a response by revalidating an existing but stale cache entry.
105      * @return the number of cache revalidations
106      */
107     public long getCacheUpdates() {
108         return cacheUpdates.get();
109     }
110 
111     SimpleHttpResponse generateCachedResponse(
112             final HttpRequest request,
113             final HttpCacheEntry entry,
114             final Instant now) throws ResourceIOException {
115         if (shouldSendNotModifiedResponse(request, entry, now)) {
116             return responseGenerator.generateNotModifiedResponse(entry);
117         }
118         return responseGenerator.generateResponse(request, entry);
119     }
120 
121     SimpleHttpResponse generateGatewayTimeout() {
122         return SimpleHttpResponse.create(HttpStatus.SC_GATEWAY_TIMEOUT, "Gateway Timeout");
123     }
124 
125     Instant getCurrentDate() {
126         return Instant.now();
127     }
128 
129     boolean clientRequestsOurOptions(final HttpRequest request) {
130         if (!Method.OPTIONS.isSame(request.getMethod())) {
131             return false;
132         }
133 
134         if (!"*".equals(request.getRequestUri())) {
135             return false;
136         }
137 
138         final Header h = request.getFirstHeader(HttpHeaders.MAX_FORWARDS);
139         return "0".equals(h != null ? h.getValue() : null);
140     }
141 
142     boolean shouldSendNotModifiedResponse(final HttpRequest request, final HttpCacheEntry responseEntry, final Instant now) {
143         return suitabilityChecker.isConditional(request)
144                 && suitabilityChecker.allConditionalsMatch(request, responseEntry, now);
145     }
146 
147     boolean staleIfErrorAppliesTo(final int statusCode) {
148         return statusCode == HttpStatus.SC_INTERNAL_SERVER_ERROR
149                 || statusCode == HttpStatus.SC_BAD_GATEWAY
150                 || statusCode == HttpStatus.SC_SERVICE_UNAVAILABLE
151                 || statusCode == HttpStatus.SC_GATEWAY_TIMEOUT;
152     }
153 
154     /**
155      * For 304 Not modified responses, adds a "Last-Modified" header with the
156      * value of the "If-Modified-Since" header passed in the request. This
157      * header is required to be able to reuse match the cache entry for
158      * subsequent requests but as defined in http specifications it is not
159      * included in 304 responses by backend servers. This header will not be
160      * included in the resulting response.
161      */
162     void storeRequestIfModifiedSinceFor304Response(final HttpRequest request, final HttpResponse backendResponse) {
163         if (backendResponse.getCode() == HttpStatus.SC_NOT_MODIFIED) {
164             final Header h = request.getFirstHeader(HttpHeaders.IF_MODIFIED_SINCE);
165             if (h != null) {
166                 backendResponse.addHeader(HttpHeaders.LAST_MODIFIED, h.getValue());
167             }
168         }
169     }
170 
171     boolean isResponseTooBig(final EntityDetails entityDetails) {
172         if (entityDetails == null) {
173             return false;
174         }
175         final long contentLength = entityDetails.getContentLength();
176         if (contentLength == -1) {
177             return false;
178         }
179         final long maxObjectSize = cacheConfig.getMaxObjectSize();
180         return contentLength > maxObjectSize;
181     }
182 
183 }