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 }