1414import io .vertx .httpproxy .spi .cache .Resource ;
1515
1616import java .time .Instant ;
17+ import java .util .ArrayList ;
1718import java .util .List ;
1819import java .util .Map ;
1920
2021class CachingFilter implements ProxyInterceptor {
2122
23+ private static final String SKIP_CACHE_RESPONSE_HANDLING = "skip_cache_response_handling" ;
24+ private static final String CACHED_RESOURCE = "cached_resource" ;
25+
2226 private final Cache cache ;
2327
2428 public CachingFilter (Cache cache ) {
@@ -32,14 +36,19 @@ public Future<ProxyResponse> handleProxyRequest(ProxyContext context) {
3236
3337 @ Override
3438 public Future <Void > handleProxyResponse (ProxyContext context ) {
35- return sendAndTryCacheProxyResponse (context );
39+ Boolean skip = context .get (SKIP_CACHE_RESPONSE_HANDLING , Boolean .class );
40+ if (skip != null && skip ) {
41+ return context .sendResponse ();
42+ } else {
43+ return sendAndTryCacheProxyResponse (context );
44+ }
3645 }
3746
3847 private Future <Void > sendAndTryCacheProxyResponse (ProxyContext context ) {
3948
4049 ProxyResponse response = context .response ();
4150 ProxyRequest request = response .request ();
42- Resource cached = context .get ("cached_resource" , Resource .class );
51+ Resource cached = context .get (CACHED_RESOURCE , Resource .class );
4352 String absoluteUri = request .absoluteURI ();
4453
4554 if (cached != null && response .getStatusCode () == 304 ) {
@@ -65,7 +74,7 @@ private Future<Void> sendAndTryCacheProxyResponse(ProxyContext context) {
6574 canCache = false ;
6675 }
6776 }
68- if (response .headers ().get (HttpHeaders .AUTHORIZATION ) != null ) {
77+ if (request .headers ().get (HttpHeaders .AUTHORIZATION ) != null ) {
6978 if (
7079 responseCacheControl == null || (
7180 !responseCacheControl .isMustRevalidate ()
@@ -78,6 +87,9 @@ private Future<Void> sendAndTryCacheProxyResponse(ProxyContext context) {
7887 if (requestCacheControl != null && requestCacheControl .isNoStore ()) {
7988 canCache = false ;
8089 }
90+ if ("*" .equals (response .headers ().get (HttpHeaders .VARY ))) {
91+ canCache = false ;
92+ }
8193 if (canCache ) {
8294 if (request .getMethod () == HttpMethod .GET ) {
8395 Resource res = new Resource (
@@ -114,9 +126,6 @@ private static MultiMap varyHeaders(MultiMap requestHeaders, MultiMap responseHe
114126 MultiMap result = MultiMap .caseInsensitiveMultiMap ();
115127 String vary = responseHeaders .get (HttpHeaders .VARY );
116128 if (vary != null ) {
117- if (vary .trim ().equals ("*" )) {
118- return result .addAll (requestHeaders );
119- }
120129 for (String toVary : vary .split ("," )) {
121130 toVary = toVary .trim ();
122131 String toVaryValue = requestHeaders .get (toVary );
@@ -149,62 +158,22 @@ private Future<ProxyResponse> tryHandleProxyRequestFromCache(ProxyContext contex
149158 return cache .get (cacheKey ).compose (resource -> {
150159 if (resource == null || !checkVaryHeaders (proxyRequest .headers (), resource .getRequestVaryHeader ())) {
151160 if (requestCacheControl != null && requestCacheControl .isOnlyIfCached ()) {
161+ context .set (SKIP_CACHE_RESPONSE_HANDLING , true );
152162 return Future .succeededFuture (proxyRequest .release ().response ().setStatusCode (504 ));
153163 }
154164 return context .sendRequest ();
155165 }
156166
157- boolean validInboundCache = false ;
158- String inboundIfModifiedSince = inboundRequest .getHeader (HttpHeaders .IF_MODIFIED_SINCE );
159- String inboundIfNoneMatch = inboundRequest .getHeader (HttpHeaders .IF_NONE_MATCH );
160- Instant resourceLastModified = resource .getLastModified ();
161- String resourceETag = resource .getEtag ();
162- if (resource .getStatusCode () == 200 ) { // TODO: status code 206
163- if (inboundIfNoneMatch != null && resourceETag != null ) {
164- String [] inboundETags = inboundIfNoneMatch .split ("," );
165- for (String inboundETag : inboundETags ) {
166- inboundETag = inboundETag .trim ();
167- if (inboundETag .equals (resourceETag )) {
168- validInboundCache = true ;
169- break ;
170- }
171- }
172- } else if (inboundIfModifiedSince != null && resourceLastModified != null ) {
173- if (ParseUtils .parseHeaderDate (inboundIfModifiedSince ).isAfter (resourceLastModified )) { // TODO: is it wrong???
174- validInboundCache = true ;
175- }
176- }
177- }
178- if (validInboundCache ) {
179- MultiMap infoHeaders = MultiMap .caseInsensitiveMultiMap ();
180- List <CharSequence > headersNeeded = List .of (
181- HttpHeaders .CACHE_CONTROL ,
182- HttpHeaders .CONTENT_LOCATION ,
183- HttpHeaders .DATE ,
184- HttpHeaders .ETAG ,
185- HttpHeaders .EXPIRES ,
186- HttpHeaders .VARY
187- );
188- for (CharSequence header : headersNeeded ) {
189- String value = resource .getHeaders ().get (header );
190- if (value != null ) infoHeaders .add (header , value );
191- }
192- ProxyResponse resp = proxyRequest .release ().response ();
193- resp .headers ().setAll (infoHeaders );
194- resp .setStatusCode (304 );
195- return Future .succeededFuture (resp );
196- }
197-
167+ // to check if the resource is fresh
198168 boolean needValidate = false ;
199169 String resourceCacheControlHeader = resource .getHeaders ().get (HttpHeaders .CACHE_CONTROL );
200170 CacheControl resourceCacheControl = resourceCacheControlHeader == null ? null : new CacheControl ().parse (resourceCacheControlHeader );
201171 if (resourceCacheControl != null && resourceCacheControl .isNoCache ()) needValidate = true ;
202172 if (requestCacheControl != null && requestCacheControl .isNoCache ()) needValidate = true ;
203173 long age = Math .subtractExact (System .currentTimeMillis (), resource .getTimestamp ()); // in ms
204174 long maxAge = Math .max (0 , resource .getMaxAge ());
205- if (resourceCacheControl != null && (resourceCacheControl .isMustRevalidate () || resourceCacheControl .isProxyRevalidate ())) {
206- if (age > maxAge ) needValidate = true ;
207- } else if (requestCacheControl != null ) {
175+ boolean responseValidateOverride = resourceCacheControl != null && (resourceCacheControl .isMustRevalidate () || resourceCacheControl .isProxyRevalidate ());
176+ if (!responseValidateOverride && requestCacheControl != null ) {
208177 if (requestCacheControl .maxAge () != -1 ) {
209178 maxAge = Math .min (maxAge , SafeMathUtils .safeMultiply (requestCacheControl .maxAge (), 1000 ));
210179 }
@@ -213,8 +182,8 @@ private Future<ProxyResponse> tryHandleProxyRequestFromCache(ProxyContext contex
213182 } else if (requestCacheControl .maxStale () != -1 ) {
214183 maxAge += SafeMathUtils .safeMultiply (requestCacheControl .maxStale (), 1000 );
215184 }
216- if (age > maxAge ) needValidate = true ;
217185 }
186+ if (age > maxAge ) needValidate = true ;
218187 String etag = resource .getHeaders ().get (HttpHeaders .ETAG );
219188 String lastModified = resource .getHeaders ().get (HttpHeaders .LAST_MODIFIED );
220189 if (needValidate ) {
@@ -224,13 +193,68 @@ private Future<ProxyResponse> tryHandleProxyRequestFromCache(ProxyContext contex
224193 if (lastModified != null ) {
225194 proxyRequest .headers ().set (HttpHeaders .IF_MODIFIED_SINCE , lastModified );
226195 }
227- context .set ("cached_resource" , resource );
196+ context .set (CACHED_RESOURCE , resource );
228197 return context .sendRequest ();
229198 } else {
230- proxyRequest .release ();
231- ProxyResponse proxyResponse = proxyRequest .response ();
232- resource .init (proxyResponse , inboundRequest .method () == HttpMethod .GET );
233- return Future .succeededFuture (proxyResponse );
199+ // check if the client already have valid cache using current cache
200+ boolean validInboundCache = false ;
201+ Instant inboundIfModifiedSince = ParseUtils .parseHeaderDate (inboundRequest .getHeader (HttpHeaders .IF_MODIFIED_SINCE ));
202+ String inboundIfNoneMatch = inboundRequest .getHeader (HttpHeaders .IF_NONE_MATCH );
203+ Instant resourceLastModified = resource .getLastModified ();
204+ Instant resourceDate = ParseUtils .parseHeaderDate (resource .getHeaders ().get (HttpHeaders .DATE ));
205+ String resourceETag = resource .getEtag ();
206+ if (resource .getStatusCode () == 200 ) {
207+ if (inboundIfNoneMatch != null ) {
208+ if (resourceETag != null ) {
209+ String [] inboundETags = inboundIfNoneMatch .split ("," );
210+ for (String inboundETag : inboundETags ) {
211+ inboundETag = inboundETag .trim ();
212+ if (inboundETag .equals (resourceETag )) {
213+ validInboundCache = true ;
214+ break ;
215+ }
216+ }
217+ }
218+ } else if (inboundIfModifiedSince != null ) {
219+ if (resourceLastModified != null ) {
220+ if (!inboundIfModifiedSince .isBefore (resourceLastModified )) {
221+ validInboundCache = true ;
222+ }
223+ } else if (resourceDate != null ) {
224+ if (!inboundIfModifiedSince .isBefore (resourceDate )) {
225+ validInboundCache = true ;
226+ }
227+ }
228+
229+ }
230+ }
231+ if (validInboundCache ) {
232+ MultiMap infoHeaders = MultiMap .caseInsensitiveMultiMap ();
233+ List <CharSequence > headersNeeded = new ArrayList <>(List .of (
234+ HttpHeaders .CACHE_CONTROL ,
235+ HttpHeaders .CONTENT_LOCATION ,
236+ HttpHeaders .DATE ,
237+ HttpHeaders .ETAG ,
238+ HttpHeaders .EXPIRES ,
239+ HttpHeaders .VARY
240+ ));
241+ if (inboundIfNoneMatch == null ) headersNeeded .add (HttpHeaders .LAST_MODIFIED );
242+ for (CharSequence header : headersNeeded ) {
243+ String value = resource .getHeaders ().get (header );
244+ if (value != null ) infoHeaders .add (header , value );
245+ }
246+ ProxyResponse resp = proxyRequest .release ().response ();
247+ resp .headers ().setAll (infoHeaders );
248+ resp .setStatusCode (304 );
249+ context .set (SKIP_CACHE_RESPONSE_HANDLING , true );
250+ return Future .succeededFuture (resp );
251+ } else {
252+ proxyRequest .release ();
253+ ProxyResponse proxyResponse = proxyRequest .response ();
254+ resource .init (proxyResponse , inboundRequest .method () == HttpMethod .GET );
255+ context .set (SKIP_CACHE_RESPONSE_HANDLING , true );
256+ return Future .succeededFuture (proxyResponse );
257+ }
234258 }
235259
236260 });
@@ -240,11 +264,9 @@ private Future<ProxyResponse> tryHandleProxyRequestFromCache(ProxyContext contex
240264
241265 private static boolean checkVaryHeaders (MultiMap requestHeaders , MultiMap varyHeaders ) {
242266 for (Map .Entry <String , String > e : varyHeaders ) {
243- String fromVary = e .getValue (). toLowerCase () ;
267+ String fromVary = e .getValue ();
244268 String fromRequest = requestHeaders .get (e .getKey ());
245- if (fromRequest == null ) return false ;
246- fromRequest = fromVary .toLowerCase ();
247- if (!fromRequest .equals (fromVary )) return false ;
269+ if (fromRequest == null || !fromRequest .equals (fromVary )) return false ;
248270 }
249271 return true ;
250272 }
0 commit comments