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.4
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          * @return this instance.
361          */
362         public Builder setMaxObjectSize(final long maxObjectSize) {
363             this.maxObjectSize = maxObjectSize;
364             return this;
365         }
366 
367         /**
368          * Sets the maximum number of cache entries the cache will retain.
369          *
370          * @return this instance.
371          */
372         public Builder setMaxCacheEntries(final int maxCacheEntries) {
373             this.maxCacheEntries = maxCacheEntries;
374             return this;
375         }
376 
377         /**
378          * Sets the number of times to retry a cache processChallenge on failure
379          *
380          * @return this instance.
381          */
382         public Builder setMaxUpdateRetries(final int maxUpdateRetries) {
383             this.maxUpdateRetries = maxUpdateRetries;
384             return this;
385         }
386 
387         /**
388          * @return this instance.
389          * @deprecated Has no effect. Do not use.
390          */
391         @Deprecated
392         public Builder setAllow303Caching(final boolean allow303Caching) {
393             return this;
394         }
395 
396         /**
397          * @return this instance.
398          * @deprecated No longer applicable. Do not use.
399          */
400         @Deprecated
401         public Builder setWeakETagOnPutDeleteAllowed(final boolean weakETagOnPutDeleteAllowed) {
402             return this;
403         }
404 
405         /**
406          * Enables or disables heuristic caching.
407          * @param heuristicCachingEnabled should be {@code true} to
408          *   permit heuristic caching, {@code false} to enable it.
409          * @return this instance.
410          */
411         public Builder setHeuristicCachingEnabled(final boolean heuristicCachingEnabled) {
412             this.heuristicCachingEnabled = heuristicCachingEnabled;
413             return this;
414         }
415 
416         /**
417          * Sets coefficient to be used in heuristic freshness caching. This is
418          * interpreted as the fraction of the time between the {@code Last-Modified}
419          * and {@code Date} headers of a cached response during which the cached
420          * response will be considered heuristically fresh.
421          * @param heuristicCoefficient should be between {@code 0.0} and
422          *   {@code 1.0}.
423          * @return this instance.
424          */
425         public Builder setHeuristicCoefficient(final float heuristicCoefficient) {
426             this.heuristicCoefficient = heuristicCoefficient;
427             return this;
428         }
429 
430         /**
431          * Sets default lifetime to be used if heuristic freshness calculation
432          * is not possible. Explicit cache control directives on either the
433          * request or origin response will override this, as will the heuristic
434          * {@code Last-Modified} freshness calculation if it is available.
435          *
436          * @param heuristicDefaultLifetime is the number to consider a
437          *   cache-eligible response fresh in the absence of other information.
438          *   Set this to {@code 0} to disable this style of heuristic caching.
439          * @return this instance.
440          */
441         public Builder setHeuristicDefaultLifetime(final TimeValue heuristicDefaultLifetime) {
442             this.heuristicDefaultLifetime = heuristicDefaultLifetime;
443             return this;
444         }
445 
446         /**
447          * Sets whether the cache should behave as a shared cache or not.
448          * @param sharedCache true to behave as a shared cache, false to
449          * behave as a non-shared (private) cache. To have the cache
450          * behave like a browser cache, you want to set this to {@code false}.
451          *
452          * @return this instance.
453          */
454         public Builder setSharedCache(final boolean sharedCache) {
455             this.sharedCache = sharedCache;
456             return this;
457         }
458 
459         /**
460          * Sets the maximum number of threads to allow for background
461          * revalidations due to the {@code stale-while-revalidate} directive.
462          * @param asynchronousWorkers number of threads; a value of 0 disables background
463          * revalidations.
464          * @return this instance.
465          */
466         public Builder setAsynchronousWorkers(final int asynchronousWorkers) {
467             this.asynchronousWorkers = asynchronousWorkers;
468             return this;
469         }
470 
471         /**
472          * Sets whether the cache should never cache HTTP 1.0 responses with a query string or not.
473          * @param neverCacheHTTP10ResponsesWithQuery true to never cache responses with a query
474          * string, false to cache if explicit cache headers are found.  Set this to {@code true}
475          * to better emulate IE, which also never caches responses, regardless of what caching
476          * headers may be present.
477          * @return this instance.
478          */
479         public Builder setNeverCacheHTTP10ResponsesWithQueryString(
480                 final boolean neverCacheHTTP10ResponsesWithQuery) {
481             this.neverCacheHTTP10ResponsesWithQuery = neverCacheHTTP10ResponsesWithQuery;
482             return this;
483         }
484 
485         /**
486          * Enables or disables the stale-if-error cache directive. If this option
487          * is enabled, cached responses that have become stale due to an error (such
488          * as a server error or a network failure) will be returned instead of
489          * generating a new request. This can help to reduce the load on the origin
490          * server and improve performance.
491          * <p>
492          * By default, the stale-if-error directive is disabled.
493          * </p>
494          *
495          * @param enabled a boolean value indicating whether the stale-if-error
496          *                directive should be enabled.
497          * @return this instance.
498          */
499         public Builder setStaleIfErrorEnabled(final boolean enabled) {
500             this.staleIfErrorEnabled = enabled;
501             return this;
502         }
503 
504         public Builder setFreshnessCheckEnabled(final boolean freshnessCheckEnabled) {
505             this.freshnessCheckEnabled = freshnessCheckEnabled;
506             return this;
507         }
508 
509         /**
510          * Sets the flag indicating whether HTTP/1.1 responses with a query string should never be cached.
511          *
512          * @param neverCacheHTTP11ResponsesWithQuery whether to never cache HTTP/1.1 responses with a query string
513          * @return this instance.
514          */
515         public Builder setNeverCacheHTTP11ResponsesWithQueryString(
516                 final boolean neverCacheHTTP11ResponsesWithQuery) {
517             this.neverCacheHTTP11ResponsesWithQuery = neverCacheHTTP11ResponsesWithQuery;
518             return this;
519         }
520 
521         public CacheConfig build() {
522             return new CacheConfig(
523                     maxObjectSize,
524                     maxCacheEntries,
525                     maxUpdateRetries,
526                     heuristicCachingEnabled,
527                     heuristicCoefficient,
528                     heuristicDefaultLifetime,
529                     sharedCache,
530                     freshnessCheckEnabled,
531                     asynchronousWorkers,
532                     neverCacheHTTP10ResponsesWithQuery,
533                     neverCacheHTTP11ResponsesWithQuery,
534                     staleIfErrorEnabled);
535         }
536 
537     }
538 
539     @Override
540     public String toString() {
541         final StringBuilder builder = new StringBuilder();
542         builder.append("[maxObjectSize=").append(this.maxObjectSize)
543                 .append(", maxCacheEntries=").append(this.maxCacheEntries)
544                 .append(", maxUpdateRetries=").append(this.maxUpdateRetries)
545                 .append(", heuristicCachingEnabled=").append(this.heuristicCachingEnabled)
546                 .append(", heuristicCoefficient=").append(this.heuristicCoefficient)
547                 .append(", heuristicDefaultLifetime=").append(this.heuristicDefaultLifetime)
548                 .append(", sharedCache=").append(this.sharedCache)
549                 .append(", freshnessCheckEnabled=").append(this.freshnessCheckEnabled)
550                 .append(", asynchronousWorkers=").append(this.asynchronousWorkers)
551                 .append(", neverCacheHTTP10ResponsesWithQuery=").append(this.neverCacheHTTP10ResponsesWithQuery)
552                 .append(", neverCacheHTTP11ResponsesWithQuery=").append(this.neverCacheHTTP11ResponsesWithQuery)
553                 .append(", staleIfErrorEnabled=").append(this.staleIfErrorEnabled)
554                 .append("]");
555         return builder.toString();
556     }
557 
558 }