14
14
import io .vertx .httpproxy .spi .cache .Resource ;
15
15
16
16
import java .time .Instant ;
17
+ import java .util .ArrayList ;
17
18
import java .util .List ;
18
19
import java .util .Map ;
19
20
20
21
class CachingFilter implements ProxyInterceptor {
21
22
23
+ private static final String SKIP_CACHE_RESPONSE_HANDLING = "skip_cache_response_handling" ;
24
+ private static final String CACHED_RESOURCE = "cached_resource" ;
25
+
22
26
private final Cache cache ;
23
27
24
28
public CachingFilter (Cache cache ) {
@@ -32,14 +36,19 @@ public Future<ProxyResponse> handleProxyRequest(ProxyContext context) {
32
36
33
37
@ Override
34
38
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
+ }
36
45
}
37
46
38
47
private Future <Void > sendAndTryCacheProxyResponse (ProxyContext context ) {
39
48
40
49
ProxyResponse response = context .response ();
41
50
ProxyRequest request = response .request ();
42
- Resource cached = context .get ("cached_resource" , Resource .class );
51
+ Resource cached = context .get (CACHED_RESOURCE , Resource .class );
43
52
String absoluteUri = request .absoluteURI ();
44
53
45
54
if (cached != null && response .getStatusCode () == 304 ) {
@@ -65,7 +74,7 @@ private Future<Void> sendAndTryCacheProxyResponse(ProxyContext context) {
65
74
canCache = false ;
66
75
}
67
76
}
68
- if (response .headers ().get (HttpHeaders .AUTHORIZATION ) != null ) {
77
+ if (request .headers ().get (HttpHeaders .AUTHORIZATION ) != null ) {
69
78
if (
70
79
responseCacheControl == null || (
71
80
!responseCacheControl .isMustRevalidate ()
@@ -78,6 +87,9 @@ private Future<Void> sendAndTryCacheProxyResponse(ProxyContext context) {
78
87
if (requestCacheControl != null && requestCacheControl .isNoStore ()) {
79
88
canCache = false ;
80
89
}
90
+ if ("*" .equals (response .headers ().get (HttpHeaders .VARY ))) {
91
+ canCache = false ;
92
+ }
81
93
if (canCache ) {
82
94
if (request .getMethod () == HttpMethod .GET ) {
83
95
Resource res = new Resource (
@@ -114,9 +126,6 @@ private static MultiMap varyHeaders(MultiMap requestHeaders, MultiMap responseHe
114
126
MultiMap result = MultiMap .caseInsensitiveMultiMap ();
115
127
String vary = responseHeaders .get (HttpHeaders .VARY );
116
128
if (vary != null ) {
117
- if (vary .trim ().equals ("*" )) {
118
- return result .addAll (requestHeaders );
119
- }
120
129
for (String toVary : vary .split ("," )) {
121
130
toVary = toVary .trim ();
122
131
String toVaryValue = requestHeaders .get (toVary );
@@ -149,62 +158,22 @@ private Future<ProxyResponse> tryHandleProxyRequestFromCache(ProxyContext contex
149
158
return cache .get (cacheKey ).compose (resource -> {
150
159
if (resource == null || !checkVaryHeaders (proxyRequest .headers (), resource .getRequestVaryHeader ())) {
151
160
if (requestCacheControl != null && requestCacheControl .isOnlyIfCached ()) {
161
+ context .set (SKIP_CACHE_RESPONSE_HANDLING , true );
152
162
return Future .succeededFuture (proxyRequest .release ().response ().setStatusCode (504 ));
153
163
}
154
164
return context .sendRequest ();
155
165
}
156
166
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
198
168
boolean needValidate = false ;
199
169
String resourceCacheControlHeader = resource .getHeaders ().get (HttpHeaders .CACHE_CONTROL );
200
170
CacheControl resourceCacheControl = resourceCacheControlHeader == null ? null : new CacheControl ().parse (resourceCacheControlHeader );
201
171
if (resourceCacheControl != null && resourceCacheControl .isNoCache ()) needValidate = true ;
202
172
if (requestCacheControl != null && requestCacheControl .isNoCache ()) needValidate = true ;
203
173
long age = Math .subtractExact (System .currentTimeMillis (), resource .getTimestamp ()); // in ms
204
174
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 ) {
208
177
if (requestCacheControl .maxAge () != -1 ) {
209
178
maxAge = Math .min (maxAge , SafeMathUtils .safeMultiply (requestCacheControl .maxAge (), 1000 ));
210
179
}
@@ -213,8 +182,8 @@ private Future<ProxyResponse> tryHandleProxyRequestFromCache(ProxyContext contex
213
182
} else if (requestCacheControl .maxStale () != -1 ) {
214
183
maxAge += SafeMathUtils .safeMultiply (requestCacheControl .maxStale (), 1000 );
215
184
}
216
- if (age > maxAge ) needValidate = true ;
217
185
}
186
+ if (age > maxAge ) needValidate = true ;
218
187
String etag = resource .getHeaders ().get (HttpHeaders .ETAG );
219
188
String lastModified = resource .getHeaders ().get (HttpHeaders .LAST_MODIFIED );
220
189
if (needValidate ) {
@@ -224,13 +193,68 @@ private Future<ProxyResponse> tryHandleProxyRequestFromCache(ProxyContext contex
224
193
if (lastModified != null ) {
225
194
proxyRequest .headers ().set (HttpHeaders .IF_MODIFIED_SINCE , lastModified );
226
195
}
227
- context .set ("cached_resource" , resource );
196
+ context .set (CACHED_RESOURCE , resource );
228
197
return context .sendRequest ();
229
198
} 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
+ }
234
258
}
235
259
236
260
});
@@ -240,11 +264,9 @@ private Future<ProxyResponse> tryHandleProxyRequestFromCache(ProxyContext contex
240
264
241
265
private static boolean checkVaryHeaders (MultiMap requestHeaders , MultiMap varyHeaders ) {
242
266
for (Map .Entry <String , String > e : varyHeaders ) {
243
- String fromVary = e .getValue (). toLowerCase () ;
267
+ String fromVary = e .getValue ();
244
268
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 ;
248
270
}
249
271
return true ;
250
272
}
0 commit comments