3535
3636/**
3737 * <p>
38- * Filter responsible for browser caching.
38+ * Filter allowing to enable browser caching.
3939 * </p>
4040 * <table border="1">
4141 * <tr>
4242 * <th>Option</th>
4343 * <th>Required</th>
4444 * <th>Default</th>
45+ * <th>Since</th>
4546 * <th>Description</th>
4647 * </tr>
4748 * <tr>
48- * <td>static</td>
49- * <td>No</td>
50- * <td>false</td>
51- * <td>Defines whether a component is static or not. Conditional requests <sup>(1)</sup> are not required for static
52- * components.</td>
49+ * <td>{@code expiration}</td>
50+ * <td>Yes</td>
51+ * <td>--</td>
52+ * <td>2.2.0</td>
53+ * <td>Cache directive to set an expiration time, in seconds, relative to the current date. Used for both
54+ * {@code Cache-Control} and {@code Expires} HTTP headers.</td>
5355 * </tr>
5456 * <tr>
55- * <td>private</td>
57+ * <td>{@code private} </td>
5658 * <td>No</td>
57- * <td>false</td>
59+ * <td>{@code false}</td>
60+ * <td>2.0.0</td>
5861 * <td>Cache directive to control where the response may be cached.
5962 * <ul>
6063 * <li><code>false</code> indicates that the response MAY be cached by any cache.</li>
6467 * </td>
6568 * </tr>
6669 * <tr>
67- * <td>expirationTime</td>
68- * <td>Yes</td>
69- * <td>-</td>
70- * <td>Cache directive to set an expiration time, in seconds, relative to the current date.</td>
70+ * <td>{@code must-revalidate}</td>
71+ * <td>No</td>
72+ * <td>{@code false}</td>
73+ * <td>2.2.0</td>
74+ * <td>Cache directive to define whether conditional requests<sup>(1)</sup> are required or not for stale responses.
75+ * When the {@code must-revalidate} directive is present in a response received by a cache, that cache MUST NOT use the
76+ * entry after it becomes stale to respond to a subsequent request without first revalidating it with the origin server.
77+ * </td>
78+ * </tr>
79+ * <tr>
80+ * <td>{@code vary}</td>
81+ * <td>No</td>
82+ * <td>--</td>
83+ * <td>2.3.0</td>
84+ * <td>Cache directive to instructs proxies to cache different versions of the same resource based on specific
85+ * request-header fields. Some possible {@code Vary} header values include:
86+ * <ul>
87+ * <li><code>Accept-Encoding</code> instructs proxies to cache two versions of a compressible resource: one compressed,
88+ * and one uncompressed.</li>
89+ * <li><code>Accept-Language</code> instructs proxies to cache different versions of a resource based on the language of
90+ * a given request.</li>
91+ * <li><code>Accept</code> instructs proxies to cache different versions of a resource based on the response format of a
92+ * given request (e.g. {@code Accept: application/xml} or {@code Accept: application/json}).</li>
93+ * </ul>
94+ * <strong>Warn:</strong> if your server does vary responses but does not indicate so via the {@code Vary} header it may
95+ * result in cache corruption: <a href="http://www.subbu.org/blog/2007/12/vary-header-for-restful-applications">Vary
96+ * Header for RESTful Applications</a>.</td>
7197 * </tr>
7298 * </table>
7399 * <p>
74100 * <sup>(1)</sup> If a component is already in the browser's cache and is being re-requested, the browser will pass the
75- * Last-Modified date in the request header. This is called a conditional GET request and if the component has not been
76- * modified, the server will return a 304 Not Modified response.
101+ * {@code Last-Modified} date in the request header. This is called a <em> conditional GET request</em> and if the
102+ * component has not been modified, the server will return a {@code 304 Not Modified} response.
77103 * </p>
104+ * <h2>Sample configuration:</h2>
78105 * <p>
79- * Example configuration :
106+ * Declare the filter in your web descriptor file {@code web.xml} :
80107 * </p>
81108 *
82109 * <pre>
83110 * <filter>
84111 * <filter-name>imagesCache</filter-name>
85112 * <filter-class>com.samaxes.filter.CacheFilter</filter-class>
86113 * <init-param>
87- * <param-name>static</param-name>
88- * <param-value>true</param-value>
89- * </init-param>
90- * <init-param>
91- * <param-name>expirationTime</param-name>
114+ * <param-name>expiration</param-name>
92115 * <param-value>2592000</param-value>
93116 * </init-param>
94117 * </filter>
97120 * <filter-name>cssCache</filter-name>
98121 * <filter-class>com.samaxes.filter.CacheFilter</filter-class>
99122 * <init-param>
100- * <param-name>expirationTime </param-name>
123+ * <param-name>expiration </param-name>
101124 * <param-value>604800</param-value>
102125 * </init-param>
126+ * <init-param>
127+ * <param-name>vary</param-name>
128+ * <param-value>Accept-Encoding</param-value>
129+ * </init-param>
103130 * </filter>
104131 *
105132 * <filter>
106133 * <filter-name>jsCache</filter-name>
107134 * <filter-class>com.samaxes.filter.CacheFilter</filter-class>
108135 * <init-param>
109- * <param-name>private </param-name>
110- * <param-value>true </param-value>
136+ * <param-name>expiration </param-name>
137+ * <param-value>216000 </param-value>
111138 * </init-param>
112139 * <init-param>
113- * <param-name>expirationTime </param-name>
114- * <param-value>216000 </param-value>
140+ * <param-name>private </param-name>
141+ * <param-value>true </param-value>
115142 * </init-param>
116143 * </filter>
117144 * </pre>
138165 *
139166 * @author Samuel Santos
140167 * @author John Yeary
141- * @version 2.2 .0
168+ * @version 2.3 .0
142169 */
143170public class CacheFilter implements Filter {
144171
@@ -148,23 +175,13 @@ public class CacheFilter implements Filter {
148175
149176 private boolean mustRevalidate ;
150177
178+ private String vary ;
179+
151180 /**
152181 * {@inheritDoc}
153182 */
154183 @ Override
155184 public void init (FilterConfig filterConfig ) throws ServletException {
156- if (filterConfig .getInitParameter ("expirationTime" ) != null ) {
157- throw new ServletException (new StringBuilder (
158- "The initialization parameter expirationTime has been replaced with " )
159- .append (CacheConfigParameter .EXPIRATION .getName ()).append (" for the filter " )
160- .append (filterConfig .getFilterName ()).append ("." ).toString ());
161- }
162- if (filterConfig .getInitParameter ("static" ) != null ) {
163- throw new ServletException (new StringBuilder ("The initialization parameter static has been replaced with " )
164- .append (CacheConfigParameter .MUST_REVALIDATE .getName ()).append (" for the filter " )
165- .append (filterConfig .getFilterName ()).append ("." ).toString ());
166- }
167-
168185 try {
169186 expiration = Long .valueOf (filterConfig .getInitParameter (CacheConfigParameter .EXPIRATION .getName ()));
170187 } catch (NumberFormatException e ) {
@@ -176,6 +193,7 @@ public void init(FilterConfig filterConfig) throws ServletException {
176193 cacheability = Boolean .valueOf (filterConfig .getInitParameter (CacheConfigParameter .PRIVATE .getName ())) ? Cacheability .PRIVATE
177194 : Cacheability .PUBLIC ;
178195 mustRevalidate = Boolean .valueOf (filterConfig .getInitParameter (CacheConfigParameter .MUST_REVALIDATE .getName ()));
196+ vary = filterConfig .getInitParameter (CacheConfigParameter .VARY .getName ());
179197 }
180198
181199 /**
@@ -198,6 +216,11 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
198216 httpServletResponse .setDateHeader (HTTPCacheHeader .EXPIRES .getName (), System .currentTimeMillis () + expiration
199217 * 1000L );
200218
219+ // Set Vary field
220+ if (vary != null && !vary .isEmpty ()) {
221+ httpServletResponse .setHeader (HTTPCacheHeader .VARY .getName (), vary );
222+ }
223+
201224 /*
202225 * By default, some servers (e.g. Tomcat) will set headers on any SSL content to deny caching. Omitting the
203226 * Pragma header takes care of user-agents implementing HTTP/1.0.
0 commit comments