Skip to content

Commit a1ec766

Browse files
committed
Merge pull request #33366 from kashike
* pr/33366: Polish "Make CacheControl immutable" Make CacheControl immutable Closes gh-33366
2 parents e27192e + 9f22e0c commit a1ec766

File tree

1 file changed

+102
-76
lines changed

1 file changed

+102
-76
lines changed

spring-web/src/main/java/org/springframework/http/CacheControl.java

+102-76
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,7 +23,8 @@
2323
import org.springframework.util.StringUtils;
2424

2525
/**
26-
* A builder for creating "Cache-Control" HTTP response headers.
26+
* A builder for creating "Cache-Control" HTTP response headers. As of Spring
27+
* Framework 6.2, this class is immutable.
2728
*
2829
* <p>Adding Cache-Control directives to HTTP responses can significantly improve the client
2930
* experience when interacting with a web application. This builder creates opinionated
@@ -51,55 +52,74 @@
5152
*/
5253
public class CacheControl {
5354

55+
private static final CacheControl EMPTY = new CacheControl();
56+
5457
@Nullable
55-
private Duration maxAge;
58+
private final Duration maxAge;
5659

57-
private boolean noCache = false;
60+
private final boolean noCache;
5861

59-
private boolean noStore = false;
62+
private final boolean noStore;
6063

61-
private boolean mustRevalidate = false;
64+
private final boolean mustRevalidate;
6265

63-
private boolean noTransform = false;
66+
private final boolean noTransform;
6467

65-
private boolean cachePublic = false;
68+
private final boolean cachePublic;
6669

67-
private boolean cachePrivate = false;
70+
private final boolean cachePrivate;
6871

69-
private boolean proxyRevalidate = false;
72+
private final boolean proxyRevalidate;
7073

7174
@Nullable
72-
private Duration staleWhileRevalidate;
75+
private final Duration staleWhileRevalidate;
7376

7477
@Nullable
75-
private Duration staleIfError;
78+
private final Duration staleIfError;
7679

7780
@Nullable
78-
private Duration sMaxAge;
79-
80-
private boolean immutable = false;
81+
private final Duration sMaxAge;
8182

83+
private final boolean immutable;
8284

8385
/**
8486
* Create an empty CacheControl instance.
8587
* @see #empty()
8688
*/
8789
protected CacheControl() {
90+
this(null, false, false, false, false, false, false, false, null, null, null, false);
8891
}
8992

93+
private CacheControl(@Nullable Duration maxAge, boolean noCache, boolean noStore,
94+
boolean mustRevalidate, boolean noTransform, boolean cachePublic,
95+
boolean cachePrivate, boolean proxyRevalidate, @Nullable Duration staleWhileRevalidate,
96+
@Nullable Duration staleIfError, @Nullable Duration sMaxAge, boolean immutable) {
97+
this.maxAge = maxAge;
98+
this.noCache = noCache;
99+
this.noStore = noStore;
100+
this.mustRevalidate = mustRevalidate;
101+
this.noTransform = noTransform;
102+
this.cachePublic = cachePublic;
103+
this.cachePrivate = cachePrivate;
104+
this.proxyRevalidate = proxyRevalidate;
105+
this.staleWhileRevalidate = staleWhileRevalidate;
106+
this.staleIfError = staleIfError;
107+
this.sMaxAge = sMaxAge;
108+
this.immutable = immutable;
109+
}
90110

91111
/**
92112
* Return an empty directive.
93113
* <p>This is well suited for using other optional directives without "max-age",
94114
* "no-cache" or "no-store".
95-
* @return {@code this}, to facilitate method chaining
115+
* @return en empty directive
96116
*/
97117
public static CacheControl empty() {
98-
return new CacheControl();
118+
return EMPTY;
99119
}
100120

101121
/**
102-
* Add a "max-age=" directive.
122+
* Return a "max-age=" directive.
103123
* <p>This directive is well suited for publicly caching resources, knowing that
104124
* they won't change within the configured amount of time. Additional directives
105125
* can be also used, in case resources shouldn't be cached ({@link #cachePrivate()})
@@ -109,7 +129,7 @@ public static CacheControl empty() {
109129
* directive should be set ({@link #mustRevalidate()}
110130
* @param maxAge the maximum time the response should be cached
111131
* @param unit the time unit of the {@code maxAge} argument
112-
* @return {@code this}, to facilitate method chaining
132+
* @return a CacheControl instance with a "max-age" directive
113133
* @see #maxAge(Duration)
114134
* @see <a href="https://tools.ietf.org/html/rfc7234#section-5.2.2.8">rfc7234 section 5.2.2.8</a>
115135
*/
@@ -118,7 +138,7 @@ public static CacheControl maxAge(long maxAge, TimeUnit unit) {
118138
}
119139

120140
/**
121-
* Add a "max-age=" directive.
141+
* Return a "max-age=" directive.
122142
* <p>This directive is well suited for publicly caching resources, knowing that
123143
* they won't change within the configured amount of time. Additional directives
124144
* can be also used, in case resources shouldn't be cached ({@link #cachePrivate()})
@@ -127,118 +147,120 @@ public static CacheControl maxAge(long maxAge, TimeUnit unit) {
127147
* become stale (i.e. the "max-age" delay is passed), the "must-revalidate"
128148
* directive should be set ({@link #mustRevalidate()}
129149
* @param maxAge the maximum time the response should be cached
130-
* @return {@code this}, to facilitate method chaining
150+
* @return a CacheControl instance with a "max-age" directive
131151
* @since 5.2
132152
* @see <a href="https://tools.ietf.org/html/rfc7234#section-5.2.2.8">rfc7234 section 5.2.2.8</a>
133153
*/
134154
public static CacheControl maxAge(Duration maxAge) {
135-
CacheControl cc = new CacheControl();
136-
cc.maxAge = maxAge;
137-
return cc;
155+
return new CacheControl(maxAge, false, false, false, false, false, false, false,
156+
null, null, null, false);
138157
}
139158

140159
/**
141-
* Add a "no-cache" directive.
160+
* Return a "no-cache" directive.
142161
* <p>This directive is well suited for telling caches that the response
143162
* can be reused only if the client revalidates it with the server.
144163
* This directive won't disable cache altogether and may result with clients
145164
* sending conditional requests (with "ETag", "If-Modified-Since" headers)
146165
* and the server responding with "304 - Not Modified" status.
147166
* <p>In order to disable caching and minimize requests/responses exchanges,
148167
* the {@link #noStore()} directive should be used instead of {@code #noCache()}.
149-
* @return {@code this}, to facilitate method chaining
168+
* @return a CacheControl instance with a "no-cache" directive
150169
* @see <a href="https://tools.ietf.org/html/rfc7234#section-5.2.2.2">rfc7234 section 5.2.2.2</a>
151170
*/
152171
public static CacheControl noCache() {
153-
CacheControl cc = new CacheControl();
154-
cc.noCache = true;
155-
return cc;
172+
return new CacheControl(null, true, false, false, false, false, false, false,
173+
null, null, null, false);
156174
}
157175

158176
/**
159-
* Add a "no-store" directive.
177+
* Return a "no-store" directive.
160178
* <p>This directive is well suited for preventing caches (browsers and proxies)
161179
* to cache the content of responses.
162-
* @return {@code this}, to facilitate method chaining
180+
* @return a CacheControl instance with a "no-store" directive
163181
* @see <a href="https://tools.ietf.org/html/rfc7234#section-5.2.2.3">rfc7234 section 5.2.2.3</a>
164182
*/
165183
public static CacheControl noStore() {
166-
CacheControl cc = new CacheControl();
167-
cc.noStore = true;
168-
return cc;
184+
return new CacheControl(null, false, true, false, false, false, false, false,
185+
null, null, null, false);
169186
}
170187

171188

172189
/**
173-
* Add a "must-revalidate" directive.
190+
* Return a new instance with an additional "must-revalidate" directive.
174191
* <p>This directive indicates that once it has become stale, a cache MUST NOT
175192
* use the response to satisfy subsequent requests without successful validation
176193
* on the origin server.
177-
* @return {@code this}, to facilitate method chaining
194+
* @return a new CacheControl instance with an additional "must-revalidate" directive
178195
* @see <a href="https://tools.ietf.org/html/rfc7234#section-5.2.2.1">rfc7234 section 5.2.2.1</a>
179196
*/
180197
public CacheControl mustRevalidate() {
181-
this.mustRevalidate = true;
182-
return this;
198+
return new CacheControl(this.maxAge, this.noCache, this.noStore, true, this.noTransform,
199+
this.cachePublic, this.cachePrivate, this.proxyRevalidate, this.staleWhileRevalidate,
200+
this.staleIfError, this.sMaxAge, this.immutable);
183201
}
184202

185203
/**
186-
* Add a "no-transform" directive.
204+
* Return a new instance with an additional "no-transform" directive.
187205
* <p>This directive indicates that intermediaries (caches and others) should
188206
* not transform the response content. This can be useful to force caches and
189207
* CDNs not to automatically gzip or optimize the response content.
190-
* @return {@code this}, to facilitate method chaining
208+
* @return a new CacheControl instance with an additional "no-transform" directive
191209
* @see <a href="https://tools.ietf.org/html/rfc7234#section-5.2.2.4">rfc7234 section 5.2.2.4</a>
192210
*/
193211
public CacheControl noTransform() {
194-
this.noTransform = true;
195-
return this;
212+
return new CacheControl(this.maxAge, this.noCache, this.noStore, this.mustRevalidate, true,
213+
this.cachePublic, this.cachePrivate, this.proxyRevalidate, this.staleWhileRevalidate,
214+
this.staleIfError, this.sMaxAge, this.immutable);
196215
}
197216

198217
/**
199-
* Add a "public" directive.
218+
* Return a new instance with an additional "public" directive.
200219
* <p>This directive indicates that any cache MAY store the response,
201220
* even if the response would normally be non-cacheable or cacheable
202221
* only within a private cache.
203-
* @return {@code this}, to facilitate method chaining
222+
* @return a new CacheControl instance with an additional "public" directive
204223
* @see <a href="https://tools.ietf.org/html/rfc7234#section-5.2.2.5">rfc7234 section 5.2.2.5</a>
205224
*/
206225
public CacheControl cachePublic() {
207-
this.cachePublic = true;
208-
return this;
226+
return new CacheControl(this.maxAge, this.noCache, this.noStore, this.mustRevalidate, this.noTransform,
227+
true, this.cachePrivate, this.proxyRevalidate, this.staleWhileRevalidate,
228+
this.staleIfError, this.sMaxAge, this.immutable);
209229
}
210230

211231
/**
212-
* Add a "private" directive.
232+
* Return a new instance with an additional "private" directive.
213233
* <p>This directive indicates that the response message is intended
214234
* for a single user and MUST NOT be stored by a shared cache.
215-
* @return {@code this}, to facilitate method chaining
235+
* @return a new CacheControl instance with an additional "private" directive
216236
* @see <a href="https://tools.ietf.org/html/rfc7234#section-5.2.2.6">rfc7234 section 5.2.2.6</a>
217237
*/
218238
public CacheControl cachePrivate() {
219-
this.cachePrivate = true;
220-
return this;
239+
return new CacheControl(this.maxAge, this.noCache, this.noStore, this.mustRevalidate, this.noTransform,
240+
this.cachePublic, true, this.proxyRevalidate, this.staleWhileRevalidate,
241+
this.staleIfError, this.sMaxAge, this.immutable);
221242
}
222243

223244
/**
224-
* Add a "proxy-revalidate" directive.
245+
* Return a new instance with an additional "proxy-revalidate" directive.
225246
* <p>This directive has the same meaning as the "must-revalidate" directive,
226247
* except that it does not apply to private caches (i.e. browsers, HTTP clients).
227-
* @return {@code this}, to facilitate method chaining
248+
* @return a new CacheControl instance with an additional "proxy-revalidate" directive
228249
* @see <a href="https://tools.ietf.org/html/rfc7234#section-5.2.2.7">rfc7234 section 5.2.2.7</a>
229250
*/
230251
public CacheControl proxyRevalidate() {
231-
this.proxyRevalidate = true;
232-
return this;
252+
return new CacheControl(this.maxAge, this.noCache, this.noStore, this.mustRevalidate, this.noTransform,
253+
this.cachePublic, this.cachePrivate, true, this.staleWhileRevalidate,
254+
this.staleIfError, this.sMaxAge, this.immutable);
233255
}
234256

235257
/**
236-
* Add an "s-maxage" directive.
258+
* Return a new instance with an additional "s-maxage" directive.
237259
* <p>This directive indicates that, in shared caches, the maximum age specified
238260
* by this directive overrides the maximum age specified by other directives.
239261
* @param sMaxAge the maximum time the response should be cached
240262
* @param unit the time unit of the {@code sMaxAge} argument
241-
* @return {@code this}, to facilitate method chaining
263+
* @return a new CacheControl instance with an additional "s-maxage" directive
242264
* @see #sMaxAge(Duration)
243265
* @see <a href="https://tools.ietf.org/html/rfc7234#section-5.2.2.9">rfc7234 section 5.2.2.9</a>
244266
*/
@@ -247,29 +269,30 @@ public CacheControl sMaxAge(long sMaxAge, TimeUnit unit) {
247269
}
248270

249271
/**
250-
* Add an "s-maxage" directive.
272+
* Return a new instance with an additional "s-maxage" directive.
251273
* <p>This directive indicates that, in shared caches, the maximum age specified
252274
* by this directive overrides the maximum age specified by other directives.
253275
* @param sMaxAge the maximum time the response should be cached
254-
* @return {@code this}, to facilitate method chaining
276+
* @return a new CacheControl instance with an additional "s-maxage" directive
255277
* @since 5.2
256278
* @see <a href="https://tools.ietf.org/html/rfc7234#section-5.2.2.9">rfc7234 section 5.2.2.9</a>
257279
*/
258280
public CacheControl sMaxAge(Duration sMaxAge) {
259-
this.sMaxAge = sMaxAge;
260-
return this;
281+
return new CacheControl(this.maxAge, this.noCache, this.noStore, this.mustRevalidate, this.noTransform,
282+
this.cachePublic, this.cachePrivate, this.proxyRevalidate, this.staleWhileRevalidate,
283+
this.staleIfError, sMaxAge, this.immutable);
261284
}
262285

263286
/**
264-
* Add a "stale-while-revalidate" directive.
287+
* Return a new instance with an additional "stale-while-revalidate" directive.
265288
* <p>This directive indicates that caches MAY serve the response in which it
266289
* appears after it becomes stale, up to the indicated number of seconds.
267290
* If a cached response is served stale due to the presence of this extension,
268291
* the cache SHOULD attempt to revalidate it while still serving stale responses
269292
* (i.e. without blocking).
270293
* @param staleWhileRevalidate the maximum time the response should be used while being revalidated
271294
* @param unit the time unit of the {@code staleWhileRevalidate} argument
272-
* @return {@code this}, to facilitate method chaining
295+
* @return a new CacheControl instance with an additional "stale-while-revalidate" directive
273296
* @see #staleWhileRevalidate(Duration)
274297
* @see <a href="https://tools.ietf.org/html/rfc5861#section-3">rfc5861 section 3</a>
275298
*/
@@ -278,29 +301,30 @@ public CacheControl staleWhileRevalidate(long staleWhileRevalidate, TimeUnit uni
278301
}
279302

280303
/**
281-
* Add a "stale-while-revalidate" directive.
304+
* Return a new instance with an additional "stale-while-revalidate" directive.
282305
* <p>This directive indicates that caches MAY serve the response in which it
283306
* appears after it becomes stale, up to the indicated number of seconds.
284307
* If a cached response is served stale due to the presence of this extension,
285308
* the cache SHOULD attempt to revalidate it while still serving stale responses
286309
* (i.e. without blocking).
287310
* @param staleWhileRevalidate the maximum time the response should be used while being revalidated
288-
* @return {@code this}, to facilitate method chaining
311+
* @return a new CacheControl instance with an additional "stale-while-revalidate" directive
289312
* @since 5.2
290313
* @see <a href="https://tools.ietf.org/html/rfc5861#section-3">rfc5861 section 3</a>
291314
*/
292315
public CacheControl staleWhileRevalidate(Duration staleWhileRevalidate) {
293-
this.staleWhileRevalidate = staleWhileRevalidate;
294-
return this;
316+
return new CacheControl(this.maxAge, this.noCache, this.noStore, this.mustRevalidate, this.noTransform,
317+
this.cachePublic, this.cachePrivate, this.proxyRevalidate, staleWhileRevalidate,
318+
this.staleIfError, this.sMaxAge, this.immutable);
295319
}
296320

297321
/**
298-
* Add a "stale-if-error" directive.
322+
* Return a new instance with an additional "stale-if-error" directive.
299323
* <p>This directive indicates that when an error is encountered, a cached stale response
300324
* MAY be used to satisfy the request, regardless of other freshness information.
301325
* @param staleIfError the maximum time the response should be used when errors are encountered
302326
* @param unit the time unit of the {@code staleIfError} argument
303-
* @return {@code this}, to facilitate method chaining
327+
* @return a new CacheControl instance with an additional "stale-if-error" directive
304328
* @see #staleIfError(Duration)
305329
* @see <a href="https://tools.ietf.org/html/rfc5861#section-4">rfc5861 section 4</a>
306330
*/
@@ -309,32 +333,34 @@ public CacheControl staleIfError(long staleIfError, TimeUnit unit) {
309333
}
310334

311335
/**
312-
* Add a "stale-if-error" directive.
336+
* Return a new instance with an additional "stale-if-error" directive.
313337
* <p>This directive indicates that when an error is encountered, a cached stale response
314338
* MAY be used to satisfy the request, regardless of other freshness information.
315339
* @param staleIfError the maximum time the response should be used when errors are encountered
316-
* @return {@code this}, to facilitate method chaining
340+
* @return a new CacheControl instance with an additional "stale-if-error" directive
317341
* @since 5.2
318342
* @see <a href="https://tools.ietf.org/html/rfc5861#section-4">rfc5861 section 4</a>
319343
*/
320344
public CacheControl staleIfError(Duration staleIfError) {
321-
this.staleIfError = staleIfError;
322-
return this;
345+
return new CacheControl(this.maxAge, this.noCache, this.noStore, this.mustRevalidate, this.noTransform,
346+
this.cachePublic, this.cachePrivate, this.proxyRevalidate, this.staleWhileRevalidate,
347+
staleIfError, this.sMaxAge, this.immutable);
323348
}
324349

325350
/**
326-
* Add an "immutable" directive.
351+
* Return a new instance with an additional "immutable" directive.
327352
* <p>This directive indicates that the origin server will not update the
328353
* representation of that resource during the freshness lifetime of the response.
329354
* Adding a {@link #maxAge(Duration) max-age} directive is strongly advised
330355
* to enforce the actual freshness lifetime.
331-
* @return {@code this}, to facilitate method chaining
356+
* @return a new CacheControl instance with an additional "immutable" directive
332357
* @since 6.0.5
333358
* @see <a href="https://tools.ietf.org/html/rfc8246">rfc8246</a>
334359
*/
335360
public CacheControl immutable() {
336-
this.immutable = true;
337-
return this;
361+
return new CacheControl(this.maxAge, this.noCache, this.noStore, this.mustRevalidate, this.noTransform,
362+
this.cachePublic, this.cachePrivate, this.proxyRevalidate, this.staleWhileRevalidate,
363+
this.staleIfError, this.sMaxAge, true);
338364
}
339365

340366
/**

0 commit comments

Comments
 (0)