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,63 +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
- return context .sendRequest ();
172
- }
173
- } else if (inboundIfModifiedSince != null && resourceLastModified != null ) {
174
- if (ParseUtils .parseHeaderDate (inboundIfModifiedSince ).isAfter (resourceLastModified )) { // TODO: is it wrong???
175
- validInboundCache = true ;
176
- }
177
- }
178
- }
179
- if (validInboundCache ) {
180
- MultiMap infoHeaders = MultiMap .caseInsensitiveMultiMap ();
181
- List <CharSequence > headersNeeded = List .of (
182
- HttpHeaders .CACHE_CONTROL ,
183
- HttpHeaders .CONTENT_LOCATION ,
184
- HttpHeaders .DATE ,
185
- HttpHeaders .ETAG ,
186
- HttpHeaders .EXPIRES ,
187
- HttpHeaders .VARY
188
- );
189
- for (CharSequence header : headersNeeded ) {
190
- String value = resource .getHeaders ().get (header );
191
- if (value != null ) infoHeaders .add (header , value );
192
- }
193
- ProxyResponse resp = proxyRequest .release ().response ();
194
- resp .headers ().setAll (infoHeaders );
195
- resp .setStatusCode (304 );
196
- return Future .succeededFuture (resp );
197
- }
198
-
167
+ // to check if the resource is fresh
199
168
boolean needValidate = false ;
200
169
String resourceCacheControlHeader = resource .getHeaders ().get (HttpHeaders .CACHE_CONTROL );
201
170
CacheControl resourceCacheControl = resourceCacheControlHeader == null ? null : new CacheControl ().parse (resourceCacheControlHeader );
202
171
if (resourceCacheControl != null && resourceCacheControl .isNoCache ()) needValidate = true ;
203
172
if (requestCacheControl != null && requestCacheControl .isNoCache ()) needValidate = true ;
204
173
long age = Math .subtractExact (System .currentTimeMillis (), resource .getTimestamp ()); // in ms
205
174
long maxAge = Math .max (0 , resource .getMaxAge ());
206
- if (resourceCacheControl != null && (resourceCacheControl .isMustRevalidate () || resourceCacheControl .isProxyRevalidate ())) {
207
- if (age > maxAge ) needValidate = true ;
208
- } else if (requestCacheControl != null ) {
175
+ boolean responseValidateOverride = resourceCacheControl != null && (resourceCacheControl .isMustRevalidate () || resourceCacheControl .isProxyRevalidate ());
176
+ if (!responseValidateOverride && requestCacheControl != null ) {
209
177
if (requestCacheControl .maxAge () != -1 ) {
210
178
maxAge = Math .min (maxAge , SafeMathUtils .safeMultiply (requestCacheControl .maxAge (), 1000 ));
211
179
}
@@ -214,8 +182,8 @@ private Future<ProxyResponse> tryHandleProxyRequestFromCache(ProxyContext contex
214
182
} else if (requestCacheControl .maxStale () != -1 ) {
215
183
maxAge += SafeMathUtils .safeMultiply (requestCacheControl .maxStale (), 1000 );
216
184
}
217
- if (age > maxAge ) needValidate = true ;
218
185
}
186
+ if (age > maxAge ) needValidate = true ;
219
187
String etag = resource .getHeaders ().get (HttpHeaders .ETAG );
220
188
String lastModified = resource .getHeaders ().get (HttpHeaders .LAST_MODIFIED );
221
189
if (needValidate ) {
@@ -225,13 +193,68 @@ private Future<ProxyResponse> tryHandleProxyRequestFromCache(ProxyContext contex
225
193
if (lastModified != null ) {
226
194
proxyRequest .headers ().set (HttpHeaders .IF_MODIFIED_SINCE , lastModified );
227
195
}
228
- context .set ("cached_resource" , resource );
196
+ context .set (CACHED_RESOURCE , resource );
229
197
return context .sendRequest ();
230
198
} else {
231
- proxyRequest .release ();
232
- ProxyResponse proxyResponse = proxyRequest .response ();
233
- resource .init (proxyResponse , inboundRequest .method () == HttpMethod .GET );
234
- 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
+ }
235
258
}
236
259
237
260
});
@@ -241,11 +264,9 @@ private Future<ProxyResponse> tryHandleProxyRequestFromCache(ProxyContext contex
241
264
242
265
private static boolean checkVaryHeaders (MultiMap requestHeaders , MultiMap varyHeaders ) {
243
266
for (Map .Entry <String , String > e : varyHeaders ) {
244
- String fromVary = e .getValue (). toLowerCase () ;
267
+ String fromVary = e .getValue ();
245
268
String fromRequest = requestHeaders .get (e .getKey ());
246
- if (fromRequest == null ) return false ;
247
- fromRequest = fromVary .toLowerCase ();
248
- if (!fromRequest .equals (fromVary )) return false ;
269
+ if (fromRequest == null || !fromRequest .equals (fromVary )) return false ;
249
270
}
250
271
return true ;
251
272
}
0 commit comments