Skip to content

Commit 0f14fde

Browse files
committed
update cache SPI and resource serialization
1 parent c9d9dfc commit 0f14fde

File tree

8 files changed

+463
-114
lines changed

8 files changed

+463
-114
lines changed

src/main/java/io/vertx/httpproxy/cache/CacheOptions.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ public CacheOptions setMaxSize(int maxSize) {
4545
return this;
4646
}
4747

48-
public <K, V> Cache<K, V> newCache() {
49-
return new CacheImpl<>(this);
48+
public Cache newCache() {
49+
return new CacheImpl(this);
5050
}
5151

5252
@Override
Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,52 @@
11
package io.vertx.httpproxy.impl;
22

3+
import io.vertx.core.Future;
34
import io.vertx.httpproxy.cache.CacheOptions;
45
import io.vertx.httpproxy.spi.cache.Cache;
6+
import io.vertx.httpproxy.spi.cache.Resource;
57

8+
import java.util.HashMap;
69
import java.util.LinkedHashMap;
10+
import java.util.LinkedList;
711
import java.util.Map;
812

913
/**
1014
* Simplistic implementation.
1115
*/
12-
public class CacheImpl<K, V> extends LinkedHashMap<K, V> implements Cache<K, V> {
16+
public class CacheImpl implements Cache {
1317

1418
private final int maxSize;
19+
private final Map<String, Resource> data;
20+
private final LinkedList<String> records;
1521

1622
public CacheImpl(CacheOptions options) {
1723
this.maxSize = options.getMaxSize();
24+
this.data = new HashMap<>();
25+
this.records = new LinkedList<>();
1826
}
1927

20-
protected boolean removeEldestEntry(Map.Entry eldest) {
21-
return size() > maxSize;
28+
29+
@Override
30+
public Future<Void> put(String key, Resource value) {
31+
while (records.size() >= maxSize) {
32+
String toRemove = records.removeLast();
33+
data.remove(toRemove);
34+
}
35+
36+
data.put(key, value);
37+
records.addFirst(key);
38+
return Future.succeededFuture();
39+
}
40+
41+
@Override
42+
public Future<Resource> get(String key) {
43+
return Future.succeededFuture(data.get(key));
44+
}
45+
46+
@Override
47+
public Future<Void> remove(String key) {
48+
records.remove(key);
49+
data.remove(key);
50+
return Future.succeededFuture();
2251
}
2352
}

src/main/java/io/vertx/httpproxy/impl/CachingFilter.java

Lines changed: 53 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,23 @@
1010
import io.vertx.httpproxy.ProxyRequest;
1111
import io.vertx.httpproxy.ProxyResponse;
1212
import io.vertx.httpproxy.spi.cache.Cache;
13+
import io.vertx.httpproxy.spi.cache.Resource;
1314

1415
import java.time.Instant;
1516
import java.util.function.BiFunction;
17+
import java.util.function.Predicate;
1618

1719
class CachingFilter implements ProxyInterceptor {
1820

19-
private static final BiFunction<String, Resource, Resource> CACHE_GET_AND_VALIDATE = (key, resource) -> {
20-
long now = System.currentTimeMillis();
21-
long val = resource.timestamp + resource.maxAge;
22-
return val < now ? null : resource;
23-
};
21+
private final Cache cache;
2422

25-
private final Cache<String, Resource> cache;
26-
27-
public CachingFilter(Cache<String, Resource> cache) {
23+
public CachingFilter(Cache cache) {
2824
this.cache = cache;
2925
}
3026

3127
@Override
3228
public Future<ProxyResponse> handleProxyRequest(ProxyContext context) {
33-
Future<ProxyResponse> future = tryHandleProxyRequestFromCache(context);
34-
if (future != null) {
35-
return future;
36-
}
37-
return context.sendRequest();
29+
return tryHandleProxyRequestFromCache(context);
3830
}
3931

4032
@Override
@@ -66,32 +58,33 @@ private Future<Void> sendAndTryCacheProxyResponse(ProxyContext context) {
6658
System.currentTimeMillis(),
6759
response.maxAge());
6860
Body body = response.getBody();
69-
response.setBody(Body.body(new BufferingReadStream(body.stream(), res.content), body.length()));
61+
response.setBody(Body.body(new BufferingReadStream(body.stream(), res.getContent()), body.length()));
7062
Future<Void> fut = context.sendResponse();
7163
fut.onSuccess(v -> {
7264
cache.put(absoluteUri, res);
7365
});
7466
return fut;
67+
} else if (request.getMethod() != HttpMethod.HEAD) {
68+
return context.sendResponse();
7569
} else {
76-
if (request.getMethod() == HttpMethod.HEAD) {
77-
Resource resource = cache.get(request.absoluteURI());
70+
return cache.get(request.absoluteURI()).compose(resource -> {
7871
if (resource != null) {
7972
if (!revalidateResource(response, resource)) {
8073
// Invalidate cache
8174
cache.remove(request.absoluteURI());
8275
}
8376
}
84-
}
85-
return context.sendResponse();
77+
return context.sendResponse();
78+
});
8679
}
8780
} else {
8881
return context.sendResponse();
8982
}
9083
}
9184

9285
private static boolean revalidateResource(ProxyResponse response, Resource resource) {
93-
if (resource.etag != null && response.etag() != null) {
94-
return resource.etag.equals(response.etag());
86+
if (resource.getEtag() != null && response.etag() != null) {
87+
return resource.getEtag().equals(response.etag());
9588
}
9689
return true;
9790
}
@@ -102,49 +95,54 @@ private Future<ProxyResponse> tryHandleProxyRequestFromCache(ProxyContext contex
10295

10396
HttpServerRequest response = proxyRequest.proxiedRequest();
10497

105-
Resource resource;
10698
HttpMethod method = response.method();
107-
if (method == HttpMethod.GET || method == HttpMethod.HEAD) {
108-
String cacheKey = proxyRequest.absoluteURI();
109-
resource = cache.computeIfPresent(cacheKey, CACHE_GET_AND_VALIDATE);
99+
if (method != HttpMethod.GET && method != HttpMethod.HEAD) {
100+
return context.sendRequest();
101+
}
102+
103+
String cacheKey = proxyRequest.absoluteURI();
104+
return cache.get(cacheKey).compose(resource -> {
110105
if (resource == null) {
111-
return null;
106+
return context.sendRequest();
107+
}
108+
109+
long now = System.currentTimeMillis();
110+
long val = resource.getTimestamp() + resource.getMaxAge();
111+
if (val < now) {
112+
return cache.remove(cacheKey).compose(v -> context.sendRequest());
112113
}
113-
} else {
114-
return null;
115-
}
116114

117-
String cacheControlHeader = response.getHeader(HttpHeaders.CACHE_CONTROL);
118-
if (cacheControlHeader != null) {
119-
CacheControl cacheControl = new CacheControl().parse(cacheControlHeader);
120-
if (cacheControl.maxAge() >= 0) {
121-
long now = System.currentTimeMillis();
122-
long currentAge = now - resource.timestamp;
123-
if (currentAge > cacheControl.maxAge() * 1000) {
124-
String etag = resource.headers.get(HttpHeaders.ETAG);
125-
if (etag != null) {
126-
proxyRequest.headers().set(HttpHeaders.IF_NONE_MATCH, resource.etag);
127-
context.set("cached_resource", resource);
128-
return context.sendRequest();
129-
} else {
130-
return null;
115+
String cacheControlHeader = response.getHeader(HttpHeaders.CACHE_CONTROL);
116+
if (cacheControlHeader != null) {
117+
CacheControl cacheControl = new CacheControl().parse(cacheControlHeader);
118+
if (cacheControl.maxAge() >= 0) {
119+
long currentAge = now - resource.getTimestamp();
120+
if (currentAge > cacheControl.maxAge() * 1000) {
121+
String etag = resource.getHeaders().get(HttpHeaders.ETAG);
122+
if (etag != null) {
123+
proxyRequest.headers().set(HttpHeaders.IF_NONE_MATCH, resource.getEtag());
124+
context.set("cached_resource", resource);
125+
return context.sendRequest();
126+
} else {
127+
return context.sendRequest();
128+
}
131129
}
132130
}
133131
}
134-
}
135132

136-
//
137-
String ifModifiedSinceHeader = response.getHeader(HttpHeaders.IF_MODIFIED_SINCE);
138-
if ((response.method() == HttpMethod.GET || response.method() == HttpMethod.HEAD) && ifModifiedSinceHeader != null && resource.lastModified != null) {
139-
Instant ifModifiedSince = ParseUtils.parseHeaderDate(ifModifiedSinceHeader);
140-
if (!ifModifiedSince.isAfter(resource.lastModified)) {
141-
response.response().setStatusCode(304).end();
142-
return Future.succeededFuture();
133+
//
134+
String ifModifiedSinceHeader = response.getHeader(HttpHeaders.IF_MODIFIED_SINCE);
135+
if ((response.method() == HttpMethod.GET || response.method() == HttpMethod.HEAD) && ifModifiedSinceHeader != null && resource.getLastModified() != null) {
136+
Instant ifModifiedSince = ParseUtils.parseHeaderDate(ifModifiedSinceHeader);
137+
if (!ifModifiedSince.isAfter(resource.getLastModified())) {
138+
return Future.succeededFuture(proxyRequest.release().response().setStatusCode(304));
139+
}
143140
}
144-
}
145-
proxyRequest.release();
146-
ProxyResponse proxyResponse = proxyRequest.response();
147-
resource.init(proxyResponse);
148-
return Future.succeededFuture(proxyResponse);
141+
proxyRequest.release();
142+
ProxyResponse proxyResponse = proxyRequest.response();
143+
resource.init(proxyResponse);
144+
return Future.succeededFuture(proxyResponse);
145+
});
146+
149147
}
150148
}

src/main/java/io/vertx/httpproxy/impl/Resource.java

Lines changed: 0 additions & 51 deletions
This file was deleted.

src/main/java/io/vertx/httpproxy/impl/ReverseProxy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public class ReverseProxy implements HttpProxy {
3333
public ReverseProxy(ProxyOptions options, HttpClient client) {
3434
CacheOptions cacheOptions = options.getCacheOptions();
3535
if (cacheOptions != null) {
36-
Cache<String, Resource> cache = cacheOptions.newCache();
36+
Cache cache = cacheOptions.newCache();
3737
addInterceptor(new CachingFilter(cache));
3838
}
3939
this.client = client;
Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,37 @@
11
package io.vertx.httpproxy.spi.cache;
22

3-
import java.util.Map;
3+
import io.vertx.core.Future;
4+
45

56
/**
67
* Cache SPI.
78
*/
8-
public interface Cache<K, V> extends Map<K, V> {
9+
public interface Cache {
10+
11+
/**
12+
* Being called when the cache attempts to add a new cache item.
13+
*
14+
* @param key the URI of the resource
15+
* @param value the cached response
16+
* @return a succeed void future
17+
*/
18+
Future<Void> put(String key, Resource value);
19+
20+
/**
21+
* Being called when the cache attempts to fetch a cache item.
22+
*
23+
* @param key the URI of the resource
24+
* @return the cached response, null if not exist
25+
*/
26+
Future<Resource> get(String key);
27+
28+
/**
29+
* Being called when the cache attempts to delete a cache item,
30+
* typically caused by invalidating an existing item. Do nothing
31+
* if not exist.
32+
*
33+
* @param key the URI of the resource
34+
* @return a succeed void future
35+
*/
36+
Future<Void> remove(String key);
937
}

0 commit comments

Comments
 (0)