Removed dependency on classic (blocking) I/O APIs from HttpCache
This commit is contained in:
parent
73c67f221d
commit
20f4290d01
|
@ -27,7 +27,6 @@
|
|||
package org.apache.hc.client5.http.impl.cache;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
@ -43,16 +42,10 @@ import org.apache.hc.client5.http.cache.HttpCacheUpdateCallback;
|
|||
import org.apache.hc.client5.http.cache.HttpCacheUpdateException;
|
||||
import org.apache.hc.client5.http.cache.Resource;
|
||||
import org.apache.hc.client5.http.cache.ResourceFactory;
|
||||
import org.apache.hc.core5.http.ClassicHttpResponse;
|
||||
import org.apache.hc.core5.http.Header;
|
||||
import org.apache.hc.core5.http.HttpEntity;
|
||||
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.HttpResponse;
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
|
||||
import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
|
||||
import org.apache.hc.core5.util.ByteArrayBuffer;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
@ -65,9 +58,7 @@ class BasicHttpCache implements HttpCache {
|
|||
|
||||
private final CacheKeyGenerator uriExtractor;
|
||||
private final ResourceFactory resourceFactory;
|
||||
private final long maxObjectSizeBytes;
|
||||
private final CacheEntryUpdater cacheEntryUpdater;
|
||||
private final CachedHttpResponseGenerator responseGenerator;
|
||||
private final HttpCacheInvalidator cacheInvalidator;
|
||||
private final HttpCacheStorage storage;
|
||||
|
||||
|
@ -76,14 +67,11 @@ class BasicHttpCache implements HttpCache {
|
|||
public BasicHttpCache(
|
||||
final ResourceFactory resourceFactory,
|
||||
final HttpCacheStorage storage,
|
||||
final CacheConfig config,
|
||||
final CacheKeyGenerator uriExtractor,
|
||||
final HttpCacheInvalidator cacheInvalidator) {
|
||||
this.resourceFactory = resourceFactory;
|
||||
this.uriExtractor = uriExtractor;
|
||||
this.cacheEntryUpdater = new CacheEntryUpdater(resourceFactory);
|
||||
this.maxObjectSizeBytes = config.getMaxObjectSize();
|
||||
this.responseGenerator = new CachedHttpResponseGenerator();
|
||||
this.storage = storage;
|
||||
this.cacheInvalidator = cacheInvalidator;
|
||||
}
|
||||
|
@ -91,21 +79,16 @@ class BasicHttpCache implements HttpCache {
|
|||
public BasicHttpCache(
|
||||
final ResourceFactory resourceFactory,
|
||||
final HttpCacheStorage storage,
|
||||
final CacheConfig config,
|
||||
final CacheKeyGenerator uriExtractor) {
|
||||
this( resourceFactory, storage, config, uriExtractor,
|
||||
new CacheInvalidator(uriExtractor, storage));
|
||||
this(resourceFactory, storage, uriExtractor, new CacheInvalidator(uriExtractor, storage));
|
||||
}
|
||||
|
||||
public BasicHttpCache(
|
||||
final ResourceFactory resourceFactory,
|
||||
final HttpCacheStorage storage,
|
||||
final CacheConfig config) {
|
||||
this( resourceFactory, storage, config, new CacheKeyGenerator());
|
||||
public BasicHttpCache(final ResourceFactory resourceFactory, final HttpCacheStorage storage) {
|
||||
this( resourceFactory, storage, new CacheKeyGenerator());
|
||||
}
|
||||
|
||||
public BasicHttpCache(final CacheConfig config) {
|
||||
this(new HeapResourceFactory(), new BasicHttpCacheStorage(config), config);
|
||||
this(new HeapResourceFactory(), new BasicHttpCacheStorage(config));
|
||||
}
|
||||
|
||||
public BasicHttpCache() {
|
||||
|
@ -194,42 +177,6 @@ class BasicHttpCache implements HttpCache {
|
|||
}
|
||||
}
|
||||
|
||||
boolean isIncompleteResponse(final HttpResponse resp, final Resource resource) {
|
||||
final int status = resp.getCode();
|
||||
if (status != HttpStatus.SC_OK
|
||||
&& status != HttpStatus.SC_PARTIAL_CONTENT) {
|
||||
return false;
|
||||
}
|
||||
final Header hdr = resp.getFirstHeader(HttpHeaders.CONTENT_LENGTH);
|
||||
if (hdr == null) {
|
||||
return false;
|
||||
}
|
||||
final int contentLength;
|
||||
try {
|
||||
contentLength = Integer.parseInt(hdr.getValue());
|
||||
} catch (final NumberFormatException nfe) {
|
||||
return false;
|
||||
}
|
||||
if (resource == null) {
|
||||
return false;
|
||||
}
|
||||
return (resource.length() < contentLength);
|
||||
}
|
||||
|
||||
ClassicHttpResponse generateIncompleteResponseError(
|
||||
final HttpResponse response, final Resource resource) {
|
||||
final Integer contentLength = Integer.valueOf(response.getFirstHeader(HttpHeaders.CONTENT_LENGTH).getValue());
|
||||
final ClassicHttpResponse error = new BasicClassicHttpResponse(HttpStatus.SC_BAD_GATEWAY, "Bad Gateway");
|
||||
error.setHeader("Content-Type","text/plain;charset=UTF-8");
|
||||
final String msg = String.format("Received incomplete response " +
|
||||
"with Content-Length %d but actual body length %d",
|
||||
contentLength, resource.length());
|
||||
final byte[] msgBytes = msg.getBytes();
|
||||
error.setHeader("Content-Length", Integer.toString(msgBytes.length));
|
||||
error.setEntity(new ByteArrayEntity(msgBytes));
|
||||
return error;
|
||||
}
|
||||
|
||||
HttpCacheEntry doGetUpdatedParentEntry(
|
||||
final String requestId,
|
||||
final HttpCacheEntry existing,
|
||||
|
@ -284,37 +231,19 @@ class BasicHttpCache implements HttpCache {
|
|||
return updatedEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassicHttpResponse cacheAndReturnResponse(
|
||||
public HttpCacheEntry createCacheEntry(
|
||||
final HttpHost host,
|
||||
final HttpRequest request,
|
||||
final ClassicHttpResponse originResponse,
|
||||
final HttpResponse originResponse,
|
||||
final ByteArrayBuffer content,
|
||||
final Date requestSent,
|
||||
final Date responseReceived) throws IOException {
|
||||
final Resource resource;
|
||||
final HttpEntity entity = originResponse.getEntity();
|
||||
if (entity != null) {
|
||||
final ByteArrayBuffer buf = new ByteArrayBuffer(1024);
|
||||
final InputStream instream = entity.getContent();
|
||||
final byte[] tmp = new byte[2048];
|
||||
long total = 0;
|
||||
int l;
|
||||
while ((l = instream.read(tmp)) != -1) {
|
||||
buf.append(tmp, 0, l);
|
||||
total += l;
|
||||
if (total > maxObjectSizeBytes) {
|
||||
originResponse.setEntity(new CombinedEntity(entity, buf));
|
||||
return originResponse;
|
||||
}
|
||||
}
|
||||
resource = resourceFactory.generate(request.getRequestUri(), buf.array(), 0, buf.length());
|
||||
if (content != null) {
|
||||
resource = resourceFactory.generate(request.getRequestUri(), content.array(), 0, content.length());
|
||||
} else {
|
||||
resource = null;
|
||||
}
|
||||
originResponse.close();
|
||||
if (isIncompleteResponse(originResponse, resource)) {
|
||||
return generateIncompleteResponseError(originResponse, resource);
|
||||
}
|
||||
final HttpCacheEntry entry = new HttpCacheEntry(
|
||||
requestSent,
|
||||
responseReceived,
|
||||
|
@ -322,7 +251,7 @@ class BasicHttpCache implements HttpCache {
|
|||
originResponse.getAllHeaders(),
|
||||
resource);
|
||||
storeInCache(host, request, entry);
|
||||
return responseGenerator.generateResponse(request, entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
package org.apache.hc.client5.http.impl.cache;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
@ -49,8 +50,10 @@ import org.apache.hc.core5.annotation.Contract;
|
|||
import org.apache.hc.core5.annotation.ThreadingBehavior;
|
||||
import org.apache.hc.core5.http.ClassicHttpRequest;
|
||||
import org.apache.hc.core5.http.ClassicHttpResponse;
|
||||
import org.apache.hc.core5.http.ContentType;
|
||||
import org.apache.hc.core5.http.Header;
|
||||
import org.apache.hc.core5.http.HeaderElement;
|
||||
import org.apache.hc.core5.http.HttpEntity;
|
||||
import org.apache.hc.core5.http.HttpException;
|
||||
import org.apache.hc.core5.http.HttpHeaders;
|
||||
import org.apache.hc.core5.http.HttpHost;
|
||||
|
@ -60,12 +63,14 @@ import org.apache.hc.core5.http.HttpResponse;
|
|||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.apache.hc.core5.http.HttpVersion;
|
||||
import org.apache.hc.core5.http.ProtocolVersion;
|
||||
import org.apache.hc.core5.http.io.entity.StringEntity;
|
||||
import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
|
||||
import org.apache.hc.core5.http.message.MessageSupport;
|
||||
import org.apache.hc.core5.http.protocol.HttpContext;
|
||||
import org.apache.hc.core5.http.protocol.HttpCoreContext;
|
||||
import org.apache.hc.core5.net.URIAuthority;
|
||||
import org.apache.hc.core5.util.Args;
|
||||
import org.apache.hc.core5.util.ByteArrayBuffer;
|
||||
import org.apache.hc.core5.util.VersionInfo;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
@ -155,7 +160,7 @@ public class CachingExec implements ExecChainHandler {
|
|||
final ResourceFactory resourceFactory,
|
||||
final HttpCacheStorage storage,
|
||||
final CacheConfig config) {
|
||||
this(new BasicHttpCache(resourceFactory, storage, config), config);
|
||||
this(new BasicHttpCache(resourceFactory, storage), config);
|
||||
}
|
||||
|
||||
public CachingExec() {
|
||||
|
@ -423,7 +428,7 @@ public class CachingExec implements ExecChainHandler {
|
|||
final ClassicHttpRequest request,
|
||||
final HttpContext context,
|
||||
final HttpCacheEntry entry,
|
||||
final Date now) {
|
||||
final Date now) throws IOException {
|
||||
if (staleResponseNotAllowed(request, entry, now)) {
|
||||
return generateGatewayTimeout(context);
|
||||
} else {
|
||||
|
@ -723,48 +728,52 @@ public class CachingExec implements ExecChainHandler {
|
|||
|
||||
Date requestDate = getCurrentDate();
|
||||
ClassicHttpResponse backendResponse = chain.proceed(conditionalRequest, scope);
|
||||
Date responseDate = getCurrentDate();
|
||||
try {
|
||||
Date responseDate = getCurrentDate();
|
||||
|
||||
if (revalidationResponseIsTooOld(backendResponse, cacheEntry)) {
|
||||
backendResponse.close();
|
||||
final ClassicHttpRequest unconditional = conditionalRequestBuilder.buildUnconditionalRequest(
|
||||
scope.originalRequest);
|
||||
requestDate = getCurrentDate();
|
||||
backendResponse = chain.proceed(unconditional, scope);
|
||||
responseDate = getCurrentDate();
|
||||
}
|
||||
|
||||
backendResponse.addHeader(HeaderConstants.VIA, generateViaHeader(backendResponse));
|
||||
|
||||
final int statusCode = backendResponse.getCode();
|
||||
if (statusCode == HttpStatus.SC_NOT_MODIFIED || statusCode == HttpStatus.SC_OK) {
|
||||
recordCacheUpdate(scope.clientContext);
|
||||
}
|
||||
|
||||
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
|
||||
final HttpCacheEntry updatedEntry = responseCache.updateCacheEntry(
|
||||
target, request, cacheEntry,
|
||||
backendResponse, requestDate, responseDate);
|
||||
if (suitabilityChecker.isConditional(request)
|
||||
&& suitabilityChecker.allConditionalsMatch(request, updatedEntry, new Date())) {
|
||||
return responseGenerator
|
||||
.generateNotModifiedResponse(updatedEntry);
|
||||
}
|
||||
return responseGenerator.generateResponse(request, updatedEntry);
|
||||
}
|
||||
|
||||
if (staleIfErrorAppliesTo(statusCode)
|
||||
&& !staleResponseNotAllowed(request, cacheEntry, getCurrentDate())
|
||||
&& validityPolicy.mayReturnStaleIfError(request, cacheEntry, responseDate)) {
|
||||
try {
|
||||
final ClassicHttpResponse cachedResponse = responseGenerator.generateResponse(request, cacheEntry);
|
||||
cachedResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
|
||||
return cachedResponse;
|
||||
} finally {
|
||||
if (revalidationResponseIsTooOld(backendResponse, cacheEntry)) {
|
||||
backendResponse.close();
|
||||
final ClassicHttpRequest unconditional = conditionalRequestBuilder.buildUnconditionalRequest(
|
||||
scope.originalRequest);
|
||||
requestDate = getCurrentDate();
|
||||
backendResponse = chain.proceed(unconditional, scope);
|
||||
responseDate = getCurrentDate();
|
||||
}
|
||||
|
||||
backendResponse.addHeader(HeaderConstants.VIA, generateViaHeader(backendResponse));
|
||||
|
||||
final int statusCode = backendResponse.getCode();
|
||||
if (statusCode == HttpStatus.SC_NOT_MODIFIED || statusCode == HttpStatus.SC_OK) {
|
||||
recordCacheUpdate(scope.clientContext);
|
||||
}
|
||||
|
||||
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
|
||||
final HttpCacheEntry updatedEntry = responseCache.updateCacheEntry(
|
||||
target, request, cacheEntry,
|
||||
backendResponse, requestDate, responseDate);
|
||||
if (suitabilityChecker.isConditional(request)
|
||||
&& suitabilityChecker.allConditionalsMatch(request, updatedEntry, new Date())) {
|
||||
return responseGenerator.generateNotModifiedResponse(updatedEntry);
|
||||
}
|
||||
return responseGenerator.generateResponse(request, updatedEntry);
|
||||
}
|
||||
|
||||
if (staleIfErrorAppliesTo(statusCode)
|
||||
&& !staleResponseNotAllowed(request, cacheEntry, getCurrentDate())
|
||||
&& validityPolicy.mayReturnStaleIfError(request, cacheEntry, responseDate)) {
|
||||
try {
|
||||
final ClassicHttpResponse cachedResponse = responseGenerator.generateResponse(request, cacheEntry);
|
||||
cachedResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
|
||||
return cachedResponse;
|
||||
} finally {
|
||||
backendResponse.close();
|
||||
}
|
||||
}
|
||||
return handleBackendResponse(target, conditionalRequest, scope, requestDate, responseDate, backendResponse);
|
||||
} catch (final IOException | RuntimeException ex) {
|
||||
backendResponse.close();
|
||||
throw ex;
|
||||
}
|
||||
return handleBackendResponse(target, conditionalRequest, scope, requestDate, responseDate, backendResponse);
|
||||
}
|
||||
|
||||
private boolean staleIfErrorAppliesTo(final int statusCode) {
|
||||
|
@ -774,6 +783,66 @@ public class CachingExec implements ExecChainHandler {
|
|||
|| statusCode == HttpStatus.SC_GATEWAY_TIMEOUT;
|
||||
}
|
||||
|
||||
boolean isIncompleteResponse(final HttpResponse resp, final ByteArrayBuffer buffer) {
|
||||
if (buffer == null) {
|
||||
return false;
|
||||
}
|
||||
final int status = resp.getCode();
|
||||
if (status != HttpStatus.SC_OK && status != HttpStatus.SC_PARTIAL_CONTENT) {
|
||||
return false;
|
||||
}
|
||||
final Header hdr = resp.getFirstHeader(HttpHeaders.CONTENT_LENGTH);
|
||||
if (hdr == null) {
|
||||
return false;
|
||||
}
|
||||
final int contentLength;
|
||||
try {
|
||||
contentLength = Integer.parseInt(hdr.getValue());
|
||||
} catch (final NumberFormatException nfe) {
|
||||
return false;
|
||||
}
|
||||
return buffer.length() < contentLength;
|
||||
}
|
||||
|
||||
public ClassicHttpResponse cacheAndReturnResponse(
|
||||
final HttpHost target,
|
||||
final HttpRequest request,
|
||||
final ClassicHttpResponse backendResponse,
|
||||
final Date requestSent,
|
||||
final Date responseReceived) throws IOException { final ByteArrayBuffer buf;
|
||||
final HttpEntity entity = backendResponse.getEntity();
|
||||
if (entity != null) {
|
||||
buf = new ByteArrayBuffer(1024);
|
||||
final InputStream instream = entity.getContent();
|
||||
final byte[] tmp = new byte[2048];
|
||||
long total = 0;
|
||||
int l;
|
||||
while ((l = instream.read(tmp)) != -1) {
|
||||
buf.append(tmp, 0, l);
|
||||
total += l;
|
||||
if (total > cacheConfig.getMaxObjectSize()) {
|
||||
backendResponse.setEntity(new CombinedEntity(entity, buf));
|
||||
return backendResponse;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buf = null;
|
||||
}
|
||||
if (buf != null && isIncompleteResponse(backendResponse, buf)) {
|
||||
final Header h = backendResponse.getFirstHeader(HttpHeaders.CONTENT_LENGTH);
|
||||
final ClassicHttpResponse error = new BasicClassicHttpResponse(HttpStatus.SC_BAD_GATEWAY, "Bad Gateway");
|
||||
final String msg = String.format("Received incomplete response " +
|
||||
"with Content-Length %s but actual body length %d",
|
||||
h != null ? h.getValue() : null, buf.length());
|
||||
error.setEntity(new StringEntity(msg, ContentType.TEXT_PLAIN));
|
||||
backendResponse.close();
|
||||
return error;
|
||||
}
|
||||
backendResponse.close();
|
||||
final HttpCacheEntry entry = responseCache.createCacheEntry(target, request, backendResponse, buf, requestSent, responseReceived);
|
||||
return responseGenerator.generateResponse(request, entry);
|
||||
}
|
||||
|
||||
ClassicHttpResponse handleBackendResponse(
|
||||
final HttpHost target,
|
||||
final ClassicHttpRequest request,
|
||||
|
@ -789,7 +858,7 @@ public class CachingExec implements ExecChainHandler {
|
|||
responseCache.flushInvalidatedCacheEntriesFor(target, request, backendResponse);
|
||||
if (cacheable && !alreadyHaveNewerCacheEntry(target, request, backendResponse)) {
|
||||
storeRequestIfModifiedSinceFor304Response(request, backendResponse);
|
||||
return responseCache.cacheAndReturnResponse(target, request, backendResponse, requestDate, responseDate);
|
||||
return cacheAndReturnResponse(target, request, backendResponse, requestDate, responseDate);
|
||||
}
|
||||
if (!cacheable) {
|
||||
try {
|
||||
|
|
|
@ -147,7 +147,7 @@ public class CachingHttpClientBuilder extends HttpClientBuilder {
|
|||
final CacheKeyGenerator uriExtractor = new CacheKeyGenerator();
|
||||
final HttpCache httpCache = new BasicHttpCache(
|
||||
resourceFactoryCopy,
|
||||
storageCopy, config,
|
||||
storageCopy,
|
||||
uriExtractor,
|
||||
this.httpCacheInvalidator != null ? this.httpCacheInvalidator : new CacheInvalidator(uriExtractor, storageCopy));
|
||||
|
||||
|
|
|
@ -31,10 +31,10 @@ import java.util.Date;
|
|||
import java.util.Map;
|
||||
|
||||
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
||||
import org.apache.hc.core5.http.ClassicHttpResponse;
|
||||
import org.apache.hc.core5.http.HttpHost;
|
||||
import org.apache.hc.core5.http.HttpRequest;
|
||||
import org.apache.hc.core5.http.HttpResponse;
|
||||
import org.apache.hc.core5.util.ByteArrayBuffer;
|
||||
|
||||
/**
|
||||
* @since 4.1
|
||||
|
@ -94,14 +94,19 @@ interface HttpCache {
|
|||
* @param host
|
||||
* @param request
|
||||
* @param originResponse
|
||||
* @param content
|
||||
* @param requestSent
|
||||
* @param responseReceived
|
||||
* @return the {@link HttpResponse}
|
||||
* @return new {@link HttpCacheEntry}
|
||||
* @throws IOException
|
||||
*/
|
||||
ClassicHttpResponse cacheAndReturnResponse(HttpHost host,
|
||||
HttpRequest request, ClassicHttpResponse originResponse,
|
||||
Date requestSent, Date responseReceived)
|
||||
HttpCacheEntry createCacheEntry(
|
||||
HttpHost host,
|
||||
HttpRequest request,
|
||||
HttpResponse originResponse,
|
||||
ByteArrayBuffer content,
|
||||
Date requestSent,
|
||||
Date responseReceived)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
|
|
|
@ -52,6 +52,7 @@ import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
|
|||
import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
|
||||
import org.apache.hc.core5.http.message.BasicHeader;
|
||||
import org.apache.hc.core5.http.message.MessageSupport;
|
||||
import org.apache.hc.core5.util.ByteArrayBuffer;
|
||||
import org.apache.hc.core5.util.LangUtils;
|
||||
import org.junit.Assert;
|
||||
|
||||
|
@ -243,6 +244,13 @@ public class HttpTestUtils {
|
|||
return bytes;
|
||||
}
|
||||
|
||||
public static ByteArrayBuffer getRandomBuffer(final int nbytes) {
|
||||
final ByteArrayBuffer buf = new ByteArrayBuffer(nbytes);
|
||||
buf.setLength(nbytes);
|
||||
new Random().nextBytes(buf.array());
|
||||
return buf;
|
||||
}
|
||||
|
||||
/** Generates a response body with random content.
|
||||
* @param nbytes length of the desired response body
|
||||
* @return an {@link HttpEntity}
|
||||
|
|
|
@ -31,18 +31,13 @@ import static org.junit.Assert.assertFalse;
|
|||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.hc.client5.http.cache.HeaderConstants;
|
||||
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
||||
import org.apache.hc.client5.http.cache.Resource;
|
||||
import org.apache.hc.client5.http.classic.methods.HttpDelete;
|
||||
import org.apache.hc.client5.http.classic.methods.HttpGet;
|
||||
import org.apache.hc.client5.http.classic.methods.HttpHead;
|
||||
|
@ -50,16 +45,14 @@ import org.apache.hc.client5.http.classic.methods.HttpOptions;
|
|||
import org.apache.hc.client5.http.classic.methods.HttpPost;
|
||||
import org.apache.hc.client5.http.classic.methods.HttpTrace;
|
||||
import org.apache.hc.client5.http.utils.DateUtils;
|
||||
import org.apache.hc.core5.http.ClassicHttpResponse;
|
||||
import org.apache.hc.core5.http.Header;
|
||||
import org.apache.hc.core5.http.HttpEntity;
|
||||
import org.apache.hc.core5.http.HttpHost;
|
||||
import org.apache.hc.core5.http.HttpRequest;
|
||||
import org.apache.hc.core5.http.HttpResponse;
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.apache.hc.core5.http.io.entity.BasicHttpEntity;
|
||||
import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
|
||||
import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
|
||||
import org.apache.hc.core5.http.message.BasicHeader;
|
||||
import org.apache.hc.core5.http.message.BasicHttpResponse;
|
||||
import org.apache.hc.core5.util.ByteArrayBuffer;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -72,7 +65,7 @@ public class TestBasicHttpCache {
|
|||
@Before
|
||||
public void setUp() throws Exception {
|
||||
backing = new SimpleHttpCacheStorage();
|
||||
impl = new BasicHttpCache(new HeapResourceFactory(), backing, CacheConfig.DEFAULT);
|
||||
impl = new BasicHttpCache(new HeapResourceFactory(), backing);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -136,7 +129,7 @@ public class TestBasicHttpCache {
|
|||
throws Exception {
|
||||
final HttpHost host = new HttpHost("foo.example.com");
|
||||
final HttpRequest req = new HttpPost("/foo");
|
||||
final ClassicHttpResponse resp = HttpTestUtils.make200Response();
|
||||
final HttpResponse resp = HttpTestUtils.make200Response();
|
||||
resp.setHeader("Content-Location", "/bar");
|
||||
resp.setHeader(HeaderConstants.ETAG, "\"etag\"");
|
||||
final String key = (new CacheKeyGenerator()).generateKey(host, new HttpGet("/bar"));
|
||||
|
@ -158,7 +151,7 @@ public class TestBasicHttpCache {
|
|||
throws Exception {
|
||||
final HttpHost host = new HttpHost("foo.example.com");
|
||||
final HttpRequest req = new HttpGet("/foo");
|
||||
final ClassicHttpResponse resp = HttpTestUtils.make200Response();
|
||||
final HttpResponse resp = HttpTestUtils.make200Response();
|
||||
resp.setHeader("Content-Location", "/bar");
|
||||
final String key = (new CacheKeyGenerator()).generateKey(host, new HttpGet("/bar"));
|
||||
|
||||
|
@ -188,127 +181,6 @@ public class TestBasicHttpCache {
|
|||
assertNull(backing.map.get(key));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecognizesComplete200Response()
|
||||
throws Exception {
|
||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
final byte[] bytes = HttpTestUtils.getRandomBytes(128);
|
||||
resp.setEntity(new ByteArrayEntity(bytes));
|
||||
resp.setHeader("Content-Length","128");
|
||||
final Resource resource = new HeapResource(bytes);
|
||||
|
||||
assertFalse(impl.isIncompleteResponse(resp, resource));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecognizesComplete206Response()
|
||||
throws Exception {
|
||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
|
||||
final byte[] bytes = HttpTestUtils.getRandomBytes(128);
|
||||
final Resource resource = new HeapResource(bytes);
|
||||
resp.setEntity(new ByteArrayEntity(bytes));
|
||||
resp.setHeader("Content-Length","128");
|
||||
resp.setHeader("Content-Range","bytes 0-127/255");
|
||||
|
||||
assertFalse(impl.isIncompleteResponse(resp, resource));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecognizesIncomplete200Response()
|
||||
throws Exception {
|
||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
final byte[] bytes = HttpTestUtils.getRandomBytes(128);
|
||||
final Resource resource = new HeapResource(bytes);
|
||||
resp.setEntity(new ByteArrayEntity(bytes));
|
||||
resp.setHeader("Content-Length","256");
|
||||
|
||||
assertTrue(impl.isIncompleteResponse(resp, resource));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIgnoresIncompleteNon200Or206Responses()
|
||||
throws Exception {
|
||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_FORBIDDEN, "Forbidden");
|
||||
final byte[] bytes = HttpTestUtils.getRandomBytes(128);
|
||||
final Resource resource = new HeapResource(bytes);
|
||||
resp.setEntity(new ByteArrayEntity(bytes));
|
||||
resp.setHeader("Content-Length","256");
|
||||
|
||||
assertFalse(impl.isIncompleteResponse(resp, resource));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponsesWithoutExplicitContentLengthAreComplete()
|
||||
throws Exception {
|
||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
final byte[] bytes = HttpTestUtils.getRandomBytes(128);
|
||||
final Resource resource = new HeapResource(bytes);
|
||||
resp.setEntity(new ByteArrayEntity(bytes));
|
||||
|
||||
assertFalse(impl.isIncompleteResponse(resp, resource));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponsesWithUnparseableContentLengthHeaderAreComplete()
|
||||
throws Exception {
|
||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
final byte[] bytes = HttpTestUtils.getRandomBytes(128);
|
||||
final Resource resource = new HeapResource(bytes);
|
||||
resp.setHeader("Content-Length","foo");
|
||||
resp.setEntity(new ByteArrayEntity(bytes));
|
||||
|
||||
assertFalse(impl.isIncompleteResponse(resp, resource));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullResourcesAreComplete()
|
||||
throws Exception {
|
||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
resp.setHeader("Content-Length","256");
|
||||
|
||||
assertFalse(impl.isIncompleteResponse(resp, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncompleteResponseErrorProvidesPlainTextErrorMessage()
|
||||
throws Exception {
|
||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
final byte[] bytes = HttpTestUtils.getRandomBytes(128);
|
||||
final Resource resource = new HeapResource(bytes);
|
||||
resp.setEntity(new ByteArrayEntity(bytes));
|
||||
resp.setHeader("Content-Length","256");
|
||||
|
||||
final ClassicHttpResponse result = impl.generateIncompleteResponseError(resp, resource);
|
||||
final Header ctype = result.getFirstHeader("Content-Type");
|
||||
assertEquals("text/plain;charset=UTF-8", ctype.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncompleteResponseErrorProvidesNonEmptyErrorMessage()
|
||||
throws Exception {
|
||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
final byte[] bytes = HttpTestUtils.getRandomBytes(128);
|
||||
final Resource resource = new HeapResource(bytes);
|
||||
resp.setEntity(new ByteArrayEntity(bytes));
|
||||
resp.setHeader("Content-Length","256");
|
||||
|
||||
final ClassicHttpResponse result = impl.generateIncompleteResponseError(resp, resource);
|
||||
final int clen = Integer.parseInt(result.getFirstHeader("Content-Length").getValue());
|
||||
assertTrue(clen > 0);
|
||||
final HttpEntity body = result.getEntity();
|
||||
if (body.getContentLength() < 0) {
|
||||
final InputStream is = body.getContent();
|
||||
int bytes_read = 0;
|
||||
while((is.read()) != -1) {
|
||||
bytes_read++;
|
||||
}
|
||||
is.close();
|
||||
assertEquals(clen, bytes_read);
|
||||
} else {
|
||||
assertTrue(body.getContentLength() == clen);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCacheUpdateAddsVariantURIToParentEntry() throws Exception {
|
||||
final String parentCacheKey = "parentCacheKey";
|
||||
|
@ -340,50 +212,6 @@ public class TestBasicHttpCache {
|
|||
assertSame(entry, backing.map.get(key));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTooLargeResponsesAreNotCached() throws Exception {
|
||||
final HttpHost host = new HttpHost("foo.example.com");
|
||||
final HttpRequest request = new HttpGet("http://foo.example.com/bar");
|
||||
|
||||
final Date now = new Date();
|
||||
final Date requestSent = new Date(now.getTime() - 3 * 1000L);
|
||||
final Date responseGenerated = new Date(now.getTime() - 2 * 1000L);
|
||||
final Date responseReceived = new Date(now.getTime() - 1 * 1000L);
|
||||
|
||||
final ClassicHttpResponse originResponse = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
originResponse.setEntity(HttpTestUtils.makeBody(CacheConfig.DEFAULT_MAX_OBJECT_SIZE_BYTES + 1));
|
||||
originResponse.setHeader("Cache-Control","public, max-age=3600");
|
||||
originResponse.setHeader("Date", DateUtils.formatDate(responseGenerated));
|
||||
originResponse.setHeader("ETag", "\"etag\"");
|
||||
|
||||
final ClassicHttpResponse result = impl.cacheAndReturnResponse(host, request, originResponse, requestSent, responseReceived);
|
||||
assertEquals(0, backing.map.size());
|
||||
assertSame(originResponse, result);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSmallEnoughResponsesAreCached() throws Exception {
|
||||
final HttpHost host = new HttpHost("foo.example.com");
|
||||
final HttpRequest request = new HttpGet("http://foo.example.com/bar");
|
||||
|
||||
final Date now = new Date();
|
||||
final Date requestSent = new Date(now.getTime() - 3 * 1000L);
|
||||
final Date responseGenerated = new Date(now.getTime() - 2 * 1000L);
|
||||
final Date responseReceived = new Date(now.getTime() - 1 * 1000L);
|
||||
|
||||
final ClassicHttpResponse originResponse = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
originResponse.setEntity(HttpTestUtils.makeBody(CacheConfig.DEFAULT_MAX_OBJECT_SIZE_BYTES - 1));
|
||||
originResponse.setHeader("Cache-Control","public, max-age=3600");
|
||||
originResponse.setHeader("Date", DateUtils.formatDate(responseGenerated));
|
||||
originResponse.setHeader("ETag", "\"etag\"");
|
||||
|
||||
final ClassicHttpResponse result = impl.cacheAndReturnResponse(host, request, originResponse, requestSent, responseReceived);
|
||||
assertEquals(1, backing.map.size());
|
||||
assertTrue(backing.map.containsKey((new CacheKeyGenerator()).generateKey(host, request)));
|
||||
assertTrue(HttpTestUtils.semanticallyTransparent(originResponse, result));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCacheEntryReturnsNullOnCacheMiss() throws Exception {
|
||||
final HttpHost host = new HttpHost("foo.example.com");
|
||||
|
@ -410,20 +238,21 @@ public class TestBasicHttpCache {
|
|||
@Test
|
||||
public void testGetCacheEntryReturnsNullIfNoVariantInCache() throws Exception {
|
||||
final HttpHost host = new HttpHost("foo.example.com");
|
||||
final HttpRequest request = new HttpGet("http://foo.example.com/bar");
|
||||
|
||||
final HttpRequest origRequest = new HttpGet("http://foo.example.com/bar");
|
||||
origRequest.setHeader("Accept-Encoding","gzip");
|
||||
|
||||
final ClassicHttpResponse origResponse = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
origResponse.setEntity(HttpTestUtils.makeBody(128));
|
||||
final ByteArrayBuffer buf = HttpTestUtils.getRandomBuffer(128);
|
||||
final HttpResponse origResponse = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
origResponse.setHeader("Date", DateUtils.formatDate(new Date()));
|
||||
origResponse.setHeader("Cache-Control", "max-age=3600, public");
|
||||
origResponse.setHeader("ETag", "\"etag\"");
|
||||
origResponse.setHeader("Vary", "Accept-Encoding");
|
||||
origResponse.setHeader("Content-Encoding","gzip");
|
||||
|
||||
impl.cacheAndReturnResponse(host, origRequest, origResponse, new Date(), new Date());
|
||||
impl.createCacheEntry(host, origRequest, origResponse, buf, new Date(), new Date());
|
||||
|
||||
final HttpRequest request = new HttpGet("http://foo.example.com/bar");
|
||||
final HttpCacheEntry result = impl.getCacheEntry(host, request);
|
||||
assertNull(result);
|
||||
}
|
||||
|
@ -431,21 +260,22 @@ public class TestBasicHttpCache {
|
|||
@Test
|
||||
public void testGetCacheEntryReturnsVariantIfPresentInCache() throws Exception {
|
||||
final HttpHost host = new HttpHost("foo.example.com");
|
||||
final HttpRequest request = new HttpGet("http://foo.example.com/bar");
|
||||
request.setHeader("Accept-Encoding","gzip");
|
||||
|
||||
final HttpRequest origRequest = new HttpGet("http://foo.example.com/bar");
|
||||
origRequest.setHeader("Accept-Encoding","gzip");
|
||||
|
||||
final ClassicHttpResponse origResponse = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
origResponse.setEntity(HttpTestUtils.makeBody(128));
|
||||
final ByteArrayBuffer buf = HttpTestUtils.getRandomBuffer(128);
|
||||
final HttpResponse origResponse = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
origResponse.setHeader("Date", DateUtils.formatDate(new Date()));
|
||||
origResponse.setHeader("Cache-Control", "max-age=3600, public");
|
||||
origResponse.setHeader("ETag", "\"etag\"");
|
||||
origResponse.setHeader("Vary", "Accept-Encoding");
|
||||
origResponse.setHeader("Content-Encoding","gzip");
|
||||
|
||||
impl.cacheAndReturnResponse(host, origRequest, origResponse, new Date(), new Date());
|
||||
impl.createCacheEntry(host, origRequest, origResponse, buf, new Date(), new Date());
|
||||
|
||||
final HttpRequest request = new HttpGet("http://foo.example.com/bar");
|
||||
request.setHeader("Accept-Encoding","gzip");
|
||||
final HttpCacheEntry result = impl.getCacheEntry(host, request);
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
@ -467,7 +297,7 @@ public class TestBasicHttpCache {
|
|||
final HttpRequest req1 = new HttpGet("http://foo.example.com/bar");
|
||||
req1.setHeader("Accept-Encoding", "gzip");
|
||||
|
||||
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
|
||||
final HttpResponse resp1 = HttpTestUtils.make200Response();
|
||||
resp1.setHeader("Date", DateUtils.formatDate(new Date()));
|
||||
resp1.setHeader("Cache-Control", "max-age=3600, public");
|
||||
resp1.setHeader("ETag", "\"etag1\"");
|
||||
|
@ -478,7 +308,7 @@ public class TestBasicHttpCache {
|
|||
final HttpRequest req2 = new HttpGet("http://foo.example.com/bar");
|
||||
req2.setHeader("Accept-Encoding", "identity");
|
||||
|
||||
final ClassicHttpResponse resp2 = HttpTestUtils.make200Response();
|
||||
final HttpResponse resp2 = HttpTestUtils.make200Response();
|
||||
resp2.setHeader("Date", DateUtils.formatDate(new Date()));
|
||||
resp2.setHeader("Cache-Control", "max-age=3600, public");
|
||||
resp2.setHeader("ETag", "\"etag2\"");
|
||||
|
@ -486,8 +316,8 @@ public class TestBasicHttpCache {
|
|||
resp2.setHeader("Content-Encoding","gzip");
|
||||
resp2.setHeader("Vary", "Accept-Encoding");
|
||||
|
||||
impl.cacheAndReturnResponse(host, req1, resp1, new Date(), new Date());
|
||||
impl.cacheAndReturnResponse(host, req2, resp2, new Date(), new Date());
|
||||
impl.createCacheEntry(host, req1, resp1, null, new Date(), new Date());
|
||||
impl.createCacheEntry(host, req2, resp2, null, new Date(), new Date());
|
||||
|
||||
final Map<String,Variant> variants = impl.getVariantCacheEntriesWithEtags(host, req1);
|
||||
|
||||
|
@ -496,55 +326,4 @@ public class TestBasicHttpCache {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOriginalResponseWithNoContentSizeHeaderIsReleased() throws Exception {
|
||||
final HttpHost host = new HttpHost("foo.example.com");
|
||||
final HttpRequest request = new HttpGet("http://foo.example.com/bar");
|
||||
|
||||
final Date now = new Date();
|
||||
final Date requestSent = new Date(now.getTime() - 3 * 1000L);
|
||||
final Date responseGenerated = new Date(now.getTime() - 2 * 1000L);
|
||||
final Date responseReceived = new Date(now.getTime() - 1 * 1000L);
|
||||
|
||||
final ClassicHttpResponse originResponse = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
final BasicHttpEntity entity = new BasicHttpEntity();
|
||||
final ConsumableInputStream inputStream = new ConsumableInputStream(new ByteArrayInputStream(HttpTestUtils.getRandomBytes(CacheConfig.DEFAULT_MAX_OBJECT_SIZE_BYTES - 1)));
|
||||
entity.setContent(inputStream);
|
||||
originResponse.setEntity(entity);
|
||||
originResponse.setHeader("Cache-Control","public, max-age=3600");
|
||||
originResponse.setHeader("Date", DateUtils.formatDate(responseGenerated));
|
||||
originResponse.setHeader("ETag", "\"etag\"");
|
||||
|
||||
final ClassicHttpResponse result = impl.cacheAndReturnResponse(host, request, originResponse, requestSent, responseReceived);
|
||||
IOUtils.consume(result.getEntity());
|
||||
assertTrue(inputStream.wasClosed());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEntryUpdate() throws Exception {
|
||||
|
||||
final HeapResourceFactory rf = new HeapResourceFactory();
|
||||
|
||||
impl = new BasicHttpCache(rf, backing, CacheConfig.DEFAULT);
|
||||
|
||||
final HttpHost host = new HttpHost("foo.example.com");
|
||||
|
||||
final HttpRequest origRequest = new HttpGet("http://foo.example.com/bar");
|
||||
origRequest.setHeader("Accept-Encoding","gzip");
|
||||
|
||||
final ClassicHttpResponse origResponse = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
origResponse.setEntity(HttpTestUtils.makeBody(128));
|
||||
origResponse.setHeader("Date", DateUtils.formatDate(new Date()));
|
||||
origResponse.setHeader("Cache-Control", "max-age=3600, public");
|
||||
origResponse.setHeader("ETag", "\"etag\"");
|
||||
origResponse.setHeader("Vary", "Accept-Encoding");
|
||||
origResponse.setHeader("Content-Encoding","gzip");
|
||||
|
||||
final ClassicHttpResponse response = impl.cacheAndReturnResponse(
|
||||
host, origRequest, origResponse, new Date(), new Date());
|
||||
final HttpEntity entity = response.getEntity();
|
||||
Assert.assertNotNull(entity);
|
||||
IOUtils.copyAndClose(entity.getContent(), new ByteArrayOutputStream());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -290,7 +290,7 @@ public class TestCachingExec extends TestCachingExecChain {
|
|||
eq(responseDate)))
|
||||
.andReturn(updatedEntry);
|
||||
expect(mockSuitabilityChecker.isConditional(request)).andReturn(false);
|
||||
responseIsGeneratedFromCache();
|
||||
responseIsGeneratedFromCache(HttpTestUtils.make200Response());
|
||||
|
||||
replayMocks();
|
||||
impl.revalidateCacheEntry(host, request, scope, mockExecChain, entry);
|
||||
|
@ -396,14 +396,11 @@ public class TestCachingExec extends TestCachingExecChain {
|
|||
cacheEntrySuitable(true);
|
||||
cacheEntryValidatable(true);
|
||||
|
||||
expect(mockResponseGenerator.generateResponse(isA(HttpRequest.class), isA(HttpCacheEntry.class)))
|
||||
.andReturn(mockBackendResponse);
|
||||
responseIsGeneratedFromCache(HttpTestUtils.make200Response());
|
||||
|
||||
replayMocks();
|
||||
final HttpResponse result = impl.execute(request, scope, mockExecChain);
|
||||
impl.execute(request, scope, mockExecChain);
|
||||
verifyMocks();
|
||||
|
||||
Assert.assertSame(mockBackendResponse, result);
|
||||
}
|
||||
|
||||
private IExpectationSetters<ClassicHttpResponse> implExpectsAnyRequestAndReturn(
|
||||
|
|
|
@ -31,10 +31,12 @@ import static org.easymock.EasyMock.eq;
|
|||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.expectLastCall;
|
||||
import static org.easymock.EasyMock.isA;
|
||||
import static org.easymock.EasyMock.same;
|
||||
import static org.easymock.classextension.EasyMock.createNiceMock;
|
||||
import static org.easymock.classextension.EasyMock.replay;
|
||||
import static org.easymock.classextension.EasyMock.verify;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
@ -54,7 +56,6 @@ import org.apache.hc.client5.http.cache.HttpCacheContext;
|
|||
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
||||
import org.apache.hc.client5.http.cache.HttpCacheStorage;
|
||||
import org.apache.hc.client5.http.classic.ExecChain;
|
||||
import org.apache.hc.client5.http.classic.ExecChainHandler;
|
||||
import org.apache.hc.client5.http.classic.ExecRuntime;
|
||||
import org.apache.hc.client5.http.classic.methods.HttpGet;
|
||||
import org.apache.hc.client5.http.classic.methods.HttpOptions;
|
||||
|
@ -77,6 +78,7 @@ import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
|
|||
import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
|
||||
import org.apache.hc.core5.http.message.BasicHeader;
|
||||
import org.apache.hc.core5.net.URIAuthority;
|
||||
import org.apache.hc.core5.util.ByteArrayBuffer;
|
||||
import org.easymock.Capture;
|
||||
import org.easymock.EasyMock;
|
||||
import org.easymock.IExpectationSetters;
|
||||
|
@ -89,7 +91,7 @@ import junit.framework.AssertionFailedError;
|
|||
@SuppressWarnings("boxing") // test code
|
||||
public abstract class TestCachingExecChain {
|
||||
|
||||
private ExecChainHandler impl;
|
||||
private CachingExec impl;
|
||||
|
||||
protected CacheValidityPolicy mockValidityPolicy;
|
||||
protected CacheableRequestPolicy mockRequestPolicy;
|
||||
|
@ -103,7 +105,6 @@ public abstract class TestCachingExecChain {
|
|||
protected CachedHttpResponseGenerator mockResponseGenerator;
|
||||
private HttpClientResponseHandler<Object> mockHandler;
|
||||
private ClassicHttpRequest mockUriRequest;
|
||||
private ClassicHttpResponse mockCachedResponse;
|
||||
protected ConditionalRequestBuilder mockConditionalRequestBuilder;
|
||||
private HttpRequest mockConditionalRequest;
|
||||
protected ResponseProtocolCompliance mockResponseProtocolCompliance;
|
||||
|
@ -131,7 +132,6 @@ public abstract class TestCachingExecChain {
|
|||
mockUriRequest = createNiceMock(ClassicHttpRequest.class);
|
||||
mockCacheEntry = createNiceMock(HttpCacheEntry.class);
|
||||
mockResponseGenerator = createNiceMock(CachedHttpResponseGenerator.class);
|
||||
mockCachedResponse = createNiceMock(ClassicHttpResponse.class);
|
||||
mockConditionalRequestBuilder = createNiceMock(ConditionalRequestBuilder.class);
|
||||
mockConditionalRequest = createNiceMock(HttpRequest.class);
|
||||
mockResponseProtocolCompliance = createNiceMock(ResponseProtocolCompliance.class);
|
||||
|
@ -151,7 +151,7 @@ public abstract class TestCachingExecChain {
|
|||
mockRequestProtocolCompliance, config, asyncValidator);
|
||||
}
|
||||
|
||||
public abstract ExecChainHandler createCachingExecChain(HttpCache responseCache, CacheValidityPolicy validityPolicy,
|
||||
public abstract CachingExec createCachingExecChain(HttpCache responseCache, CacheValidityPolicy validityPolicy,
|
||||
ResponseCachingPolicy responseCachingPolicy, CachedHttpResponseGenerator responseGenerator,
|
||||
CacheableRequestPolicy cacheableRequestPolicy,
|
||||
CachedResponseSuitabilityChecker suitabilityChecker,
|
||||
|
@ -159,7 +159,7 @@ public abstract class TestCachingExecChain {
|
|||
ResponseProtocolCompliance responseCompliance, RequestProtocolCompliance requestCompliance,
|
||||
CacheConfig config, AsynchronousValidator asynchRevalidator);
|
||||
|
||||
public abstract ExecChainHandler createCachingExecChain(HttpCache cache, CacheConfig config);
|
||||
public abstract CachingExec createCachingExecChain(HttpCache cache, CacheConfig config);
|
||||
|
||||
protected ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException {
|
||||
return impl.execute(ClassicRequestCopier.INSTANCE.copy(request), new ExecChain.Scope(
|
||||
|
@ -187,7 +187,6 @@ public abstract class TestCachingExecChain {
|
|||
replay(mockCache);
|
||||
replay(mockHandler);
|
||||
replay(mockUriRequest);
|
||||
replay(mockCachedResponse);
|
||||
replay(mockConditionalRequestBuilder);
|
||||
replay(mockConditionalRequest);
|
||||
replay(mockResponseProtocolCompliance);
|
||||
|
@ -206,7 +205,6 @@ public abstract class TestCachingExecChain {
|
|||
verify(mockCache);
|
||||
verify(mockHandler);
|
||||
verify(mockUriRequest);
|
||||
verify(mockCachedResponse);
|
||||
verify(mockConditionalRequestBuilder);
|
||||
verify(mockConditionalRequest);
|
||||
verify(mockResponseProtocolCompliance);
|
||||
|
@ -314,22 +312,20 @@ public abstract class TestCachingExecChain {
|
|||
requestPolicyAllowsCaching(true);
|
||||
getCacheEntryReturns(mockCacheEntry);
|
||||
cacheEntrySuitable(true);
|
||||
responseIsGeneratedFromCache();
|
||||
responseIsGeneratedFromCache(HttpTestUtils.make200Response());
|
||||
requestIsFatallyNonCompliant(null);
|
||||
entryHasStaleness(0L);
|
||||
|
||||
replayMocks();
|
||||
final ClassicHttpResponse result = execute(request);
|
||||
verifyMocks();
|
||||
|
||||
Assert.assertSame(mockCachedResponse, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonCacheableResponseIsNotCachedAndIsReturnedAsIs() throws Exception {
|
||||
final CacheConfig configDefault = CacheConfig.DEFAULT;
|
||||
impl = createCachingExecChain(new BasicHttpCache(new HeapResourceFactory(),
|
||||
mockStorage, configDefault), configDefault);
|
||||
mockStorage), configDefault);
|
||||
|
||||
final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest();
|
||||
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
|
||||
|
@ -354,7 +350,7 @@ public abstract class TestCachingExecChain {
|
|||
requestPolicyAllowsCaching(true);
|
||||
cacheEntrySuitable(true);
|
||||
getCacheEntryReturns(mockCacheEntry);
|
||||
responseIsGeneratedFromCache();
|
||||
responseIsGeneratedFromCache(HttpTestUtils.make200Response());
|
||||
entryHasStaleness(0L);
|
||||
|
||||
replayMocks();
|
||||
|
@ -1265,31 +1261,134 @@ public abstract class TestCachingExecChain {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testTreatsCacheIOExceptionsAsCacheMiss() throws Exception {
|
||||
public void testRecognizesComplete200Response()
|
||||
throws Exception {
|
||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
final ByteArrayBuffer buf = HttpTestUtils.getRandomBuffer(128);
|
||||
resp.setHeader("Content-Length","128");
|
||||
assertFalse(impl.isIncompleteResponse(resp, buf));
|
||||
}
|
||||
|
||||
impl = createCachingExecChain(mockCache, CacheConfig.DEFAULT);
|
||||
final ClassicHttpResponse resp = HttpTestUtils.make200Response();
|
||||
@Test
|
||||
public void testRecognizesComplete206Response()
|
||||
throws Exception {
|
||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
|
||||
final ByteArrayBuffer buf = HttpTestUtils.getRandomBuffer(128);
|
||||
resp.setHeader("Content-Length","128");
|
||||
resp.setHeader("Content-Range","bytes 0-127/255");
|
||||
assertFalse(impl.isIncompleteResponse(resp, buf));
|
||||
}
|
||||
|
||||
mockCache.flushInvalidatedCacheEntriesFor(host, request);
|
||||
expectLastCall().andThrow(new IOException()).anyTimes();
|
||||
mockCache.flushInvalidatedCacheEntriesFor(isA(HttpHost.class), isA(HttpRequest.class),
|
||||
isA(HttpResponse.class));
|
||||
expectLastCall().anyTimes();
|
||||
expect(mockCache.getCacheEntry(eq(host), isA(HttpRequest.class))).andThrow(
|
||||
new IOException()).anyTimes();
|
||||
expect(mockCache.getVariantCacheEntriesWithEtags(eq(host), isA(HttpRequest.class)))
|
||||
.andThrow(new IOException()).anyTimes();
|
||||
expect(
|
||||
mockCache.cacheAndReturnResponse(eq(host), isA(HttpRequest.class),
|
||||
isA(ClassicHttpResponse.class), isA(Date.class), isA(Date.class)))
|
||||
.andReturn(resp).anyTimes();
|
||||
expect(
|
||||
mockExecChain.proceed(isA(ClassicHttpRequest.class), isA(ExecChain.Scope.class))).andReturn(resp);
|
||||
@Test
|
||||
public void testRecognizesIncomplete200Response()
|
||||
throws Exception {
|
||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
final ByteArrayBuffer buf = HttpTestUtils.getRandomBuffer(128);
|
||||
resp.setHeader("Content-Length","256");
|
||||
|
||||
assertTrue(impl.isIncompleteResponse(resp, buf));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIgnoresIncompleteNon200Or206Responses()
|
||||
throws Exception {
|
||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_FORBIDDEN, "Forbidden");
|
||||
final ByteArrayBuffer buf = HttpTestUtils.getRandomBuffer(128);
|
||||
resp.setHeader("Content-Length","256");
|
||||
|
||||
assertFalse(impl.isIncompleteResponse(resp, buf));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponsesWithoutExplicitContentLengthAreComplete()
|
||||
throws Exception {
|
||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
final ByteArrayBuffer buf = HttpTestUtils.getRandomBuffer(128);
|
||||
|
||||
assertFalse(impl.isIncompleteResponse(resp, buf));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponsesWithUnparseableContentLengthHeaderAreComplete()
|
||||
throws Exception {
|
||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
final ByteArrayBuffer buf = HttpTestUtils.getRandomBuffer(128);
|
||||
resp.setHeader("Content-Length","foo");
|
||||
assertFalse(impl.isIncompleteResponse(resp, buf));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullResourcesAreComplete()
|
||||
throws Exception {
|
||||
final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
resp.setHeader("Content-Length","256");
|
||||
|
||||
assertFalse(impl.isIncompleteResponse(resp, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTooLargeResponsesAreNotCached() throws Exception {
|
||||
mockCache = EasyMock.createStrictMock(HttpCache.class);
|
||||
impl = createCachingExecChain(mockCache, mockValidityPolicy,
|
||||
mockResponsePolicy, mockResponseGenerator, mockRequestPolicy, mockSuitabilityChecker,
|
||||
mockConditionalRequestBuilder, mockResponseProtocolCompliance,
|
||||
mockRequestProtocolCompliance, config, asyncValidator);
|
||||
|
||||
final HttpHost host = new HttpHost("foo.example.com");
|
||||
final HttpRequest request = new HttpGet("http://foo.example.com/bar");
|
||||
|
||||
final Date now = new Date();
|
||||
final Date requestSent = new Date(now.getTime() - 3 * 1000L);
|
||||
final Date responseGenerated = new Date(now.getTime() - 2 * 1000L);
|
||||
final Date responseReceived = new Date(now.getTime() - 1 * 1000L);
|
||||
|
||||
final ClassicHttpResponse originResponse = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
originResponse.setEntity(HttpTestUtils.makeBody(CacheConfig.DEFAULT_MAX_OBJECT_SIZE_BYTES + 1));
|
||||
originResponse.setHeader("Cache-Control","public, max-age=3600");
|
||||
originResponse.setHeader("Date", DateUtils.formatDate(responseGenerated));
|
||||
originResponse.setHeader("ETag", "\"etag\"");
|
||||
|
||||
replayMocks();
|
||||
final ClassicHttpResponse result = execute(request);
|
||||
|
||||
impl.cacheAndReturnResponse(host, request, originResponse, requestSent, responseReceived);
|
||||
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmallEnoughResponsesAreCached() throws Exception {
|
||||
final HttpHost host = new HttpHost("foo.example.com");
|
||||
final HttpRequest request = new HttpGet("http://foo.example.com/bar");
|
||||
|
||||
final Date now = new Date();
|
||||
final Date requestSent = new Date(now.getTime() - 3 * 1000L);
|
||||
final Date responseGenerated = new Date(now.getTime() - 2 * 1000L);
|
||||
final Date responseReceived = new Date(now.getTime() - 1 * 1000L);
|
||||
|
||||
final ClassicHttpResponse originResponse = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
|
||||
originResponse.setEntity(HttpTestUtils.makeBody(CacheConfig.DEFAULT_MAX_OBJECT_SIZE_BYTES - 1));
|
||||
originResponse.setHeader("Cache-Control","public, max-age=3600");
|
||||
originResponse.setHeader("Date", DateUtils.formatDate(responseGenerated));
|
||||
originResponse.setHeader("ETag", "\"etag\"");
|
||||
|
||||
final HttpCacheEntry httpCacheEntry = HttpTestUtils.makeCacheEntry();
|
||||
final ClassicHttpResponse response = HttpTestUtils.make200Response();
|
||||
|
||||
EasyMock.expect(mockCache.createCacheEntry(
|
||||
eq(host),
|
||||
same(request),
|
||||
same(originResponse),
|
||||
isA(ByteArrayBuffer.class),
|
||||
eq(requestSent),
|
||||
eq(responseReceived))).andReturn(httpCacheEntry).once();
|
||||
EasyMock.expect(mockResponseGenerator.generateResponse(
|
||||
same(request),
|
||||
same(httpCacheEntry))).andReturn(response).once();
|
||||
replayMocks();
|
||||
|
||||
impl.cacheAndReturnResponse(host, request, originResponse, requestSent, responseReceived);
|
||||
|
||||
verifyMocks();
|
||||
Assert.assertSame(resp, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1332,14 +1431,12 @@ public abstract class TestCachingExecChain {
|
|||
requestPolicyAllowsCaching(true);
|
||||
getCacheEntryReturns(entry);
|
||||
cacheEntrySuitable(true);
|
||||
responseIsGeneratedFromCache();
|
||||
responseIsGeneratedFromCache(HttpTestUtils.make200Response());
|
||||
entryHasStaleness(0);
|
||||
|
||||
replayMocks();
|
||||
final ClassicHttpResponse resp = execute(request);
|
||||
verifyMocks();
|
||||
|
||||
Assert.assertSame(mockCachedResponse, resp);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1669,10 +1766,11 @@ public abstract class TestCachingExecChain {
|
|||
.andReturn(staleness);
|
||||
}
|
||||
|
||||
protected void responseIsGeneratedFromCache() {
|
||||
protected void responseIsGeneratedFromCache(final ClassicHttpResponse cachedResponse) throws IOException {
|
||||
expect(
|
||||
mockResponseGenerator.generateResponse((ClassicHttpRequest) anyObject(), (HttpCacheEntry) anyObject()))
|
||||
.andReturn(mockCachedResponse);
|
||||
mockResponseGenerator.generateResponse(
|
||||
(ClassicHttpRequest) anyObject(),
|
||||
(HttpCacheEntry) anyObject())).andReturn(cachedResponse);
|
||||
}
|
||||
|
||||
protected void doesNotFlushCache() throws IOException {
|
||||
|
|
|
@ -113,7 +113,7 @@ public class TestHttpCacheJiraNumber1147 {
|
|||
isA(ClassicHttpRequest.class),
|
||||
isA(ExecChain.Scope.class))).thenReturn(response);
|
||||
|
||||
final BasicHttpCache cache = new BasicHttpCache(resourceFactory, httpCacheStorage, cacheConfig);
|
||||
final BasicHttpCache cache = new BasicHttpCache(resourceFactory, httpCacheStorage);
|
||||
final ExecChainHandler t = createCachingExecChain(cache, cacheConfig);
|
||||
|
||||
final ExecChain.Scope scope = new ExecChain.Scope("teset", route, get, mockEndpoint, context);
|
||||
|
|
|
@ -56,6 +56,7 @@ import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
|
|||
import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
|
||||
import org.apache.hc.core5.http.message.BasicHeader;
|
||||
import org.apache.hc.core5.http.message.MessageSupport;
|
||||
import org.apache.hc.core5.util.ByteArrayBuffer;
|
||||
import org.easymock.Capture;
|
||||
import org.easymock.EasyMock;
|
||||
import org.junit.Assert;
|
||||
|
@ -2617,7 +2618,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
|
|||
validated.setHeader("Content-Length", "128");
|
||||
validated.setEntity(new ByteArrayEntity(bytes));
|
||||
|
||||
final ClassicHttpResponse reconstructed = HttpTestUtils.make200Response();
|
||||
final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry();
|
||||
|
||||
final Capture<ClassicHttpRequest> cap = new Capture<>();
|
||||
|
||||
|
@ -2633,12 +2634,13 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
|
|||
EasyMock.expect(mockCache.getCacheEntry(
|
||||
EasyMock.isA(HttpHost.class),
|
||||
EasyMock.isA(ClassicHttpRequest.class))).andReturn(entry).times(0, 1);
|
||||
EasyMock.expect(mockCache.cacheAndReturnResponse(
|
||||
EasyMock.expect(mockCache.createCacheEntry(
|
||||
EasyMock.isA(HttpHost.class),
|
||||
EasyMock.isA(ClassicHttpRequest.class),
|
||||
eqCloseableResponse(validated),
|
||||
EasyMock.isA(ByteArrayBuffer.class),
|
||||
EasyMock.isA(Date.class),
|
||||
EasyMock.isA(Date.class))).andReturn(reconstructed).times(0, 1);
|
||||
EasyMock.isA(Date.class))).andReturn(cacheEntry).times(0, 1);
|
||||
|
||||
replayMocks();
|
||||
final ClassicHttpResponse result = execute(request);
|
||||
|
|
|
@ -43,6 +43,10 @@ public final class SimpleHttpResponse extends BasicHttpResponse {
|
|||
super(code);
|
||||
}
|
||||
|
||||
public SimpleHttpResponse(final int code, final String reasonPhrase) {
|
||||
super(code, reasonPhrase);
|
||||
}
|
||||
|
||||
public static SimpleHttpResponse copy(final HttpResponse original) {
|
||||
Args.notNull(original, "HTTP response");
|
||||
final SimpleHttpResponse copy = new SimpleHttpResponse(original.getCode());
|
||||
|
@ -53,6 +57,34 @@ public final class SimpleHttpResponse extends BasicHttpResponse {
|
|||
return copy;
|
||||
}
|
||||
|
||||
public static SimpleHttpResponse create(final int code) {
|
||||
return new SimpleHttpResponse(code);
|
||||
}
|
||||
|
||||
public static SimpleHttpResponse create(final int code, final String content, final ContentType contentType) {
|
||||
final SimpleHttpResponse response = new SimpleHttpResponse(code);
|
||||
if (content != null) {
|
||||
response.setBodyText(content, contentType);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
public static SimpleHttpResponse create(final int code, final String content) {
|
||||
return create(code, content, ContentType.TEXT_PLAIN);
|
||||
}
|
||||
|
||||
public static SimpleHttpResponse create(final int code, final byte[] content, final ContentType contentType) {
|
||||
final SimpleHttpResponse response = new SimpleHttpResponse(code);
|
||||
if (content != null) {
|
||||
response.setBodyBytes(content, contentType);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
public static SimpleHttpResponse create(final int code, final byte[] content) {
|
||||
return create(code, content, ContentType.TEXT_PLAIN);
|
||||
}
|
||||
|
||||
public void setBody(final SimpleBody body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue