HTTPCLIENT-2284: Cache entry representation improvements: (#477)
* request URI stored in cache is now normalized and is presently expected to be the same as the root key * removed references to variant cache keys from the cache entry * Variants in root entries are now represented as a set, not as a map
This commit is contained in:
parent
5fe89b4e32
commit
097c17b78b
|
@ -28,11 +28,15 @@ package org.apache.hc.client5.http.cache;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.utils.DateUtils;
|
import org.apache.hc.client5.http.utils.DateUtils;
|
||||||
import org.apache.hc.core5.annotation.Contract;
|
import org.apache.hc.core5.annotation.Contract;
|
||||||
|
@ -69,7 +73,7 @@ public class HttpCacheEntry implements MessageHeaders, Serializable {
|
||||||
private final int status;
|
private final int status;
|
||||||
private final HeaderGroup responseHeaders;
|
private final HeaderGroup responseHeaders;
|
||||||
private final Resource resource;
|
private final Resource resource;
|
||||||
private final Map<String, String> variantMap;
|
private final Set<String> variants;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal constructor that makes no validation of the input parameters and makes
|
* Internal constructor that makes no validation of the input parameters and makes
|
||||||
|
@ -85,7 +89,7 @@ public class HttpCacheEntry implements MessageHeaders, Serializable {
|
||||||
final int status,
|
final int status,
|
||||||
final HeaderGroup responseHeaders,
|
final HeaderGroup responseHeaders,
|
||||||
final Resource resource,
|
final Resource resource,
|
||||||
final Map<String, String> variantMap) {
|
final Collection<String> variants) {
|
||||||
super();
|
super();
|
||||||
this.requestDate = requestDate;
|
this.requestDate = requestDate;
|
||||||
this.responseDate = responseDate;
|
this.responseDate = responseDate;
|
||||||
|
@ -95,7 +99,7 @@ public class HttpCacheEntry implements MessageHeaders, Serializable {
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.responseHeaders = responseHeaders;
|
this.responseHeaders = responseHeaders;
|
||||||
this.resource = resource;
|
this.resource = resource;
|
||||||
this.variantMap = variantMap != null ? new HashMap<>(variantMap) : null;
|
this.variants = variants != null ? Collections.unmodifiableSet(new HashSet<>(variants)) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -164,7 +168,7 @@ public class HttpCacheEntry implements MessageHeaders, Serializable {
|
||||||
this.responseHeaders = new HeaderGroup();
|
this.responseHeaders = new HeaderGroup();
|
||||||
this.responseHeaders.setHeaders(responseHeaders);
|
this.responseHeaders.setHeaders(responseHeaders);
|
||||||
this.resource = resource;
|
this.resource = resource;
|
||||||
this.variantMap = variantMap != null ? new HashMap<>(variantMap) : null;
|
this.variants = variantMap != null ? Collections.unmodifiableSet(new HashSet<>(variantMap.keySet())) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -353,30 +357,27 @@ public class HttpCacheEntry implements MessageHeaders, Serializable {
|
||||||
* Indicates whether the origin response indicated the associated
|
* Indicates whether the origin response indicated the associated
|
||||||
* resource had variants (i.e. that the Vary header was set on the
|
* resource had variants (i.e. that the Vary header was set on the
|
||||||
* origin response).
|
* origin response).
|
||||||
* @return {@code true} if this cached response was a variant
|
|
||||||
*/
|
*/
|
||||||
public boolean hasVariants() {
|
public boolean hasVariants() {
|
||||||
return containsHeader(HttpHeaders.VARY);
|
return variants != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Returns all known variants.
|
||||||
|
*
|
||||||
* @since 5.3
|
* @since 5.3
|
||||||
*/
|
*/
|
||||||
public boolean isVariantRoot() {
|
public Set<String> getVariants() {
|
||||||
return variantMap != null;
|
return variants != null ? variants : Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an index about where in the cache different variants for
|
* @deprecated No longer applicable. Use {@link #getVariants()} instead.
|
||||||
* a given resource are stored. This maps "variant keys" to "cache keys",
|
|
||||||
* where the variant key is derived from the varying request headers,
|
|
||||||
* and the cache key is the location in the
|
|
||||||
* {@link HttpCacheStorage} where that
|
|
||||||
* particular variant is stored. The first variant returned is used as
|
|
||||||
* the "parent" entry to hold this index of the other variants.
|
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public Map<String, String> getVariantMap() {
|
public Map<String, String> getVariantMap() {
|
||||||
return variantMap != null ? Collections.unmodifiableMap(variantMap) : Collections.emptyMap();
|
return variants != null ? variants.stream()
|
||||||
|
.collect(Collectors.toMap(e -> e, e -> e + requestURI)) : Collections.emptyMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -418,7 +419,7 @@ public class HttpCacheEntry implements MessageHeaders, Serializable {
|
||||||
", status=" + status +
|
", status=" + status +
|
||||||
", responseHeaders=" + responseHeaders +
|
", responseHeaders=" + responseHeaders +
|
||||||
", resource=" + resource +
|
", resource=" + resource +
|
||||||
", variantMap=" + variantMap +
|
", variants=" + variants +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,15 +26,17 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hc.client5.http.cache;
|
package org.apache.hc.client5.http.cache;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import org.apache.hc.client5.http.impl.cache.CacheSupport;
|
||||||
import org.apache.hc.client5.http.impl.cache.DateSupport;
|
import org.apache.hc.client5.http.impl.cache.DateSupport;
|
||||||
import org.apache.hc.client5.http.utils.DateUtils;
|
import org.apache.hc.client5.http.utils.DateUtils;
|
||||||
import org.apache.hc.core5.annotation.Contract;
|
import org.apache.hc.core5.annotation.Contract;
|
||||||
|
@ -43,6 +45,7 @@ import org.apache.hc.core5.annotation.ThreadingBehavior;
|
||||||
import org.apache.hc.core5.http.Header;
|
import org.apache.hc.core5.http.Header;
|
||||||
import org.apache.hc.core5.http.HeaderElements;
|
import org.apache.hc.core5.http.HeaderElements;
|
||||||
import org.apache.hc.core5.http.HttpHeaders;
|
import org.apache.hc.core5.http.HttpHeaders;
|
||||||
|
import org.apache.hc.core5.http.HttpHost;
|
||||||
import org.apache.hc.core5.http.HttpMessage;
|
import org.apache.hc.core5.http.HttpMessage;
|
||||||
import org.apache.hc.core5.http.HttpRequest;
|
import org.apache.hc.core5.http.HttpRequest;
|
||||||
import org.apache.hc.core5.http.HttpResponse;
|
import org.apache.hc.core5.http.HttpResponse;
|
||||||
|
@ -161,26 +164,36 @@ public class HttpCacheEntryFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String normalizeRequestUri(final HttpHost host, final HttpRequest request) {
|
||||||
|
final String s = CacheSupport.getRequestUri(request, host);
|
||||||
|
final URI normalizeRequestUri = CacheSupport.normalize(s);
|
||||||
|
return normalizeRequestUri.toASCIIString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new root {@link HttpCacheEntry} (parent of multiple variants).
|
* Creates a new root {@link HttpCacheEntry} (parent of multiple variants).
|
||||||
*
|
*
|
||||||
* @param requestInstant Date/time when the request was made (Used for age calculations)
|
* @param requestInstant Date/time when the request was made (Used for age calculations)
|
||||||
* @param responseInstant Date/time that the response came back (Used for age calculations)
|
* @param responseInstant Date/time that the response came back (Used for age calculations)
|
||||||
|
* @param host Target host
|
||||||
* @param request Original client request (a deep copy of this object is made)
|
* @param request Original client request (a deep copy of this object is made)
|
||||||
* @param variantMap describing cache entries that are variants of this parent entry; this
|
* @param variants describing cache entries that are variants of this parent entry; this
|
||||||
* maps a "variant key" (derived from the varying request headers) to a
|
* maps a "variant key" (derived from the varying request headers) to a
|
||||||
* "cache key" (where in the cache storage the particular variant is
|
* "cache key" (where in the cache storage the particular variant is
|
||||||
* located)
|
* located)
|
||||||
*/
|
*/
|
||||||
public HttpCacheEntry createRoot(final Instant requestInstant,
|
public HttpCacheEntry createRoot(final Instant requestInstant,
|
||||||
final Instant responseInstant,
|
final Instant responseInstant,
|
||||||
|
final HttpHost host,
|
||||||
final HttpRequest request,
|
final HttpRequest request,
|
||||||
final HttpResponse response,
|
final HttpResponse response,
|
||||||
final Map<String, String> variantMap) {
|
final Collection<String> variants) {
|
||||||
Args.notNull(requestInstant, "Request instant");
|
Args.notNull(requestInstant, "Request instant");
|
||||||
Args.notNull(responseInstant, "Response instant");
|
Args.notNull(responseInstant, "Response instant");
|
||||||
|
Args.notNull(host, "Host");
|
||||||
Args.notNull(request, "Request");
|
Args.notNull(request, "Request");
|
||||||
Args.notNull(response, "Origin response");
|
Args.notNull(response, "Origin response");
|
||||||
|
final String requestUri = normalizeRequestUri(host, request);
|
||||||
final HeaderGroup requestHeaders = filterHopByHopHeaders(request);
|
final HeaderGroup requestHeaders = filterHopByHopHeaders(request);
|
||||||
final HeaderGroup responseHeaders = filterHopByHopHeaders(response);
|
final HeaderGroup responseHeaders = filterHopByHopHeaders(response);
|
||||||
ensureDate(responseHeaders, responseInstant);
|
ensureDate(responseHeaders, responseInstant);
|
||||||
|
@ -188,12 +201,12 @@ public class HttpCacheEntryFactory {
|
||||||
requestInstant,
|
requestInstant,
|
||||||
responseInstant,
|
responseInstant,
|
||||||
request.getMethod(),
|
request.getMethod(),
|
||||||
request.getRequestUri(),
|
requestUri,
|
||||||
requestHeaders,
|
requestHeaders,
|
||||||
response.getCode(),
|
response.getCode(),
|
||||||
responseHeaders,
|
responseHeaders,
|
||||||
null,
|
null,
|
||||||
variantMap);
|
variants);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -201,19 +214,23 @@ public class HttpCacheEntryFactory {
|
||||||
*
|
*
|
||||||
* @param requestInstant Date/time when the request was made (Used for age calculations)
|
* @param requestInstant Date/time when the request was made (Used for age calculations)
|
||||||
* @param responseInstant Date/time that the response came back (Used for age calculations)
|
* @param responseInstant Date/time that the response came back (Used for age calculations)
|
||||||
|
* @param host Target host
|
||||||
* @param request Original client request (a deep copy of this object is made)
|
* @param request Original client request (a deep copy of this object is made)
|
||||||
* @param response Origin response (a deep copy of this object is made)
|
* @param response Origin response (a deep copy of this object is made)
|
||||||
* @param resource Resource representing origin response body
|
* @param resource Resource representing origin response body
|
||||||
*/
|
*/
|
||||||
public HttpCacheEntry create(final Instant requestInstant,
|
public HttpCacheEntry create(final Instant requestInstant,
|
||||||
final Instant responseInstant,
|
final Instant responseInstant,
|
||||||
|
final HttpHost host,
|
||||||
final HttpRequest request,
|
final HttpRequest request,
|
||||||
final HttpResponse response,
|
final HttpResponse response,
|
||||||
final Resource resource) {
|
final Resource resource) {
|
||||||
Args.notNull(requestInstant, "Request instant");
|
Args.notNull(requestInstant, "Request instant");
|
||||||
Args.notNull(responseInstant, "Response instant");
|
Args.notNull(responseInstant, "Response instant");
|
||||||
|
Args.notNull(host, "Host");
|
||||||
Args.notNull(request, "Request");
|
Args.notNull(request, "Request");
|
||||||
Args.notNull(response, "Origin response");
|
Args.notNull(response, "Origin response");
|
||||||
|
final String requestUri = normalizeRequestUri(host, request);
|
||||||
final HeaderGroup requestHeaders = filterHopByHopHeaders(request);
|
final HeaderGroup requestHeaders = filterHopByHopHeaders(request);
|
||||||
final HeaderGroup responseHeaders = filterHopByHopHeaders(response);
|
final HeaderGroup responseHeaders = filterHopByHopHeaders(response);
|
||||||
ensureDate(responseHeaders, responseInstant);
|
ensureDate(responseHeaders, responseInstant);
|
||||||
|
@ -221,7 +238,7 @@ public class HttpCacheEntryFactory {
|
||||||
requestInstant,
|
requestInstant,
|
||||||
responseInstant,
|
responseInstant,
|
||||||
request.getMethod(),
|
request.getMethod(),
|
||||||
request.getRequestUri(),
|
requestUri,
|
||||||
requestHeaders,
|
requestHeaders,
|
||||||
response.getCode(),
|
response.getCode(),
|
||||||
responseHeaders,
|
responseHeaders,
|
||||||
|
@ -282,7 +299,7 @@ public class HttpCacheEntryFactory {
|
||||||
entry.getStatus(),
|
entry.getStatus(),
|
||||||
headers(entry.headerIterator()),
|
headers(entry.headerIterator()),
|
||||||
entry.getResource(),
|
entry.getResource(),
|
||||||
entry.isVariantRoot() ? new HashMap<>(entry.getVariantMap()) : null);
|
entry.hasVariants() ? new HashSet<>(entry.getVariants()) : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -703,6 +703,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
|
||||||
recordCacheUpdate(scope.clientContext);
|
recordCacheUpdate(scope.clientContext);
|
||||||
operation.setDependency(responseCache.update(
|
operation.setDependency(responseCache.update(
|
||||||
hit,
|
hit,
|
||||||
|
target,
|
||||||
request,
|
request,
|
||||||
backendResponse,
|
backendResponse,
|
||||||
requestDate,
|
requestDate,
|
||||||
|
@ -880,7 +881,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
|
||||||
triggerResponse(cacheResponse, scope, asyncExecCallback);
|
triggerResponse(cacheResponse, scope, asyncExecCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (partialMatch != null && partialMatch.entry.isVariantRoot()) {
|
if (partialMatch != null && partialMatch.entry.hasVariants()) {
|
||||||
operation.setDependency(responseCache.getVariants(
|
operation.setDependency(responseCache.getVariants(
|
||||||
partialMatch,
|
partialMatch,
|
||||||
new FutureCallback<Collection<CacheHit>>() {
|
new FutureCallback<Collection<CacheHit>>() {
|
||||||
|
@ -1016,6 +1017,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
|
||||||
}
|
}
|
||||||
responseCache.update(
|
responseCache.update(
|
||||||
hit,
|
hit,
|
||||||
|
target,
|
||||||
request,
|
request,
|
||||||
backendResponse,
|
backendResponse,
|
||||||
requestDate,
|
requestDate,
|
||||||
|
|
|
@ -30,7 +30,7 @@ import java.net.URI;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -104,14 +104,14 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
|
||||||
@Override
|
@Override
|
||||||
public void completed(final HttpCacheEntry root) {
|
public void completed(final HttpCacheEntry root) {
|
||||||
if (root != null) {
|
if (root != null) {
|
||||||
if (root.isVariantRoot()) {
|
if (root.hasVariants()) {
|
||||||
final List<String> variantNames = CacheKeyGenerator.variantNames(root);
|
final List<String> variantNames = CacheKeyGenerator.variantNames(root);
|
||||||
final String variantKey = cacheKeyGenerator.generateVariantKey(request, variantNames);
|
final String variantKey = cacheKeyGenerator.generateVariantKey(request, variantNames);
|
||||||
final String cacheKey = root.getVariantMap().get(variantKey);
|
if (root.getVariants().contains(variantKey)) {
|
||||||
if (LOG.isDebugEnabled()) {
|
final String cacheKey = variantKey + rootKey;
|
||||||
LOG.debug("Get cache variant entry: {}", cacheKey);
|
if (LOG.isDebugEnabled()) {
|
||||||
}
|
LOG.debug("Get cache variant entry: {}", cacheKey);
|
||||||
if (cacheKey != null) {
|
}
|
||||||
complexCancellable.setDependency(storage.getEntry(
|
complexCancellable.setDependency(storage.getEntry(
|
||||||
cacheKey,
|
cacheKey,
|
||||||
new FutureCallback<HttpCacheEntry>() {
|
new FutureCallback<HttpCacheEntry>() {
|
||||||
|
@ -179,8 +179,11 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
|
||||||
}
|
}
|
||||||
final ComplexCancellable complexCancellable = new ComplexCancellable();
|
final ComplexCancellable complexCancellable = new ComplexCancellable();
|
||||||
final HttpCacheEntry root = hit.entry;
|
final HttpCacheEntry root = hit.entry;
|
||||||
if (root != null && root.isVariantRoot()) {
|
final String rootKey = hit.rootKey;
|
||||||
final Set<String> variantCacheKeys = root.getVariantMap().keySet();
|
if (root != null && root.hasVariants()) {
|
||||||
|
final List<String> variantCacheKeys = root.getVariants().stream()
|
||||||
|
.map(e -> e + rootKey)
|
||||||
|
.collect(Collectors.toList());
|
||||||
complexCancellable.setDependency(storage.getEntries(
|
complexCancellable.setDependency(storage.getEntries(
|
||||||
variantCacheKeys,
|
variantCacheKeys,
|
||||||
new FutureCallback<Map<String, HttpCacheEntry>>() {
|
new FutureCallback<Map<String, HttpCacheEntry>>() {
|
||||||
|
@ -253,6 +256,7 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
Cancellable storeVariant(
|
Cancellable storeVariant(
|
||||||
|
final HttpHost host,
|
||||||
final HttpRequest request,
|
final HttpRequest request,
|
||||||
final HttpResponse originResponse,
|
final HttpResponse originResponse,
|
||||||
final Instant requestSent,
|
final Instant requestSent,
|
||||||
|
@ -278,9 +282,9 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
|
||||||
|
|
||||||
storage.updateEntry(rootKey,
|
storage.updateEntry(rootKey,
|
||||||
existing -> {
|
existing -> {
|
||||||
final Map<String,String> variantMap = existing != null ? new HashMap<>(existing.getVariantMap()) : new HashMap<>();
|
final Set<String> variantMap = existing != null ? new HashSet<>(existing.getVariants()) : new HashSet<>();
|
||||||
variantMap.put(variantKey, variantCacheKey);
|
variantMap.add(variantKey);
|
||||||
return cacheEntryFactory.createRoot(requestSent, responseReceived, request, originResponse, variantMap);
|
return cacheEntryFactory.createRoot(requestSent, responseReceived, host, request, originResponse, variantMap);
|
||||||
},
|
},
|
||||||
new FutureCallback<Boolean>() {
|
new FutureCallback<Boolean>() {
|
||||||
|
|
||||||
|
@ -333,6 +337,7 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
Cancellable store(
|
Cancellable store(
|
||||||
|
final HttpHost host,
|
||||||
final HttpRequest request,
|
final HttpRequest request,
|
||||||
final HttpResponse originResponse,
|
final HttpResponse originResponse,
|
||||||
final Instant requestSent,
|
final Instant requestSent,
|
||||||
|
@ -340,8 +345,8 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
|
||||||
final String rootKey,
|
final String rootKey,
|
||||||
final HttpCacheEntry entry,
|
final HttpCacheEntry entry,
|
||||||
final FutureCallback<CacheHit> callback) {
|
final FutureCallback<CacheHit> callback) {
|
||||||
if (entry.hasVariants()) {
|
if (entry.containsHeader(HttpHeaders.VARY)) {
|
||||||
return storeVariant(request, originResponse, requestSent, responseReceived, rootKey, entry, callback);
|
return storeVariant(host, request, originResponse, requestSent, responseReceived, rootKey, entry, callback);
|
||||||
} else {
|
} else {
|
||||||
return storeEntry(rootKey, entry, callback);
|
return storeEntry(rootKey, entry, callback);
|
||||||
}
|
}
|
||||||
|
@ -370,14 +375,16 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
|
||||||
final HttpCacheEntry backup = cacheEntryFactory.create(
|
final HttpCacheEntry backup = cacheEntryFactory.create(
|
||||||
requestSent,
|
requestSent,
|
||||||
responseReceived,
|
responseReceived,
|
||||||
|
host,
|
||||||
request,
|
request,
|
||||||
originResponse,
|
originResponse,
|
||||||
content != null ? HeapResourceFactory.INSTANCE.generate(null, content.array(), 0, content.length()) : null);
|
content != null ? HeapResourceFactory.INSTANCE.generate(null, content.array(), 0, content.length()) : null);
|
||||||
callback.completed(new CacheHit(rootKey, backup));
|
callback.completed(new CacheHit(rootKey, backup));
|
||||||
return Operations.nonCancellable();
|
return Operations.nonCancellable();
|
||||||
}
|
}
|
||||||
final HttpCacheEntry entry = cacheEntryFactory.create(requestSent, responseReceived, request, originResponse, resource);
|
final HttpCacheEntry entry = cacheEntryFactory.create(requestSent, responseReceived, host, request, originResponse, resource);
|
||||||
return store(
|
return store(
|
||||||
|
host,
|
||||||
request,
|
request,
|
||||||
originResponse,
|
originResponse,
|
||||||
requestSent,
|
requestSent,
|
||||||
|
@ -390,6 +397,7 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
|
||||||
@Override
|
@Override
|
||||||
public Cancellable update(
|
public Cancellable update(
|
||||||
final CacheHit stale,
|
final CacheHit stale,
|
||||||
|
final HttpHost host,
|
||||||
final HttpRequest request,
|
final HttpRequest request,
|
||||||
final HttpResponse originResponse,
|
final HttpResponse originResponse,
|
||||||
final Instant requestSent,
|
final Instant requestSent,
|
||||||
|
@ -410,6 +418,7 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
return store(
|
return store(
|
||||||
|
host,
|
||||||
request,
|
request,
|
||||||
originResponse,
|
originResponse,
|
||||||
requestSent,
|
requestSent,
|
||||||
|
@ -454,7 +463,7 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Store cache entry using existing entry: {} -> {}", rootKey, hit.rootKey);
|
LOG.debug("Store cache entry using existing entry: {} -> {}", rootKey, hit.rootKey);
|
||||||
}
|
}
|
||||||
return store(request, originResponse, requestSent, responseReceived, rootKey, hit.entry, callback);
|
return store(host, request, originResponse, requestSent, responseReceived, rootKey, hit.entry, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void evictEntry(final String cacheKey) {
|
private void evictEntry(final String cacheKey) {
|
||||||
|
@ -487,12 +496,13 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
|
||||||
LOG.debug("Evicting root cache entry {}", rootKey);
|
LOG.debug("Evicting root cache entry {}", rootKey);
|
||||||
}
|
}
|
||||||
evictEntry(rootKey);
|
evictEntry(rootKey);
|
||||||
if (root.isVariantRoot()) {
|
if (root.hasVariants()) {
|
||||||
for (final String variantKey : root.getVariantMap().values()) {
|
for (final String variantKey : root.getVariants()) {
|
||||||
|
final String variantEntryKey = variantKey + rootKey;
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Evicting variant cache entry {}", variantKey);
|
LOG.debug("Evicting variant cache entry {}", variantEntryKey);
|
||||||
}
|
}
|
||||||
evictEntry(variantKey);
|
evictEntry(variantEntryKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,10 +30,10 @@ import java.net.URI;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.cache.HttpCacheCASOperation;
|
import org.apache.hc.client5.http.cache.HttpCacheCASOperation;
|
||||||
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
||||||
|
@ -150,14 +150,14 @@ class BasicHttpCache implements HttpCache {
|
||||||
if (root == null) {
|
if (root == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (root.isVariantRoot()) {
|
if (root.hasVariants()) {
|
||||||
final List<String> variantNames = CacheKeyGenerator.variantNames(root);
|
final List<String> variantNames = CacheKeyGenerator.variantNames(root);
|
||||||
final String variantKey = cacheKeyGenerator.generateVariantKey(request, variantNames);
|
final String variantKey = cacheKeyGenerator.generateVariantKey(request, variantNames);
|
||||||
final String cacheKey = root.getVariantMap().get(variantKey);
|
if (root.getVariants().contains(variantKey)) {
|
||||||
if (LOG.isDebugEnabled()) {
|
final String cacheKey = variantKey + rootKey;
|
||||||
LOG.debug("Get cache variant entry: {}", cacheKey);
|
if (LOG.isDebugEnabled()) {
|
||||||
}
|
LOG.debug("Get cache variant entry: {}", cacheKey);
|
||||||
if (cacheKey != null) {
|
}
|
||||||
final HttpCacheEntry entry = getInternal(cacheKey);
|
final HttpCacheEntry entry = getInternal(cacheKey);
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
return new CacheMatch(new CacheHit(rootKey, cacheKey, entry), new CacheHit(rootKey, root));
|
return new CacheMatch(new CacheHit(rootKey, cacheKey, entry), new CacheHit(rootKey, root));
|
||||||
|
@ -175,10 +175,11 @@ class BasicHttpCache implements HttpCache {
|
||||||
LOG.debug("Get variant cache entries: {}", hit.rootKey);
|
LOG.debug("Get variant cache entries: {}", hit.rootKey);
|
||||||
}
|
}
|
||||||
final HttpCacheEntry root = hit.entry;
|
final HttpCacheEntry root = hit.entry;
|
||||||
if (root != null && root.isVariantRoot()) {
|
final String rootKey = hit.rootKey;
|
||||||
|
if (root != null && root.hasVariants()) {
|
||||||
final List<CacheHit> variants = new ArrayList<>();
|
final List<CacheHit> variants = new ArrayList<>();
|
||||||
for (final String variantKey : root.getVariantMap().values()) {
|
for (final String variantKey : root.getVariants()) {
|
||||||
final HttpCacheEntry variant = getInternal(variantKey);
|
final HttpCacheEntry variant = getInternal(variantKey + rootKey);
|
||||||
if (variant != null) {
|
if (variant != null) {
|
||||||
variants.add(new CacheHit(hit.rootKey, variantKey, variant));
|
variants.add(new CacheHit(hit.rootKey, variantKey, variant));
|
||||||
}
|
}
|
||||||
|
@ -189,14 +190,15 @@ class BasicHttpCache implements HttpCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
CacheHit store(
|
CacheHit store(
|
||||||
|
final HttpHost host,
|
||||||
final HttpRequest request,
|
final HttpRequest request,
|
||||||
final HttpResponse originResponse,
|
final HttpResponse originResponse,
|
||||||
final Instant requestSent,
|
final Instant requestSent,
|
||||||
final Instant responseReceived,
|
final Instant responseReceived,
|
||||||
final String rootKey,
|
final String rootKey,
|
||||||
final HttpCacheEntry entry) {
|
final HttpCacheEntry entry) {
|
||||||
if (entry.hasVariants()) {
|
if (entry.containsHeader(HttpHeaders.VARY)) {
|
||||||
return storeVariant(request, originResponse, requestSent, responseReceived, rootKey, entry);
|
return storeVariant(host, request, originResponse, requestSent, responseReceived, rootKey, entry);
|
||||||
} else {
|
} else {
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Put entry in cache: {}", rootKey);
|
LOG.debug("Put entry in cache: {}", rootKey);
|
||||||
|
@ -207,6 +209,7 @@ class BasicHttpCache implements HttpCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
CacheHit storeVariant(
|
CacheHit storeVariant(
|
||||||
|
final HttpHost host,
|
||||||
final HttpRequest request,
|
final HttpRequest request,
|
||||||
final HttpResponse originResponse,
|
final HttpResponse originResponse,
|
||||||
final Instant requestSent,
|
final Instant requestSent,
|
||||||
|
@ -228,9 +231,9 @@ class BasicHttpCache implements HttpCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateInternal(rootKey, existing -> {
|
updateInternal(rootKey, existing -> {
|
||||||
final Map<String, String> variantMap = existing != null ? new HashMap<>(existing.getVariantMap()) : new HashMap<>();
|
final Set<String> variants = existing != null ? new HashSet<>(existing.getVariants()) : new HashSet<>();
|
||||||
variantMap.put(variantKey, variantCacheKey);
|
variants.add(variantKey);
|
||||||
return cacheEntryFactory.createRoot(requestSent, responseReceived, request, originResponse, variantMap);
|
return cacheEntryFactory.createRoot(requestSent, responseReceived, host, request, originResponse, variants);
|
||||||
});
|
});
|
||||||
return new CacheHit(rootKey, variantCacheKey, entry);
|
return new CacheHit(rootKey, variantCacheKey, entry);
|
||||||
}
|
}
|
||||||
|
@ -257,18 +260,20 @@ class BasicHttpCache implements HttpCache {
|
||||||
final HttpCacheEntry backup = cacheEntryFactory.create(
|
final HttpCacheEntry backup = cacheEntryFactory.create(
|
||||||
requestSent,
|
requestSent,
|
||||||
responseReceived,
|
responseReceived,
|
||||||
|
host,
|
||||||
request,
|
request,
|
||||||
originResponse,
|
originResponse,
|
||||||
content != null ? HeapResourceFactory.INSTANCE.generate(null, content.array(), 0, content.length()) : null);
|
content != null ? HeapResourceFactory.INSTANCE.generate(null, content.array(), 0, content.length()) : null);
|
||||||
return new CacheHit(rootKey, backup);
|
return new CacheHit(rootKey, backup);
|
||||||
}
|
}
|
||||||
final HttpCacheEntry entry = cacheEntryFactory.create(requestSent, responseReceived, request, originResponse, resource);
|
final HttpCacheEntry entry = cacheEntryFactory.create(requestSent, responseReceived, host, request, originResponse, resource);
|
||||||
return store(request, originResponse, requestSent, responseReceived, rootKey, entry);
|
return store(host, request, originResponse, requestSent, responseReceived, rootKey, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CacheHit update(
|
public CacheHit update(
|
||||||
final CacheHit stale,
|
final CacheHit stale,
|
||||||
|
final HttpHost host,
|
||||||
final HttpRequest request,
|
final HttpRequest request,
|
||||||
final HttpResponse originResponse,
|
final HttpResponse originResponse,
|
||||||
final Instant requestSent,
|
final Instant requestSent,
|
||||||
|
@ -282,7 +287,7 @@ class BasicHttpCache implements HttpCache {
|
||||||
responseReceived,
|
responseReceived,
|
||||||
originResponse,
|
originResponse,
|
||||||
stale.entry);
|
stale.entry);
|
||||||
return store(request, originResponse, requestSent, responseReceived, entryKey, updatedEntry);
|
return store(host, request, originResponse, requestSent, responseReceived, entryKey, updatedEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -321,7 +326,7 @@ class BasicHttpCache implements HttpCache {
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Store cache entry using existing entry: {} -> {}", rootKey, hit.rootKey);
|
LOG.debug("Store cache entry using existing entry: {} -> {}", rootKey, hit.rootKey);
|
||||||
}
|
}
|
||||||
return store(request, originResponse, requestSent, responseReceived, rootKey, hit.entry);
|
return store(host, request, originResponse, requestSent, responseReceived, rootKey, hit.entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void evictAll(final HttpCacheEntry root, final String rootKey) {
|
private void evictAll(final HttpCacheEntry root, final String rootKey) {
|
||||||
|
@ -329,12 +334,13 @@ class BasicHttpCache implements HttpCache {
|
||||||
LOG.debug("Evicting root cache entry {}", rootKey);
|
LOG.debug("Evicting root cache entry {}", rootKey);
|
||||||
}
|
}
|
||||||
removeInternal(rootKey);
|
removeInternal(rootKey);
|
||||||
if (root.isVariantRoot()) {
|
if (root.hasVariants()) {
|
||||||
for (final String variantKey : root.getVariantMap().values()) {
|
for (final String variantKey : root.getVariants()) {
|
||||||
|
final String variantEntryKey = variantKey + rootKey;
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Evicting variant cache entry {}", variantKey);
|
LOG.debug("Evicting variant cache entry {}", variantEntryKey);
|
||||||
}
|
}
|
||||||
removeInternal(variantKey);
|
removeInternal(variantEntryKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -339,7 +339,7 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
|
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
|
||||||
final CacheHit updated = responseCache.update(hit, request, backendResponse, requestDate, responseDate);
|
final CacheHit updated = responseCache.update(hit, target, request, backendResponse, requestDate, responseDate);
|
||||||
if (suitabilityChecker.isConditional(request)
|
if (suitabilityChecker.isConditional(request)
|
||||||
&& suitabilityChecker.allConditionalsMatch(request, updated.entry, Instant.now())) {
|
&& suitabilityChecker.allConditionalsMatch(request, updated.entry, Instant.now())) {
|
||||||
return convert(responseGenerator.generateNotModifiedResponse(updated.entry), scope);
|
return convert(responseGenerator.generateNotModifiedResponse(updated.entry), scope);
|
||||||
|
@ -399,6 +399,7 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
|
||||||
if (hit != null) {
|
if (hit != null) {
|
||||||
final CacheHit updated = responseCache.update(
|
final CacheHit updated = responseCache.update(
|
||||||
hit,
|
hit,
|
||||||
|
target,
|
||||||
request,
|
request,
|
||||||
backendResponse,
|
backendResponse,
|
||||||
requestSent,
|
requestSent,
|
||||||
|
@ -458,7 +459,7 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
|
||||||
if (!mayCallBackend(requestCacheControl)) {
|
if (!mayCallBackend(requestCacheControl)) {
|
||||||
return new BasicClassicHttpResponse(HttpStatus.SC_GATEWAY_TIMEOUT, "Gateway Timeout");
|
return new BasicClassicHttpResponse(HttpStatus.SC_GATEWAY_TIMEOUT, "Gateway Timeout");
|
||||||
}
|
}
|
||||||
if (partialMatch != null && partialMatch.entry.isVariantRoot()) {
|
if (partialMatch != null && partialMatch.entry.hasVariants()) {
|
||||||
final List<CacheHit> variants = responseCache.getVariants(partialMatch);
|
final List<CacheHit> variants = responseCache.getVariants(partialMatch);
|
||||||
if (variants != null && !variants.isEmpty()) {
|
if (variants != null && !variants.isEmpty()) {
|
||||||
return negotiateResponseFromVariants(target, request, scope, chain, variants);
|
return negotiateResponseFromVariants(target, request, scope, chain, variants);
|
||||||
|
|
|
@ -69,6 +69,7 @@ interface HttpAsyncCache {
|
||||||
*/
|
*/
|
||||||
Cancellable update(
|
Cancellable update(
|
||||||
CacheHit stale,
|
CacheHit stale,
|
||||||
|
HttpHost host,
|
||||||
HttpRequest request,
|
HttpRequest request,
|
||||||
HttpResponse originResponse,
|
HttpResponse originResponse,
|
||||||
Instant requestSent,
|
Instant requestSent,
|
||||||
|
|
|
@ -32,9 +32,9 @@ import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.HashMap;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
|
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
|
||||||
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
||||||
|
@ -88,6 +88,7 @@ public class HttpByteArrayCacheEntrySerializer implements HttpCacheEntrySerializ
|
||||||
static final String HC_CACHE_LENGTH = "HC-Resource-Length";
|
static final String HC_CACHE_LENGTH = "HC-Resource-Length";
|
||||||
static final String HC_REQUEST_INSTANT = "HC-Request-Instant";
|
static final String HC_REQUEST_INSTANT = "HC-Request-Instant";
|
||||||
static final String HC_RESPONSE_INSTANT = "HC-Response-Instant";
|
static final String HC_RESPONSE_INSTANT = "HC-Response-Instant";
|
||||||
|
static final String HC_VARIANT = "HC-Variant";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Singleton instance of this class.
|
* Singleton instance of this class.
|
||||||
|
@ -166,12 +167,11 @@ public class HttpByteArrayCacheEntrySerializer implements HttpCacheEntrySerializ
|
||||||
line.append(asStr(cacheEntry.getResponseInstant()));
|
line.append(asStr(cacheEntry.getResponseInstant()));
|
||||||
outputBuffer.writeLine(line, out);
|
outputBuffer.writeLine(line, out);
|
||||||
|
|
||||||
final Map<String, String> variantMap = cacheEntry.getVariantMap();
|
for (final String variant : cacheEntry.getVariants()) {
|
||||||
for (final Map.Entry<String, String> entry : variantMap.entrySet()) {
|
|
||||||
line.clear();
|
line.clear();
|
||||||
line.append(entry.getKey());
|
line.append(HC_VARIANT);
|
||||||
line.append(": ");
|
line.append(": ");
|
||||||
line.append(entry.getValue());
|
line.append(variant);
|
||||||
outputBuffer.writeLine(line, out);
|
outputBuffer.writeLine(line, out);
|
||||||
}
|
}
|
||||||
line.clear();
|
line.clear();
|
||||||
|
@ -243,7 +243,7 @@ public class HttpByteArrayCacheEntrySerializer implements HttpCacheEntrySerializ
|
||||||
long length = -1;
|
long length = -1;
|
||||||
Instant requestDate = null;
|
Instant requestDate = null;
|
||||||
Instant responseDate = null;
|
Instant responseDate = null;
|
||||||
final Map<String, String> variantMap = new HashMap<>();
|
final Set<String> variants = new HashSet<>();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
line.clear();
|
line.clear();
|
||||||
|
@ -262,8 +262,10 @@ public class HttpByteArrayCacheEntrySerializer implements HttpCacheEntrySerializ
|
||||||
requestDate = asInstant(value);
|
requestDate = asInstant(value);
|
||||||
} else if (name.equalsIgnoreCase(HC_RESPONSE_INSTANT)) {
|
} else if (name.equalsIgnoreCase(HC_RESPONSE_INSTANT)) {
|
||||||
responseDate = asInstant(value);
|
responseDate = asInstant(value);
|
||||||
|
} else if (name.equalsIgnoreCase(HC_VARIANT)) {
|
||||||
|
variants.add(value);
|
||||||
} else {
|
} else {
|
||||||
variantMap.put(name, value);
|
throw new ResourceIOException("Unexpected header entry");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,7 +330,7 @@ public class HttpByteArrayCacheEntrySerializer implements HttpCacheEntrySerializ
|
||||||
statusLine.getStatusCode(),
|
statusLine.getStatusCode(),
|
||||||
responseHeaders,
|
responseHeaders,
|
||||||
resource,
|
resource,
|
||||||
variantMap
|
!variants.isEmpty() ? variants : null
|
||||||
);
|
);
|
||||||
|
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
|
|
|
@ -65,6 +65,7 @@ interface HttpCache {
|
||||||
*/
|
*/
|
||||||
CacheHit update(
|
CacheHit update(
|
||||||
CacheHit stale,
|
CacheHit stale,
|
||||||
|
HttpHost host,
|
||||||
HttpRequest request,
|
HttpRequest request,
|
||||||
HttpResponse originResponse,
|
HttpResponse originResponse,
|
||||||
Instant requestSent,
|
Instant requestSent,
|
||||||
|
|
|
@ -27,19 +27,20 @@
|
||||||
package org.apache.hc.client5.http.cache;
|
package org.apache.hc.client5.http.cache;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.temporal.ChronoField;
|
import java.time.temporal.ChronoField;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.impl.cache.HttpTestUtils;
|
import org.apache.hc.client5.http.impl.cache.HttpTestUtils;
|
||||||
import org.apache.hc.client5.http.utils.DateUtils;
|
import org.apache.hc.client5.http.utils.DateUtils;
|
||||||
|
@ -47,6 +48,7 @@ import org.apache.hc.core5.http.Header;
|
||||||
import org.apache.hc.core5.http.HttpStatus;
|
import org.apache.hc.core5.http.HttpStatus;
|
||||||
import org.apache.hc.core5.http.Method;
|
import org.apache.hc.core5.http.Method;
|
||||||
import org.apache.hc.core5.http.message.BasicHeader;
|
import org.apache.hc.core5.http.message.BasicHeader;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@ -77,10 +79,10 @@ public class TestHttpCacheEntry {
|
||||||
final int status,
|
final int status,
|
||||||
final Header[] headers,
|
final Header[] headers,
|
||||||
final Resource resource,
|
final Resource resource,
|
||||||
final Map<String, String> variantMap) {
|
final Collection<String> variants) {
|
||||||
return new HttpCacheEntry(requestDate, responseDate,
|
return new HttpCacheEntry(requestDate, responseDate,
|
||||||
"GET", "/", HttpTestUtils.headers(),
|
"GET", "/", HttpTestUtils.headers(),
|
||||||
status, HttpTestUtils.headers(headers), resource, variantMap);
|
status, HttpTestUtils.headers(headers), resource, variants);
|
||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
public void testGetHeadersReturnsCorrectHeaders() {
|
public void testGetHeadersReturnsCorrectHeaders() {
|
||||||
|
@ -122,29 +124,6 @@ public class TestHttpCacheEntry {
|
||||||
assertNull(entry.getFirstHeader("quux"));
|
assertNull(entry.getFirstHeader("quux"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCacheEntryWithOneVaryHeaderHasVariants() {
|
|
||||||
final Header[] headers = { new BasicHeader("Vary", "User-Agent") };
|
|
||||||
entry = makeEntry(headers);
|
|
||||||
assertTrue(entry.hasVariants());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCacheEntryWithMultipleVaryHeadersHasVariants() {
|
|
||||||
final Header[] headers = { new BasicHeader("Vary", "User-Agent"),
|
|
||||||
new BasicHeader("Vary", "Accept-Encoding")
|
|
||||||
};
|
|
||||||
entry = makeEntry(headers);
|
|
||||||
assertTrue(entry.hasVariants());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCacheEntryWithVaryStarHasVariants(){
|
|
||||||
final Header[] headers = { new BasicHeader("Vary", "*") };
|
|
||||||
entry = makeEntry(headers);
|
|
||||||
assertTrue(entry.hasVariants());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetMethodReturnsCorrectRequestMethod() {
|
public void testGetMethodReturnsCorrectRequestMethod() {
|
||||||
final Header[] headers = { new BasicHeader("foo", "fooValue"),
|
final Header[] headers = { new BasicHeader("foo", "fooValue"),
|
||||||
|
@ -204,42 +183,38 @@ public class TestHttpCacheEntry {
|
||||||
public void canProvideVariantMap() {
|
public void canProvideVariantMap() {
|
||||||
makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
|
makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
|
||||||
new Header[]{}, mockResource,
|
new Header[]{}, mockResource,
|
||||||
new HashMap<>());
|
null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void canRetrieveOriginalVariantMap() {
|
public void canRetrieveOriginalVariantMap() {
|
||||||
final Map<String,String> variantMap = new HashMap<>();
|
final Set<String> variants = new HashSet<>();
|
||||||
variantMap.put("A","B");
|
variants.add("A");
|
||||||
variantMap.put("C","D");
|
variants.add("B");
|
||||||
|
variants.add("C");
|
||||||
entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
|
entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
|
||||||
new Header[]{}, mockResource,
|
new Header[]{}, mockResource,
|
||||||
variantMap);
|
variants);
|
||||||
final Map<String,String> result = entry.getVariantMap();
|
final Set<String> result = entry.getVariants();
|
||||||
assertEquals(2, result.size());
|
assertEquals(3, result.size());
|
||||||
assertEquals("B", result.get("A"));
|
assertTrue(result.contains("A"));
|
||||||
assertEquals("D", result.get("C"));
|
assertTrue(result.contains("B"));
|
||||||
|
assertTrue(result.contains("C"));
|
||||||
|
assertFalse(result.contains("D"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void retrievedVariantMapIsNotModifiable() {
|
public void retrievedVariantMapIsNotModifiable() {
|
||||||
final Map<String,String> variantMap = new HashMap<>();
|
final Set<String> variants = new HashSet<>();
|
||||||
variantMap.put("A","B");
|
variants.add("A");
|
||||||
variantMap.put("C","D");
|
variants.add("B");
|
||||||
|
variants.add("C");
|
||||||
entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
|
entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
|
||||||
new Header[]{}, mockResource,
|
new Header[]{}, mockResource,
|
||||||
variantMap);
|
variants);
|
||||||
final Map<String,String> result = entry.getVariantMap();
|
final Set<String> result = entry.getVariants();
|
||||||
try {
|
Assertions.assertThrows(UnsupportedOperationException.class, () -> result.remove("A"));
|
||||||
result.remove("A");
|
Assertions.assertThrows(UnsupportedOperationException.class, () -> result.add("D"));
|
||||||
fail("Should have thrown exception");
|
|
||||||
} catch (final UnsupportedOperationException expected) {
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
result.put("E","F");
|
|
||||||
fail("Should have thrown exception");
|
|
||||||
} catch (final UnsupportedOperationException expected) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -27,8 +27,7 @@
|
||||||
package org.apache.hc.client5.http.cache;
|
package org.apache.hc.client5.http.cache;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.HashMap;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.HeadersMatcher;
|
import org.apache.hc.client5.http.HeadersMatcher;
|
||||||
|
@ -39,6 +38,7 @@ import org.apache.hc.client5.http.utils.DateUtils;
|
||||||
import org.apache.hc.core5.http.ContentType;
|
import org.apache.hc.core5.http.ContentType;
|
||||||
import org.apache.hc.core5.http.Header;
|
import org.apache.hc.core5.http.Header;
|
||||||
import org.apache.hc.core5.http.HttpHeaders;
|
import org.apache.hc.core5.http.HttpHeaders;
|
||||||
|
import org.apache.hc.core5.http.HttpHost;
|
||||||
import org.apache.hc.core5.http.HttpRequest;
|
import org.apache.hc.core5.http.HttpRequest;
|
||||||
import org.apache.hc.core5.http.HttpResponse;
|
import org.apache.hc.core5.http.HttpResponse;
|
||||||
import org.apache.hc.core5.http.HttpStatus;
|
import org.apache.hc.core5.http.HttpStatus;
|
||||||
|
@ -64,6 +64,7 @@ public class TestHttpCacheEntryFactory {
|
||||||
private Instant twoSecondsAgo;
|
private Instant twoSecondsAgo;
|
||||||
private Instant eightSecondsAgo;
|
private Instant eightSecondsAgo;
|
||||||
private Instant tenSecondsAgo;
|
private Instant tenSecondsAgo;
|
||||||
|
private HttpHost host;
|
||||||
private HttpRequest request;
|
private HttpRequest request;
|
||||||
private HttpResponse response;
|
private HttpResponse response;
|
||||||
private HttpCacheEntryFactory impl;
|
private HttpCacheEntryFactory impl;
|
||||||
|
@ -79,6 +80,7 @@ public class TestHttpCacheEntryFactory {
|
||||||
eightSecondsAgo = now.minusSeconds(8);
|
eightSecondsAgo = now.minusSeconds(8);
|
||||||
tenSecondsAgo = now.minusSeconds(10);
|
tenSecondsAgo = now.minusSeconds(10);
|
||||||
|
|
||||||
|
host = new HttpHost("foo.example.com");
|
||||||
request = new BasicHttpRequest("GET", "/stuff");
|
request = new BasicHttpRequest("GET", "/stuff");
|
||||||
response = new BasicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified");
|
response = new BasicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified");
|
||||||
|
|
||||||
|
@ -231,19 +233,19 @@ public class TestHttpCacheEntryFactory {
|
||||||
new BasicHeader("X-custom", "my stuff")
|
new BasicHeader("X-custom", "my stuff")
|
||||||
);
|
);
|
||||||
|
|
||||||
final Map<String, String> variants = new HashMap<>();
|
final Set<String> variants = new HashSet<>();
|
||||||
variants.put("key1", "variant1");
|
variants.add("variant1");
|
||||||
variants.put("key2", "variant2");
|
variants.add("variant2");
|
||||||
variants.put("key3", "variant3");
|
variants.add("variant3");
|
||||||
|
|
||||||
final HttpCacheEntry newEntry = impl.createRoot(tenSecondsAgo, oneSecondAgo, request, response, variants);
|
final HttpCacheEntry newEntry = impl.createRoot(tenSecondsAgo, oneSecondAgo, host, request, response, variants);
|
||||||
|
|
||||||
MatcherAssert.assertThat(newEntry, HttpCacheEntryMatcher.equivalent(
|
MatcherAssert.assertThat(newEntry, HttpCacheEntryMatcher.equivalent(
|
||||||
HttpTestUtils.makeCacheEntry(
|
HttpTestUtils.makeCacheEntry(
|
||||||
tenSecondsAgo,
|
tenSecondsAgo,
|
||||||
oneSecondAgo,
|
oneSecondAgo,
|
||||||
Method.GET,
|
Method.GET,
|
||||||
"/stuff",
|
"http://foo.example.com:80/stuff",
|
||||||
new Header[]{
|
new Header[]{
|
||||||
new BasicHeader("X-custom", "my stuff"),
|
new BasicHeader("X-custom", "my stuff"),
|
||||||
new BasicHeader(HttpHeaders.ACCEPT, "stuff"),
|
new BasicHeader(HttpHeaders.ACCEPT, "stuff"),
|
||||||
|
@ -259,7 +261,6 @@ public class TestHttpCacheEntryFactory {
|
||||||
variants
|
variants
|
||||||
)));
|
)));
|
||||||
|
|
||||||
Assertions.assertTrue(newEntry.isVariantRoot());
|
|
||||||
Assertions.assertTrue(newEntry.hasVariants());
|
Assertions.assertTrue(newEntry.hasVariants());
|
||||||
Assertions.assertNull(newEntry.getResource());
|
Assertions.assertNull(newEntry.getResource());
|
||||||
}
|
}
|
||||||
|
@ -282,14 +283,14 @@ public class TestHttpCacheEntryFactory {
|
||||||
);
|
);
|
||||||
|
|
||||||
final Resource resource = HttpTestUtils.makeRandomResource(128);
|
final Resource resource = HttpTestUtils.makeRandomResource(128);
|
||||||
final HttpCacheEntry newEntry = impl.create(tenSecondsAgo, oneSecondAgo, request, response, resource);
|
final HttpCacheEntry newEntry = impl.create(tenSecondsAgo, oneSecondAgo, host, request, response, resource);
|
||||||
|
|
||||||
MatcherAssert.assertThat(newEntry, HttpCacheEntryMatcher.equivalent(
|
MatcherAssert.assertThat(newEntry, HttpCacheEntryMatcher.equivalent(
|
||||||
HttpTestUtils.makeCacheEntry(
|
HttpTestUtils.makeCacheEntry(
|
||||||
tenSecondsAgo,
|
tenSecondsAgo,
|
||||||
oneSecondAgo,
|
oneSecondAgo,
|
||||||
Method.GET,
|
Method.GET,
|
||||||
"/stuff",
|
"http://foo.example.com:80/stuff",
|
||||||
new Header[]{
|
new Header[]{
|
||||||
new BasicHeader("X-custom", "my stuff"),
|
new BasicHeader("X-custom", "my stuff"),
|
||||||
new BasicHeader(HttpHeaders.ACCEPT, "stuff"),
|
new BasicHeader(HttpHeaders.ACCEPT, "stuff"),
|
||||||
|
@ -304,7 +305,6 @@ public class TestHttpCacheEntryFactory {
|
||||||
resource
|
resource
|
||||||
)));
|
)));
|
||||||
|
|
||||||
Assertions.assertFalse(newEntry.isVariantRoot());
|
|
||||||
Assertions.assertFalse(newEntry.hasVariants());
|
Assertions.assertFalse(newEntry.hasVariants());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,7 +361,6 @@ public class TestHttpCacheEntryFactory {
|
||||||
resource
|
resource
|
||||||
)));
|
)));
|
||||||
|
|
||||||
Assertions.assertFalse(updatedEntry.isVariantRoot());
|
|
||||||
Assertions.assertFalse(updatedEntry.hasVariants());
|
Assertions.assertFalse(updatedEntry.hasVariants());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,8 +30,8 @@ import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
||||||
import org.apache.hc.client5.http.cache.Resource;
|
import org.apache.hc.client5.http.cache.Resource;
|
||||||
|
@ -55,7 +55,7 @@ public class HttpCacheEntryMatcher extends BaseMatcher<HttpCacheEntry> {
|
||||||
try {
|
try {
|
||||||
final HttpCacheEntry otherValue = (HttpCacheEntry) item;
|
final HttpCacheEntry otherValue = (HttpCacheEntry) item;
|
||||||
|
|
||||||
if (!mapEqual(expectedValue.getVariantMap(), otherValue.getVariantMap())) {
|
if (!setEqual(expectedValue.getVariants(), otherValue.getVariants())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!Objects.equals(expectedValue.getRequestMethod(), otherValue.getRequestMethod())) {
|
if (!Objects.equals(expectedValue.getRequestMethod(), otherValue.getRequestMethod())) {
|
||||||
|
@ -114,12 +114,11 @@ public class HttpCacheEntryMatcher extends BaseMatcher<HttpCacheEntry> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean mapEqual(final Map<?, ?> expected, final Map<?, ?> actual) {
|
private static boolean setEqual(final Set<?> expected, final Set<?> actual) {
|
||||||
if (expected.size() != actual.size()) {
|
if (expected.size() != actual.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return expected.entrySet().stream()
|
return actual.containsAll(expected);
|
||||||
.allMatch(e -> Objects.equals(e.getValue(), actual.get(e.getKey())));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -29,7 +29,7 @@ package org.apache.hc.client5.http.impl.cache;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Map;
|
import java.util.Collection;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
@ -229,14 +229,14 @@ public class HttpTestUtils {
|
||||||
final Header[] requestHeaders,
|
final Header[] requestHeaders,
|
||||||
final int status,
|
final int status,
|
||||||
final Header[] responseHeaders,
|
final Header[] responseHeaders,
|
||||||
final Map<String, String> variantMap) {
|
final Collection<String> variants) {
|
||||||
return new HttpCacheEntry(
|
return new HttpCacheEntry(
|
||||||
requestDate,
|
requestDate,
|
||||||
responseDate,
|
responseDate,
|
||||||
method.name(), requestUri, headers(requestHeaders),
|
method.name(), requestUri, headers(requestHeaders),
|
||||||
status, headers(responseHeaders),
|
status, headers(responseHeaders),
|
||||||
null,
|
null,
|
||||||
variantMap);
|
variants);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpCacheEntry makeCacheEntry(final Instant requestDate,
|
public static HttpCacheEntry makeCacheEntry(final Instant requestDate,
|
||||||
|
@ -260,13 +260,13 @@ public class HttpTestUtils {
|
||||||
final Instant responseDate,
|
final Instant responseDate,
|
||||||
final int status,
|
final int status,
|
||||||
final Header[] responseHeaders,
|
final Header[] responseHeaders,
|
||||||
final Map<String, String> variantMap) {
|
final Collection<String> variants) {
|
||||||
return makeCacheEntry(
|
return makeCacheEntry(
|
||||||
requestDate,
|
requestDate,
|
||||||
responseDate,
|
responseDate,
|
||||||
Method.GET, "/", null,
|
Method.GET, "/", null,
|
||||||
status, responseHeaders,
|
status, responseHeaders,
|
||||||
variantMap);
|
variants);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpCacheEntry makeCacheEntry(final Instant requestDate,
|
public static HttpCacheEntry makeCacheEntry(final Instant requestDate,
|
||||||
|
@ -311,13 +311,13 @@ public class HttpTestUtils {
|
||||||
public static HttpCacheEntry makeCacheEntry(final Instant requestDate,
|
public static HttpCacheEntry makeCacheEntry(final Instant requestDate,
|
||||||
final Instant responseDate,
|
final Instant responseDate,
|
||||||
final Header[] headers,
|
final Header[] headers,
|
||||||
final Map<String,String> variantMap) {
|
final Collection<String> variants) {
|
||||||
return makeCacheEntry(requestDate, responseDate, Method.GET, "/", null, HttpStatus.SC_OK, headers, variantMap);
|
return makeCacheEntry(requestDate, responseDate, Method.GET, "/", null, HttpStatus.SC_OK, headers, variants);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpCacheEntry makeCacheEntry(final Map<String, String> variantMap) {
|
public static HttpCacheEntry makeCacheEntry(final Collection<String> variants) {
|
||||||
final Instant now = Instant.now();
|
final Instant now = Instant.now();
|
||||||
return makeCacheEntry(now, now, new Header[] {}, variantMap);
|
return makeCacheEntry(now, now, new Header[] {}, variants);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpCacheEntry makeCacheEntry(final Header[] headers, final byte[] bytes) {
|
public static HttpCacheEntry makeCacheEntry(final Header[] headers, final byte[] bytes) {
|
||||||
|
|
|
@ -31,8 +31,8 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.HashMap;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.utils.DateUtils;
|
import org.apache.hc.client5.http.utils.DateUtils;
|
||||||
|
@ -111,15 +111,15 @@ public class TestBasicHttpAsyncCache {
|
||||||
public void testInvalidatesUnsafeRequestsWithVariants() throws Exception {
|
public void testInvalidatesUnsafeRequestsWithVariants() throws Exception {
|
||||||
final HttpRequest request = new BasicHttpRequest("POST", "/path");
|
final HttpRequest request = new BasicHttpRequest("POST", "/path");
|
||||||
final String rootKey = CacheKeyGenerator.INSTANCE.generateKey(host, request);
|
final String rootKey = CacheKeyGenerator.INSTANCE.generateKey(host, request);
|
||||||
|
final Set<String> variants = new HashSet<>();
|
||||||
|
variants.add("{var1}");
|
||||||
|
variants.add("{var2}");
|
||||||
final String variantKey1 = "{var1}" + rootKey;
|
final String variantKey1 = "{var1}" + rootKey;
|
||||||
final String variantKey2 = "{var2}" + rootKey;
|
final String variantKey2 = "{var2}" + rootKey;
|
||||||
final Map<String, String> variantMap = new HashMap<>();
|
|
||||||
variantMap.put("{var1}", variantKey1);
|
|
||||||
variantMap.put("{var2}", variantKey2);
|
|
||||||
|
|
||||||
final HttpResponse response = HttpTestUtils.make200Response();
|
final HttpResponse response = HttpTestUtils.make200Response();
|
||||||
|
|
||||||
mockStorage.putEntry(rootKey, HttpTestUtils.makeCacheEntry(variantMap));
|
mockStorage.putEntry(rootKey, HttpTestUtils.makeCacheEntry(variants));
|
||||||
mockStorage.putEntry(variantKey1, HttpTestUtils.makeCacheEntry());
|
mockStorage.putEntry(variantKey1, HttpTestUtils.makeCacheEntry());
|
||||||
mockStorage.putEntry(variantKey2, HttpTestUtils.makeCacheEntry());
|
mockStorage.putEntry(variantKey2, HttpTestUtils.makeCacheEntry());
|
||||||
|
|
||||||
|
|
|
@ -37,9 +37,10 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.HashMap;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
||||||
|
@ -88,7 +89,7 @@ public class TestBasicHttpCache {
|
||||||
|
|
||||||
final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req);
|
final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req);
|
||||||
|
|
||||||
impl.store(req, resp, now, now, key, entry);
|
impl.store(host, req, resp, now, now, key, entry);
|
||||||
assertSame(entry, backing.map.get(key));
|
assertSame(entry, backing.map.get(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,10 +212,10 @@ public class TestBasicHttpCache {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetVariantsRootNonExistentVariants() throws Exception {
|
public void testGetVariantsRootNonExistentVariants() throws Exception {
|
||||||
final Map<String, String> variantMap = new HashMap<>();
|
final Set<String> varinats = new HashSet<>();
|
||||||
variantMap.put("variant1", "variant-key-1");
|
varinats.add("variant1");
|
||||||
variantMap.put("variant2", "variant-key-2");
|
varinats.add("variant2");
|
||||||
final HttpCacheEntry root = HttpTestUtils.makeCacheEntry(variantMap);
|
final HttpCacheEntry root = HttpTestUtils.makeCacheEntry(varinats);
|
||||||
final List<CacheHit> variants = impl.getVariants(new CacheHit("root-key", root));
|
final List<CacheHit> variants = impl.getVariants(new CacheHit("root-key", root));
|
||||||
|
|
||||||
assertNotNull(variants);
|
assertNotNull(variants);
|
||||||
|
@ -224,18 +225,20 @@ public class TestBasicHttpCache {
|
||||||
@Test
|
@Test
|
||||||
public void testGetVariantCacheEntriesReturnsAllVariants() throws Exception {
|
public void testGetVariantCacheEntriesReturnsAllVariants() throws Exception {
|
||||||
final HttpHost host = new HttpHost("foo.example.com");
|
final HttpHost host = new HttpHost("foo.example.com");
|
||||||
final HttpRequest req1 = new HttpGet("http://foo.example.com/bar");
|
final URI uri = new URI("http://foo.example.com/bar");
|
||||||
|
final HttpRequest req1 = new HttpGet(uri);
|
||||||
req1.setHeader("Accept-Encoding", "gzip");
|
req1.setHeader("Accept-Encoding", "gzip");
|
||||||
|
|
||||||
|
final String rootKey = CacheKeyGenerator.INSTANCE.generateKey(uri);
|
||||||
|
|
||||||
final HttpResponse resp1 = HttpTestUtils.make200Response();
|
final HttpResponse resp1 = HttpTestUtils.make200Response();
|
||||||
resp1.setHeader("Date", DateUtils.formatStandardDate(now));
|
resp1.setHeader("Date", DateUtils.formatStandardDate(now));
|
||||||
resp1.setHeader("Cache-Control", "max-age=3600, public");
|
resp1.setHeader("Cache-Control", "max-age=3600, public");
|
||||||
resp1.setHeader("ETag", "\"etag1\"");
|
resp1.setHeader("ETag", "\"etag1\"");
|
||||||
resp1.setHeader("Vary", "Accept-Encoding");
|
resp1.setHeader("Vary", "Accept-Encoding");
|
||||||
resp1.setHeader("Content-Encoding","gzip");
|
resp1.setHeader("Content-Encoding","gzip");
|
||||||
resp1.setHeader("Vary", "Accept-Encoding");
|
|
||||||
|
|
||||||
final HttpRequest req2 = new HttpGet("http://foo.example.com/bar");
|
final HttpRequest req2 = new HttpGet(uri);
|
||||||
req2.setHeader("Accept-Encoding", "identity");
|
req2.setHeader("Accept-Encoding", "identity");
|
||||||
|
|
||||||
final HttpResponse resp2 = HttpTestUtils.make200Response();
|
final HttpResponse resp2 = HttpTestUtils.make200Response();
|
||||||
|
@ -244,23 +247,22 @@ public class TestBasicHttpCache {
|
||||||
resp2.setHeader("ETag", "\"etag2\"");
|
resp2.setHeader("ETag", "\"etag2\"");
|
||||||
resp2.setHeader("Vary", "Accept-Encoding");
|
resp2.setHeader("Vary", "Accept-Encoding");
|
||||||
resp2.setHeader("Content-Encoding","gzip");
|
resp2.setHeader("Content-Encoding","gzip");
|
||||||
resp2.setHeader("Vary", "Accept-Encoding");
|
|
||||||
|
|
||||||
final CacheHit hit1 = impl.store(host, req1, resp1, null, now, now);
|
final CacheHit hit1 = impl.store(host, req1, resp1, null, now, now);
|
||||||
final CacheHit hit2 = impl.store(host, req2, resp2, null, now, now);
|
final CacheHit hit2 = impl.store(host, req2, resp2, null, now, now);
|
||||||
|
|
||||||
final Map<String, String> variantMap = new HashMap<>();
|
final Set<String> variants = new HashSet<>();
|
||||||
variantMap.put("variant-1", hit1.variantKey);
|
variants.add("{accept-encoding=gzip}");
|
||||||
variantMap.put("variant-2", hit2.variantKey);
|
variants.add("{accept-encoding=identity}");
|
||||||
|
|
||||||
final Map<String, HttpCacheEntry> variants = impl.getVariants(new CacheHit(hit1.rootKey,
|
final Map<String, HttpCacheEntry> variantMap = impl.getVariants(new CacheHit(hit1.rootKey,
|
||||||
HttpTestUtils.makeCacheEntry(variantMap))).stream()
|
HttpTestUtils.makeCacheEntry(variants))).stream()
|
||||||
.collect(Collectors.toMap(CacheHit::getEntryKey, e -> e.entry));
|
.collect(Collectors.toMap(CacheHit::getEntryKey, e -> e.entry));
|
||||||
|
|
||||||
assertNotNull(variants);
|
assertNotNull(variantMap);
|
||||||
assertEquals(2, variants.size());
|
assertEquals(2, variantMap.size());
|
||||||
MatcherAssert.assertThat(variants.get(hit1.getEntryKey()), HttpCacheEntryMatcher.equivalent(hit1.entry));
|
MatcherAssert.assertThat(variantMap.get("{accept-encoding=gzip}"), HttpCacheEntryMatcher.equivalent(hit1.entry));
|
||||||
MatcherAssert.assertThat(variants.get(hit2.getEntryKey()), HttpCacheEntryMatcher.equivalent(hit2.entry));
|
MatcherAssert.assertThat(variantMap.get("{accept-encoding=identity}"), HttpCacheEntryMatcher.equivalent(hit2.entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -300,15 +302,15 @@ public class TestBasicHttpCache {
|
||||||
public void testInvalidatesUnsafeRequestsWithVariants() throws Exception {
|
public void testInvalidatesUnsafeRequestsWithVariants() throws Exception {
|
||||||
final HttpRequest request = new BasicHttpRequest("POST","/path");
|
final HttpRequest request = new BasicHttpRequest("POST","/path");
|
||||||
final String rootKey = CacheKeyGenerator.INSTANCE.generateKey(host, request);
|
final String rootKey = CacheKeyGenerator.INSTANCE.generateKey(host, request);
|
||||||
|
final Set<String> variants = new HashSet<>();
|
||||||
|
variants.add("{var1}");
|
||||||
|
variants.add("{var2}");
|
||||||
final String variantKey1 = "{var1}" + rootKey;
|
final String variantKey1 = "{var1}" + rootKey;
|
||||||
final String variantKey2 = "{var2}" + rootKey;
|
final String variantKey2 = "{var2}" + rootKey;
|
||||||
final Map<String, String> variantMap = new HashMap<>();
|
|
||||||
variantMap.put("{var1}", variantKey1);
|
|
||||||
variantMap.put("{var2}", variantKey2);
|
|
||||||
|
|
||||||
final HttpResponse response = HttpTestUtils.make200Response();
|
final HttpResponse response = HttpTestUtils.make200Response();
|
||||||
|
|
||||||
backing.putEntry(rootKey, HttpTestUtils.makeCacheEntry(variantMap));
|
backing.putEntry(rootKey, HttpTestUtils.makeCacheEntry(variants));
|
||||||
backing.putEntry(variantKey1, HttpTestUtils.makeCacheEntry());
|
backing.putEntry(variantKey1, HttpTestUtils.makeCacheEntry());
|
||||||
backing.putEntry(variantKey2, HttpTestUtils.makeCacheEntry());
|
backing.putEntry(variantKey2, HttpTestUtils.makeCacheEntry());
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,8 @@ import java.io.ObjectOutputStream;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
||||||
import org.apache.hc.client5.http.cache.HttpCacheStorageEntry;
|
import org.apache.hc.client5.http.cache.HttpCacheStorageEntry;
|
||||||
|
@ -274,15 +274,15 @@ public class TestByteArrayCacheEntrySerializer {
|
||||||
for (int i = 0; i < headers.length; i++) {
|
for (int i = 0; i < headers.length; i++) {
|
||||||
headers[i] = new BasicHeader("header" + i, "value" + i);
|
headers[i] = new BasicHeader("header" + i, "value" + i);
|
||||||
}
|
}
|
||||||
final Map<String,String> variantMap = new HashMap<>();
|
final Set<String> variants = new HashSet<>();
|
||||||
variantMap.put("test variant 1","true");
|
variants.add("test variant 1");
|
||||||
variantMap.put("test variant 2","true");
|
variants.add("test variant 2");
|
||||||
final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(
|
final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(
|
||||||
Instant.now(),
|
Instant.now(),
|
||||||
Instant.now(),
|
Instant.now(),
|
||||||
HttpStatus.SC_OK,
|
HttpStatus.SC_OK,
|
||||||
headers,
|
headers,
|
||||||
variantMap);
|
variants);
|
||||||
|
|
||||||
return new HttpCacheStorageEntry(key, cacheEntry);
|
return new HttpCacheStorageEntry(key, cacheEntry);
|
||||||
}
|
}
|
||||||
|
@ -292,15 +292,15 @@ public class TestByteArrayCacheEntrySerializer {
|
||||||
for (int i = 0; i < headers.length; i++) {
|
for (int i = 0; i < headers.length; i++) {
|
||||||
headers[i] = new BasicHeader("header" + i, "value" + i);
|
headers[i] = new BasicHeader("header" + i, "value" + i);
|
||||||
}
|
}
|
||||||
final Map<String,String> variantMap = new HashMap<>();
|
final Set<String> variants = new HashSet<>();
|
||||||
variantMap.put("test variant 1","true");
|
variants.add("test variant 1");
|
||||||
variantMap.put("test variant 2","true");
|
variants.add("test variant 2");
|
||||||
final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(
|
final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(
|
||||||
Instant.now(),
|
Instant.now(),
|
||||||
Instant.now(),
|
Instant.now(),
|
||||||
HttpStatus.SC_OK,
|
HttpStatus.SC_OK,
|
||||||
headers,
|
headers,
|
||||||
variantMap);
|
variants);
|
||||||
|
|
||||||
return new HttpCacheStorageEntry(key, cacheEntry);
|
return new HttpCacheStorageEntry(key, cacheEntry);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1432,7 +1432,7 @@ public class TestCachingExecChain {
|
||||||
headers,
|
headers,
|
||||||
new HeapResource(body.getBytes(StandardCharsets.UTF_8)));
|
new HeapResource(body.getBytes(StandardCharsets.UTF_8)));
|
||||||
|
|
||||||
Mockito.when(mockCache.update(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()))
|
Mockito.when(mockCache.update(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()))
|
||||||
.thenReturn(new CacheHit("key", cacheEntry));
|
.thenReturn(new CacheHit("key", cacheEntry));
|
||||||
|
|
||||||
// Call cacheAndReturnResponse with 304 Not Modified response
|
// Call cacheAndReturnResponse with 304 Not Modified response
|
||||||
|
@ -1441,6 +1441,7 @@ public class TestCachingExecChain {
|
||||||
// Verify cache entry is updated
|
// Verify cache entry is updated
|
||||||
Mockito.verify(mockCache).update(
|
Mockito.verify(mockCache).update(
|
||||||
Mockito.any(),
|
Mockito.any(),
|
||||||
|
Mockito.same(host),
|
||||||
Mockito.same(request),
|
Mockito.same(request),
|
||||||
Mockito.same(backendResponse),
|
Mockito.same(backendResponse),
|
||||||
Mockito.eq(requestSent),
|
Mockito.eq(requestSent),
|
||||||
|
|
|
@ -32,8 +32,8 @@ import java.io.InputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.HashMap;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
||||||
import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer;
|
import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer;
|
||||||
|
@ -378,14 +378,14 @@ public class TestHttpByteArrayCacheEntrySerializer {
|
||||||
public void testSimpleVariantMap() throws Exception {
|
public void testSimpleVariantMap() throws Exception {
|
||||||
final String content = "Hello World";
|
final String content = "Hello World";
|
||||||
final ContentType contentType = ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8);
|
final ContentType contentType = ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8);
|
||||||
final Map<String, String> variantMap = new HashMap<>();
|
final Set<String> variants = new HashSet<>();
|
||||||
variantMap.put("{Accept-Encoding=gzip}","{Accept-Encoding=gzip}https://example.com:1234/foo");
|
variants.add("{Accept-Encoding=gzip}");
|
||||||
variantMap.put("{Accept-Encoding=compress}","{Accept-Encoding=compress}https://example.com:1234/foo");
|
variants.add("{Accept-Encoding=compress}");
|
||||||
final HttpCacheEntry cacheEntry = new HttpCacheEntry(Instant.now(), Instant.now(),
|
final HttpCacheEntry cacheEntry = new HttpCacheEntry(Instant.now(), Instant.now(),
|
||||||
"GET", "/stuff", HttpTestUtils.headers(),
|
"GET", "/stuff", HttpTestUtils.headers(),
|
||||||
HttpStatus.SC_OK, HttpTestUtils.headers(new BasicHeader(HttpHeaders.CONTENT_TYPE, contentType.toString())),
|
HttpStatus.SC_OK, HttpTestUtils.headers(new BasicHeader(HttpHeaders.CONTENT_TYPE, contentType.toString())),
|
||||||
new HeapResource(content.getBytes(contentType.getCharset())),
|
new HeapResource(content.getBytes(contentType.getCharset())),
|
||||||
variantMap);
|
variants);
|
||||||
final HttpCacheStorageEntry storageEntry = new HttpCacheStorageEntry("unique-cache-key", cacheEntry);
|
final HttpCacheStorageEntry storageEntry = new HttpCacheStorageEntry("unique-cache-key", cacheEntry);
|
||||||
final byte[] serialized = httpCacheEntrySerializer.serialize(storageEntry);
|
final byte[] serialized = httpCacheEntrySerializer.serialize(storageEntry);
|
||||||
|
|
||||||
|
|
|
@ -1723,6 +1723,7 @@ public class TestProtocolRequirements {
|
||||||
Mockito.any(),
|
Mockito.any(),
|
||||||
Mockito.any(),
|
Mockito.any(),
|
||||||
Mockito.any(),
|
Mockito.any(),
|
||||||
|
Mockito.any(),
|
||||||
Mockito.any()))
|
Mockito.any()))
|
||||||
.thenReturn(new CacheHit("key", HttpTestUtils.makeCacheEntry()));
|
.thenReturn(new CacheHit("key", HttpTestUtils.makeCacheEntry()));
|
||||||
|
|
||||||
|
@ -1730,6 +1731,7 @@ public class TestProtocolRequirements {
|
||||||
|
|
||||||
Mockito.verify(mockCache).update(
|
Mockito.verify(mockCache).update(
|
||||||
Mockito.any(),
|
Mockito.any(),
|
||||||
|
Mockito.eq(host),
|
||||||
RequestEquivalent.eq(request),
|
RequestEquivalent.eq(request),
|
||||||
ResponseEquivalent.eq(notModified),
|
ResponseEquivalent.eq(notModified),
|
||||||
Mockito.any(),
|
Mockito.any(),
|
||||||
|
|
Loading…
Reference in New Issue