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  
28  package org.apache.hc.client5.http.cache;
29  
30  import java.util.Arrays;
31  import java.util.Collections;
32  import java.util.HashSet;
33  import java.util.Set;
34  
35  import org.apache.hc.core5.annotation.Contract;
36  import org.apache.hc.core5.annotation.ThreadingBehavior;
37  
38  /**
39   * Represents the value of the Cache-Control header in an HTTP response, which indicate whether and for how long
40   * the response can be cached by the client and intermediary proxies.
41   * <p>
42   * The class provides methods to retrieve the maximum age of the response and the maximum age that applies to shared
43   * caches. The values are expressed in seconds, with -1 indicating that the value was not specified in the header.
44   * <p>
45   * Instances of this class are immutable, meaning that their values cannot be changed once they are set. To create an
46   * instance, use one of the constructors that take the desired values as arguments. Alternatively, use the default
47   * constructor to create an instance with both values set to -1, indicating that the header was not present in the
48   * response.
49   *
50   * @since 5.4
51   */
52  @Contract(threading = ThreadingBehavior.IMMUTABLE)
53  public final class ResponseCacheControl implements CacheControl {
54  
55      /**
56       * The max-age directive value.
57       */
58      private final long maxAge;
59      /**
60       * The shared-max-age directive value.
61       */
62      private final long sharedMaxAge;
63      /**
64       * The isNoCache flag indicates whether the Cache-Control header includes the no-cache directive.
65       */
66      private final boolean noCache;
67      /**
68       * The isNoStore flag indicates whether the Cache-Control header includes the no-store directive.
69       */
70      private final boolean noStore;
71      /**
72       * The isPrivate flag indicates whether the Cache-Control header includes the private directive.
73       */
74      private final boolean cachePrivate;
75      /**
76       * Indicates whether the Cache-Control header includes the "must-revalidate" directive.
77       */
78      private final boolean mustRevalidate;
79      /**
80       * Indicates whether the Cache-Control header includes the "proxy-revalidate" directive.
81       */
82      private final boolean proxyRevalidate;
83      /**
84       * Indicates whether the Cache-Control header includes the "public" directive.
85       */
86      private final boolean cachePublic;
87      /**
88       * Indicates whether the Cache-Control header includes the "must-understand" directive.
89       */
90      private final boolean mustUnderstand;
91  
92      /**
93       * The number of seconds that a stale response is considered fresh for the purpose
94       * of serving a response while a revalidation request is made to the origin server.
95       */
96      private final long staleWhileRevalidate;
97      /**
98       * The number of seconds that a cached stale response MAY be used to satisfy the request,
99       * regardless of other freshness information..
100      */
101     private final long staleIfError;
102     /**
103      * A set of field names specified in the "no-cache" directive of the Cache-Control header.
104      */
105     private final Set<String> noCacheFields;
106 
107     private final boolean undefined;
108 
109     /**
110      * Flag for the 'immutable' Cache-Control directive.
111      * If this field is true, then the 'immutable' directive is present in the Cache-Control header.
112      * The 'immutable' directive is meant to inform a cache or user agent that the response body will not
113      * change over time, even though it may be requested multiple times.
114      */
115     private final boolean immutable;
116 
117     /**
118      * Creates a new instance of {@code CacheControl} with the specified values.
119      *
120      * @param maxAge          The max-age value from the Cache-Control header.
121      * @param sharedMaxAge    The shared-max-age value from the Cache-Control header.
122      * @param mustRevalidate  The must-revalidate value from the Cache-Control header.
123      * @param noCache         The no-cache value from the Cache-Control header.
124      * @param noStore         The no-store value from the Cache-Control header.
125      * @param cachePrivate    The private value from the Cache-Control header.
126      * @param proxyRevalidate The proxy-revalidate value from the Cache-Control header.
127      * @param cachePublic     The public value from the Cache-Control header.
128      * @param staleWhileRevalidate  The stale-while-revalidate value from the Cache-Control header.
129      * @param staleIfError    The stale-if-error value from the Cache-Control header.
130      * @param noCacheFields   The set of field names specified in the "no-cache" directive of the Cache-Control header.
131      * @param mustUnderstand  The must-understand value from the Cache-Control header.
132      * @param immutable       The immutable value from the Cache-Control header.
133      */
134     ResponseCacheControl(final long maxAge, final long sharedMaxAge, final boolean mustRevalidate, final boolean noCache,
135                          final boolean noStore, final boolean cachePrivate, final boolean proxyRevalidate,
136                          final boolean cachePublic, final long staleWhileRevalidate, final long staleIfError,
137                          final Set<String> noCacheFields, final boolean mustUnderstand, final boolean immutable) {
138         this.maxAge = maxAge;
139         this.sharedMaxAge = sharedMaxAge;
140         this.noCache = noCache;
141         this.noStore = noStore;
142         this.cachePrivate = cachePrivate;
143         this.mustRevalidate = mustRevalidate;
144         this.proxyRevalidate = proxyRevalidate;
145         this.cachePublic = cachePublic;
146         this.staleWhileRevalidate = staleWhileRevalidate;
147         this.staleIfError = staleIfError;
148         this.noCacheFields = noCacheFields != null ? Collections.unmodifiableSet(noCacheFields) : Collections.emptySet();
149         this.undefined = maxAge == -1 &&
150                 sharedMaxAge == -1 &&
151                 !noCache &&
152                 !noStore &&
153                 !cachePrivate &&
154                 !mustRevalidate &&
155                 !proxyRevalidate &&
156                 !cachePublic &&
157                 staleWhileRevalidate == -1
158                 && staleIfError == -1;
159         this.mustUnderstand = mustUnderstand;
160         this.immutable = immutable;
161     }
162 
163     /**
164      * Returns the max-age value from the Cache-Control header.
165      *
166      * @return The max-age value.
167      */
168     @Override
169     public long getMaxAge() {
170         return maxAge;
171     }
172 
173     /**
174      * Returns the shared-max-age value from the Cache-Control header.
175      *
176      * @return The shared-max-age value.
177      */
178     public long getSharedMaxAge() {
179         return sharedMaxAge;
180     }
181 
182     /**
183      * Returns the no-cache flag from the Cache-Control header.
184      *
185      * @return The no-cache flag.
186      */
187     @Override
188     public boolean isNoCache() {
189         return noCache;
190     }
191 
192     /**
193      * Returns the no-store flag from the Cache-Control header.
194      *
195      * @return The no-store flag.
196      */
197     @Override
198     public boolean isNoStore() {
199         return noStore;
200     }
201 
202     /**
203      * Returns the private flag from the Cache-Control header.
204      *
205      * @return The private flag.
206      */
207     public boolean isCachePrivate() {
208         return cachePrivate;
209     }
210 
211     /**
212      * Returns the must-understand directive from the Cache-Control header.
213      *
214      * @return The must-understand directive.
215      */
216     public boolean isMustUnderstand() {
217         return mustUnderstand;
218     }
219 
220     /**
221      * Returns whether the must-revalidate directive is present in the Cache-Control header.
222      *
223      * @return {@code true} if the must-revalidate directive is present, otherwise {@code false}
224      */
225     public boolean isMustRevalidate() {
226         return mustRevalidate;
227     }
228 
229     /**
230      * Returns whether the proxy-revalidate value is set in the Cache-Control header.
231      *
232      * @return {@code true} if proxy-revalidate is set, {@code false} otherwise.
233      */
234     public boolean isProxyRevalidate() {
235         return proxyRevalidate;
236     }
237 
238     /**
239      * Returns whether the public value is set in the Cache-Control header.
240      *
241      * @return {@code true} if public is set, {@code false} otherwise.
242      */
243     public boolean isPublic() {
244         return cachePublic;
245     }
246 
247     /**
248      * Returns the stale-while-revalidate value from the Cache-Control header.
249      *
250      * @return The stale-while-revalidate value.
251      */
252     public long getStaleWhileRevalidate() {
253         return staleWhileRevalidate;
254     }
255 
256     /**
257      * Returns the stale-if-error value from the Cache-Control header.
258      *
259      * @return The stale-if-error value.
260      */
261     @Override
262     public long getStaleIfError() {
263         return staleIfError;
264     }
265 
266     /**
267      * Returns an unmodifiable set of field names specified in the "no-cache" directive of the Cache-Control header.
268      *
269      * @return The set of field names specified in the "no-cache" directive.
270      */
271     public Set<String> getNoCacheFields() {
272         return noCacheFields;
273     }
274 
275     /**
276      * Returns the 'immutable' Cache-Control directive status.
277      *
278      * @return true if the 'immutable' directive is present in the Cache-Control header.
279      */
280     public boolean isUndefined() {
281         return undefined;
282     }
283 
284     /**
285      * Returns the 'immutable' Cache-Control directive status.
286      *
287      * @return true if the 'immutable' directive is present in the Cache-Control header.
288      */
289     public boolean isImmutable() {
290         return immutable;
291     }
292 
293     @Override
294     public String toString() {
295             final StringBuilder buf = new StringBuilder();
296             buf.append("[");
297             if (maxAge >= 0) {
298                 buf.append("max-age=").append(maxAge).append(",");
299             }
300             if (sharedMaxAge >= 0) {
301                 buf.append("shared-max-age=").append(sharedMaxAge).append(",");
302             }
303             if (noCache) {
304                 buf.append("no-cache").append(",");
305             }
306             if (noStore) {
307                 buf.append("no-store").append(",");
308             }
309             if (cachePrivate) {
310                 buf.append("private").append(",");
311             }
312             if (cachePublic) {
313                 buf.append("public").append(",");
314             }
315             if (mustRevalidate) {
316                 buf.append("must-revalidate").append(",");
317             }
318             if (proxyRevalidate) {
319                 buf.append("proxy-revalidate").append(",");
320             }
321             if (staleWhileRevalidate >= 0) {
322                 buf.append("state-while-revalidate=").append(staleWhileRevalidate).append(",");
323             }
324             if (staleIfError >= 0) {
325                 buf.append("stale-if-error").append(staleIfError).append(",");
326             }
327             if (mustUnderstand) {
328                 buf.append("must-understand").append(",");
329             }
330             if (immutable) {
331                 buf.append("immutable").append(",");
332             }
333             if (buf.charAt(buf.length() - 1) == ',') {
334                 buf.setLength(buf.length() - 1);
335             }
336             buf.append("]");
337             return buf.toString();
338     }
339 
340     public static Builder builder() {
341         return new Builder();
342     }
343 
344     public static final ResponseCacheControl DEFAULT = builder().build();
345 
346     public static class Builder {
347 
348         private long maxAge = -1;
349         private long sharedMaxAge = -1;
350         private boolean noCache;
351         private boolean noStore;
352         private boolean cachePrivate;
353         private boolean mustRevalidate;
354         private boolean proxyRevalidate;
355         private boolean cachePublic;
356         private long staleWhileRevalidate = -1;
357         private long staleIfError = -1;
358         private Set<String> noCacheFields;
359         private boolean mustUnderstand;
360         private boolean immutable;
361 
362         Builder() {
363         }
364 
365         public long getMaxAge() {
366             return maxAge;
367         }
368 
369         public Builder setMaxAge(final long maxAge) {
370             this.maxAge = maxAge;
371             return this;
372         }
373 
374         public long getSharedMaxAge() {
375             return sharedMaxAge;
376         }
377 
378         public Builder setSharedMaxAge(final long sharedMaxAge) {
379             this.sharedMaxAge = sharedMaxAge;
380             return this;
381         }
382 
383         public boolean isNoCache() {
384             return noCache;
385         }
386 
387         public Builder setNoCache(final boolean noCache) {
388             this.noCache = noCache;
389             return this;
390         }
391 
392         public boolean isNoStore() {
393             return noStore;
394         }
395 
396         public Builder setNoStore(final boolean noStore) {
397             this.noStore = noStore;
398             return this;
399         }
400 
401         public boolean isCachePrivate() {
402             return cachePrivate;
403         }
404 
405         public Builder setCachePrivate(final boolean cachePrivate) {
406             this.cachePrivate = cachePrivate;
407             return this;
408         }
409 
410         public boolean isMustRevalidate() {
411             return mustRevalidate;
412         }
413 
414         public Builder setMustRevalidate(final boolean mustRevalidate) {
415             this.mustRevalidate = mustRevalidate;
416             return this;
417         }
418 
419         public boolean isProxyRevalidate() {
420             return proxyRevalidate;
421         }
422 
423         public Builder setProxyRevalidate(final boolean proxyRevalidate) {
424             this.proxyRevalidate = proxyRevalidate;
425             return this;
426         }
427 
428         public boolean isCachePublic() {
429             return cachePublic;
430         }
431 
432         public Builder setCachePublic(final boolean cachePublic) {
433             this.cachePublic = cachePublic;
434             return this;
435         }
436 
437         public long getStaleWhileRevalidate() {
438             return staleWhileRevalidate;
439         }
440 
441         public Builder setStaleWhileRevalidate(final long staleWhileRevalidate) {
442             this.staleWhileRevalidate = staleWhileRevalidate;
443             return this;
444         }
445 
446         public long getStaleIfError() {
447             return staleIfError;
448         }
449 
450         public Builder setStaleIfError(final long staleIfError) {
451             this.staleIfError = staleIfError;
452             return this;
453         }
454 
455         public Set<String> getNoCacheFields() {
456             return noCacheFields;
457         }
458 
459         public Builder setNoCacheFields(final Set<String> noCacheFields) {
460             this.noCacheFields = noCacheFields;
461             return this;
462         }
463 
464         public Builder setNoCacheFields(final String... noCacheFields) {
465             this.noCacheFields = new HashSet<>();
466             this.noCacheFields.addAll(Arrays.asList(noCacheFields));
467             return this;
468         }
469 
470         public boolean isMustUnderstand() {
471             return mustUnderstand;
472         }
473 
474         public Builder setMustUnderstand(final boolean mustUnderstand) {
475             this.mustUnderstand = mustUnderstand;
476             return this;
477         }
478 
479         public boolean isImmutable() {
480             return immutable;
481         }
482 
483         public Builder setImmutable(final boolean immutable) {
484             this.immutable = immutable;
485             return this;
486         }
487 
488         public ResponseCacheControl build() {
489             return new ResponseCacheControl(maxAge, sharedMaxAge, mustRevalidate, noCache, noStore, cachePrivate, proxyRevalidate,
490                     cachePublic, staleWhileRevalidate, staleIfError, noCacheFields, mustUnderstand, immutable);
491         }
492 
493     }
494 
495 }