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 org.apache.hc.core5.util.Args;
30  import org.apache.hc.core5.util.TimeValue;
31  
32  /**
33   * <p>Configuration for HTTP caches</p>
34   *
35   * <p>Cache configuration can be grouped into the following categories:</p>
36   *
37   * <p><b>Protocol options.</b> I some cases the HTTP protocol allows for
38   * conditional behaviors or optional protocol extensions. Such conditional
39   * protocol behaviors or extensions can be turned on or off here.
40   * See {@link CacheConfig#isNeverCacheHTTP10ResponsesWithQuery()},
41   * {@link CacheConfig#isNeverCacheHTTP11ResponsesWithQuery()},
42   * {@link CacheConfig#isStaleIfErrorEnabled()}</p>
43   *
44   * <p><b>Cache size.</b> If the backend storage supports these limits, one
45   * can specify the {@link CacheConfig#getMaxCacheEntries maximum number of
46   * cache entries} as well as the {@link CacheConfig#getMaxObjectSize()}
47   * maximum cacheable response body size}.</p>
48   *
49   * <p><b>Public/private caching.</b> By default, the caching module considers
50   * itself to be a shared (public) cache, and will not, for example, cache
51   * responses to requests with {@code Authorization} headers or responses
52   * marked with {@code Cache-Control: private}. If, however, the cache
53   * is only going to be used by one logical "user" (behaving similarly to a
54   * browser cache), then one may want to {@link CacheConfig#isSharedCache()}
55   * turn off the shared cache setting}.</p>
56   *
57   * <p><b>Heuristic caching</b>. Per HTTP caching specification, a cache may
58   * cache certain cache entries even if no explicit cache control headers are
59   * set by the origin. This behavior is off by default, but you may want to
60   * turn this on if you are working with an origin that doesn't set proper
61   * headers but where one may still want to cache the responses. Use {@link
62   * CacheConfig#isHeuristicCachingEnabled()} to enable heuristic caching},
63   * then specify either a {@link CacheConfig#getHeuristicDefaultLifetime()
64   * default freshness lifetime} and/or a {@link
65   * CacheConfig#getHeuristicCoefficient() fraction of the time since
66   * the resource was last modified}.
67   *
68   * <p><b>Background validation</b>. The cache module supports the
69   * {@code stale-while-revalidate} directive, which allows certain cache entry
70   * revalidations to happen in the background. Asynchronous validation is enabled
71   * by default but it could be disabled by setting the number of re-validation
72   * workers to {@code 0} with {@link CacheConfig#getAsynchronousWorkers()}
73   * parameter</p>
74   */
75  public class CacheConfig implements Cloneable {
76  
77      /** Default setting for the maximum object size that will be
78       * cached, in bytes.
79       */
80      public final static int DEFAULT_MAX_OBJECT_SIZE_BYTES = 8192;
81  
82      /** Default setting for the maximum number of cache entries
83       * that will be retained.
84       */
85      public final static int DEFAULT_MAX_CACHE_ENTRIES = 1000;
86  
87      /** Default setting for the number of retries on a failed
88       * cache processChallenge
89       */
90      public final static int DEFAULT_MAX_UPDATE_RETRIES = 1;
91  
92      /**
93       * @deprecated No longer applicable. Do not use.
94       */
95      @Deprecated
96      public final static boolean DEFAULT_303_CACHING_ENABLED = false;
97  
98      /**
99       * @deprecated No longer applicable. Do not use.
100      */
101     @Deprecated
102     public final static boolean DEFAULT_WEAK_ETAG_ON_PUTDELETE_ALLOWED = false;
103 
104     /** Default setting for heuristic caching
105      */
106     public final static boolean DEFAULT_HEURISTIC_CACHING_ENABLED = false;
107 
108     /** Default coefficient used to heuristically determine freshness
109      * lifetime from the Last-Modified time of a cache entry.
110      */
111     public final static float DEFAULT_HEURISTIC_COEFFICIENT = 0.1f;
112 
113     /** Default lifetime to be assumed when we cannot calculate
114      * freshness heuristically.
115      */
116     public final static TimeValue DEFAULT_HEURISTIC_LIFETIME = TimeValue.ZERO_MILLISECONDS;
117 
118     /** Default number of worker threads to allow for background revalidations
119      * resulting from the stale-while-revalidate directive.
120      */
121     public static final int DEFAULT_ASYNCHRONOUS_WORKERS = 1;
122 
123     public static final CacheConfig DEFAULT = new Builder().build();
124 
125     private final long maxObjectSize;
126     private final int maxCacheEntries;
127     private final int maxUpdateRetries;
128     private final boolean heuristicCachingEnabled;
129     private final float heuristicCoefficient;
130     private final TimeValue heuristicDefaultLifetime;
131     private final boolean sharedCache;
132     private final boolean freshnessCheckEnabled;
133     private final int asynchronousWorkers;
134     private final boolean neverCacheHTTP10ResponsesWithQuery;
135     private final boolean staleIfErrorEnabled;
136 
137 
138     /**
139      * A constant indicating whether HTTP/1.1 responses with a query string should never be cached.
140      *
141      */
142     private final boolean neverCacheHTTP11ResponsesWithQuery;
143 
144     CacheConfig(
145             final long maxObjectSize,
146             final int maxCacheEntries,
147             final int maxUpdateRetries,
148             final boolean heuristicCachingEnabled,
149             final float heuristicCoefficient,
150             final TimeValue heuristicDefaultLifetime,
151             final boolean sharedCache,
152             final boolean freshnessCheckEnabled,
153             final int asynchronousWorkers,
154             final boolean neverCacheHTTP10ResponsesWithQuery,
155             final boolean neverCacheHTTP11ResponsesWithQuery,
156             final boolean staleIfErrorEnabled) {
157         super();
158         this.maxObjectSize = maxObjectSize;
159         this.maxCacheEntries = maxCacheEntries;
160         this.maxUpdateRetries = maxUpdateRetries;
161         this.heuristicCachingEnabled = heuristicCachingEnabled;
162         this.heuristicCoefficient = heuristicCoefficient;
163         this.heuristicDefaultLifetime = heuristicDefaultLifetime;
164         this.sharedCache = sharedCache;
165         this.freshnessCheckEnabled = freshnessCheckEnabled;
166         this.asynchronousWorkers = asynchronousWorkers;
167         this.neverCacheHTTP10ResponsesWithQuery = neverCacheHTTP10ResponsesWithQuery;
168         this.neverCacheHTTP11ResponsesWithQuery = neverCacheHTTP11ResponsesWithQuery;
169         this.staleIfErrorEnabled = staleIfErrorEnabled;
170     }
171 
172     /**
173      * Returns the current maximum response body size that will be cached.
174      * @return size in bytes
175      *
176      * @since 4.2
177      */
178     public long getMaxObjectSize() {
179         return maxObjectSize;
180     }
181 
182     /**
183      * Returns whether the cache will never cache HTTP 1.0 responses with a query string or not.
184      * @return {@code true} to not cache query string responses, {@code false} to cache if explicit cache headers are
185      * found
186      */
187     public boolean isNeverCacheHTTP10ResponsesWithQuery() {
188         return neverCacheHTTP10ResponsesWithQuery;
189     }
190 
191     /**
192      * Determines whether HTTP/1.1 responses with query strings should never be cached by the
193      * client. By default, caching of such responses is allowed. Enabling this option may improve
194      * security by preventing responses with sensitive information from being cached.
195      * <p>
196      * Note that this option only applies to HTTP/1.1.
197      * </p>
198      *
199      * @return {@code true} if HTTP/1.1 responses with query strings should never be cached;
200      * {@code false} otherwise.
201      * @since 5.3
202      */
203     public boolean isNeverCacheHTTP11ResponsesWithQuery() {
204         return neverCacheHTTP11ResponsesWithQuery;
205     }
206 
207     /**
208      * Returns a boolean value indicating whether the stale-if-error cache
209      * directive is enabled. If this option is enabled, cached responses that
210      * have become stale due to an error (such as a server error or a network
211      * failure) will be returned instead of generating a new request. This can
212      * help to reduce the load on the origin server and improve performance.
213      * @return {@code true} if the stale-if-error directive is enabled, or
214      * {@code false} otherwise.
215      */
216     public boolean isStaleIfErrorEnabled() {
217         return this.staleIfErrorEnabled;
218     }
219 
220     /**
221      * Returns the maximum number of cache entries the cache will retain.
222      */
223     public int getMaxCacheEntries() {
224         return maxCacheEntries;
225     }
226 
227     /**
228      * Returns the number of times to retry a cache processChallenge on failure
229      */
230     public int getMaxUpdateRetries(){
231         return maxUpdateRetries;
232     }
233 
234     /**
235      * @deprecated No longer applicable. Do not use.
236      */
237     @Deprecated
238     public boolean is303CachingEnabled() {
239         return true;
240     }
241 
242     /**
243      * Returns whether weak etags is allowed with PUT/DELETE methods.
244      * @return {@code true} if it is allowed.
245      *
246      * @deprecated Do not use.
247      */
248     @Deprecated
249     public boolean isWeakETagOnPutDeleteAllowed() {
250         return true;
251     }
252 
253     /**
254      * Returns whether heuristic caching is enabled.
255      * @return {@code true} if it is enabled.
256      */
257     public boolean isHeuristicCachingEnabled() {
258         return heuristicCachingEnabled;
259     }
260 
261     /**
262      * Returns lifetime coefficient used in heuristic freshness caching.
263      */
264     public float getHeuristicCoefficient() {
265         return heuristicCoefficient;
266     }
267 
268     /**
269      * Get the default lifetime to be used if heuristic freshness calculation is
270      * not possible.
271      */
272     public TimeValue getHeuristicDefaultLifetime() {
273         return heuristicDefaultLifetime;
274     }
275 
276     /**
277      * Returns whether the cache will behave as a shared cache or not.
278      * @return {@code true} for a shared cache, {@code false} for a non-
279      * shared (private) cache
280      */
281     public boolean isSharedCache() {
282         return sharedCache;
283     }
284 
285     /**
286      * Returns whether the cache will perform an extra cache entry freshness check
287      * upon cache update in case of a cache miss
288      *
289      * @since 5.0
290      */
291     public boolean isFreshnessCheckEnabled() {
292         return freshnessCheckEnabled;
293     }
294 
295     /**
296      * Returns the maximum number of threads to allow for background
297      * revalidations due to the {@code stale-while-revalidate} directive. A
298      * value of 0 means background revalidations are disabled.
299      */
300     public int getAsynchronousWorkers() {
301         return asynchronousWorkers;
302     }
303 
304     @Override
305     protected CacheConfig clone() throws CloneNotSupportedException {
306         return (CacheConfig) super.clone();
307     }
308 
309     public static Builder custom() {
310         return new Builder();
311     }
312 
313     public static Builder copy(final CacheConfig config) {
314         Args.notNull(config, "Cache config");
315         return new Builder()
316             .setMaxObjectSize(config.getMaxObjectSize())
317             .setMaxCacheEntries(config.getMaxCacheEntries())
318             .setMaxUpdateRetries(config.getMaxUpdateRetries())
319             .setHeuristicCachingEnabled(config.isHeuristicCachingEnabled())
320             .setHeuristicCoefficient(config.getHeuristicCoefficient())
321             .setHeuristicDefaultLifetime(config.getHeuristicDefaultLifetime())
322             .setSharedCache(config.isSharedCache())
323             .setAsynchronousWorkers(config.getAsynchronousWorkers())
324             .setNeverCacheHTTP10ResponsesWithQueryString(config.isNeverCacheHTTP10ResponsesWithQuery())
325             .setNeverCacheHTTP11ResponsesWithQueryString(config.isNeverCacheHTTP11ResponsesWithQuery())
326             .setStaleIfErrorEnabled(config.isStaleIfErrorEnabled());
327     }
328 
329     public static class Builder {
330 
331         private long maxObjectSize;
332         private int maxCacheEntries;
333         private int maxUpdateRetries;
334         private boolean heuristicCachingEnabled;
335         private float heuristicCoefficient;
336         private TimeValue heuristicDefaultLifetime;
337         private boolean sharedCache;
338         private boolean freshnessCheckEnabled;
339         private int asynchronousWorkers;
340         private boolean neverCacheHTTP10ResponsesWithQuery;
341         private boolean neverCacheHTTP11ResponsesWithQuery;
342         private boolean staleIfErrorEnabled;
343 
344         Builder() {
345             this.maxObjectSize = DEFAULT_MAX_OBJECT_SIZE_BYTES;
346             this.maxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES;
347             this.maxUpdateRetries = DEFAULT_MAX_UPDATE_RETRIES;
348             this.heuristicCachingEnabled = DEFAULT_HEURISTIC_CACHING_ENABLED;
349             this.heuristicCoefficient = DEFAULT_HEURISTIC_COEFFICIENT;
350             this.heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME;
351             this.sharedCache = true;
352             this.freshnessCheckEnabled = true;
353             this.asynchronousWorkers = DEFAULT_ASYNCHRONOUS_WORKERS;
354             this.staleIfErrorEnabled = false;
355         }
356 
357         /**
358          * Specifies the maximum response body size that will be eligible for caching.
359          * @param maxObjectSize size in bytes
360          */
361         public Builder setMaxObjectSize(final long maxObjectSize) {
362             this.maxObjectSize = maxObjectSize;
363             return this;
364         }
365 
366         /**
367          * Sets the maximum number of cache entries the cache will retain.
368          */
369         public Builder setMaxCacheEntries(final int maxCacheEntries) {
370             this.maxCacheEntries = maxCacheEntries;
371             return this;
372         }
373 
374         /**
375          * Sets the number of times to retry a cache processChallenge on failure
376          */
377         public Builder setMaxUpdateRetries(final int maxUpdateRetries) {
378             this.maxUpdateRetries = maxUpdateRetries;
379             return this;
380         }
381 
382         /**
383          * @deprecated Has no effect. Do not use.
384          */
385         @Deprecated
386         public Builder setAllow303Caching(final boolean allow303Caching) {
387             return this;
388         }
389 
390         /**
391          * @deprecated No longer applicable. Do not use.
392          */
393         @Deprecated
394         public Builder setWeakETagOnPutDeleteAllowed(final boolean weakETagOnPutDeleteAllowed) {
395             return this;
396         }
397 
398         /**
399          * Enables or disables heuristic caching.
400          * @param heuristicCachingEnabled should be {@code true} to
401          *   permit heuristic caching, {@code false} to enable it.
402          */
403         public Builder setHeuristicCachingEnabled(final boolean heuristicCachingEnabled) {
404             this.heuristicCachingEnabled = heuristicCachingEnabled;
405             return this;
406         }
407 
408         /**
409          * Sets coefficient to be used in heuristic freshness caching. This is
410          * interpreted as the fraction of the time between the {@code Last-Modified}
411          * and {@code Date} headers of a cached response during which the cached
412          * response will be considered heuristically fresh.
413          * @param heuristicCoefficient should be between {@code 0.0} and
414          *   {@code 1.0}.
415          */
416         public Builder setHeuristicCoefficient(final float heuristicCoefficient) {
417             this.heuristicCoefficient = heuristicCoefficient;
418             return this;
419         }
420 
421         /**
422          * Sets default lifetime to be used if heuristic freshness calculation
423          * is not possible. Explicit cache control directives on either the
424          * request or origin response will override this, as will the heuristic
425          * {@code Last-Modified} freshness calculation if it is available.
426          *
427          * @param heuristicDefaultLifetime is the number to consider a
428          *   cache-eligible response fresh in the absence of other information.
429          *   Set this to {@code 0} to disable this style of heuristic caching.
430          */
431         public Builder setHeuristicDefaultLifetime(final TimeValue heuristicDefaultLifetime) {
432             this.heuristicDefaultLifetime = heuristicDefaultLifetime;
433             return this;
434         }
435 
436         /**
437          * Sets whether the cache should behave as a shared cache or not.
438          * @param sharedCache true to behave as a shared cache, false to
439          * behave as a non-shared (private) cache. To have the cache
440          * behave like a browser cache, you want to set this to {@code false}.
441          */
442         public Builder setSharedCache(final boolean sharedCache) {
443             this.sharedCache = sharedCache;
444             return this;
445         }
446 
447         /**
448          * Sets the maximum number of threads to allow for background
449          * revalidations due to the {@code stale-while-revalidate} directive.
450          * @param asynchronousWorkers number of threads; a value of 0 disables background
451          * revalidations.
452          */
453         public Builder setAsynchronousWorkers(final int asynchronousWorkers) {
454             this.asynchronousWorkers = asynchronousWorkers;
455             return this;
456         }
457 
458         /**
459          * Sets whether the cache should never cache HTTP 1.0 responses with a query string or not.
460          * @param neverCacheHTTP10ResponsesWithQuery true to never cache responses with a query
461          * string, false to cache if explicit cache headers are found.  Set this to {@code true}
462          * to better emulate IE, which also never caches responses, regardless of what caching
463          * headers may be present.
464          */
465         public Builder setNeverCacheHTTP10ResponsesWithQueryString(
466                 final boolean neverCacheHTTP10ResponsesWithQuery) {
467             this.neverCacheHTTP10ResponsesWithQuery = neverCacheHTTP10ResponsesWithQuery;
468             return this;
469         }
470 
471         /**
472          * Enables or disables the stale-if-error cache directive. If this option
473          * is enabled, cached responses that have become stale due to an error (such
474          * as a server error or a network failure) will be returned instead of
475          * generating a new request. This can help to reduce the load on the origin
476          * server and improve performance.
477          * <p>
478          * By default, the stale-if-error directive is disabled.
479          *
480          * @param enabled a boolean value indicating whether the stale-if-error
481          *                directive should be enabled.
482          * @return the builder object
483          */
484         public Builder setStaleIfErrorEnabled(final boolean enabled) {
485             this.staleIfErrorEnabled = enabled;
486             return this;
487         }
488 
489         public Builder setFreshnessCheckEnabled(final boolean freshnessCheckEnabled) {
490             this.freshnessCheckEnabled = freshnessCheckEnabled;
491             return this;
492         }
493 
494         /**
495          * Sets the flag indicating whether HTTP/1.1 responses with a query string should never be cached.
496          *
497          * @param neverCacheHTTP11ResponsesWithQuery whether to never cache HTTP/1.1 responses with a query string
498          * @return the builder object
499          */
500         public Builder setNeverCacheHTTP11ResponsesWithQueryString(
501                 final boolean neverCacheHTTP11ResponsesWithQuery) {
502             this.neverCacheHTTP11ResponsesWithQuery = neverCacheHTTP11ResponsesWithQuery;
503             return this;
504         }
505 
506         public CacheConfig build() {
507             return new CacheConfig(
508                     maxObjectSize,
509                     maxCacheEntries,
510                     maxUpdateRetries,
511                     heuristicCachingEnabled,
512                     heuristicCoefficient,
513                     heuristicDefaultLifetime,
514                     sharedCache,
515                     freshnessCheckEnabled,
516                     asynchronousWorkers,
517                     neverCacheHTTP10ResponsesWithQuery,
518                     neverCacheHTTP11ResponsesWithQuery,
519                     staleIfErrorEnabled);
520         }
521 
522     }
523 
524     @Override
525     public String toString() {
526         final StringBuilder builder = new StringBuilder();
527         builder.append("[maxObjectSize=").append(this.maxObjectSize)
528                 .append(", maxCacheEntries=").append(this.maxCacheEntries)
529                 .append(", maxUpdateRetries=").append(this.maxUpdateRetries)
530                 .append(", heuristicCachingEnabled=").append(this.heuristicCachingEnabled)
531                 .append(", heuristicCoefficient=").append(this.heuristicCoefficient)
532                 .append(", heuristicDefaultLifetime=").append(this.heuristicDefaultLifetime)
533                 .append(", sharedCache=").append(this.sharedCache)
534                 .append(", freshnessCheckEnabled=").append(this.freshnessCheckEnabled)
535                 .append(", asynchronousWorkers=").append(this.asynchronousWorkers)
536                 .append(", neverCacheHTTP10ResponsesWithQuery=").append(this.neverCacheHTTP10ResponsesWithQuery)
537                 .append(", neverCacheHTTP11ResponsesWithQuery=").append(this.neverCacheHTTP11ResponsesWithQuery)
538                 .append(", staleIfErrorEnabled=").append(this.staleIfErrorEnabled)
539                 .append("]");
540         return builder.toString();
541     }
542 
543 }