### Eclipse Workspace Patch 1.0 #P httpcomponents-client Index: httpclient-cache/src/main/java/org/apache/http/client/cache/params/CacheParamBean.java =================================================================== --- httpclient-cache/src/main/java/org/apache/http/client/cache/params/CacheParamBean.java (revision 0) +++ httpclient-cache/src/main/java/org/apache/http/client/cache/params/CacheParamBean.java (revision 0) @@ -0,0 +1,22 @@ +package org.apache.http.client.cache.params; + +import org.apache.http.params.HttpAbstractParamBean; +import org.apache.http.params.HttpParams; + +/** + * This class can be used to wrap an instance of {@link HttpParams} + * and manipulate caching parameters using Java Beans conventions. + */ +public class CacheParamBean extends HttpAbstractParamBean { + public CacheParamBean(final HttpParams params) { + super(params); + } + + public void setMaxObjectSizeBytes(int sz) { + CacheParams.setMaxObjectSizeBytes(params, sz); + } + + public void setSharedCache(boolean b) { + CacheParams.setSharedCache(params, b); + } +} Index: httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java =================================================================== --- httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java (revision 979784) +++ httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java (working copy) @@ -51,12 +51,15 @@ import org.apache.http.client.cache.HttpCache; import org.apache.http.client.cache.HttpCacheEntry; import org.apache.http.client.cache.HttpCacheUpdateCallback; +import org.apache.http.client.cache.params.CacheParams; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicHttpResponse; import org.apache.http.message.BasicStatusLine; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.DefaultedHttpParams; import org.apache.http.params.HttpParams; import org.apache.http.protocol.HttpContext; @@ -67,8 +70,6 @@ public class CachingHttpClient implements HttpClient { private final static int MAX_CACHE_ENTRIES = 1000; - private final static int DEFAULT_MAX_OBJECT_SIZE_BYTES = 8192; - private final static boolean SUPPORTS_RANGE_AND_CONTENT_RANGE_HEADERS = false; private final AtomicLong cacheHits = new AtomicLong(); @@ -94,13 +95,16 @@ private final RequestProtocolCompliance requestCompliance; private final Log log = LogFactory.getLog(getClass()); + private boolean sharedCache = true; - public CachingHttpClient(HttpClient client, HttpCache cache, int maxObjectSizeBytes) { + public CachingHttpClient(HttpClient client, HttpCache cache, HttpParams params) { super(); this.backend = client; this.responseCache = cache; + this.maxObjectSizeBytes = CacheParams.getMaxObjectSizeBytes(params); + this.sharedCache = CacheParams.getSharedCache(params); this.validityPolicy = new CacheValidityPolicy(); - this.responseCachingPolicy = new ResponseCachingPolicy(maxObjectSizeBytes); + this.responseCachingPolicy = new ResponseCachingPolicy(maxObjectSizeBytes, this.sharedCache); this.responseGenerator = new CachedHttpResponseGenerator(this.validityPolicy); this.cacheEntryGenerator = new CacheEntryGenerator(); this.uriExtractor = new URIExtractor(); @@ -109,17 +113,16 @@ this.suitabilityChecker = new CachedResponseSuitabilityChecker(this.validityPolicy); this.conditionalRequestBuilder = new ConditionalRequestBuilder(); this.cacheEntryUpdater = new CacheEntryUpdater(); - this.maxObjectSizeBytes = maxObjectSizeBytes; this.responseCompliance = new ResponseProtocolCompliance(); this.requestCompliance = new RequestProtocolCompliance(); } public CachingHttpClient() { - this(new DefaultHttpClient(), new BasicHttpCache(MAX_CACHE_ENTRIES), DEFAULT_MAX_OBJECT_SIZE_BYTES); + this(new DefaultHttpClient(), new BasicHttpCache(MAX_CACHE_ENTRIES), new BasicHttpParams()); } - public CachingHttpClient(HttpCache cache, int maxObjectSizeBytes) { - this(new DefaultHttpClient(), cache, maxObjectSizeBytes); + public CachingHttpClient(HttpCache cache, HttpParams params) { + this(new DefaultHttpClient(), cache, params); } CachingHttpClient(HttpClient backend, CacheValidityPolicy validityPolicy, ResponseCachingPolicy responseCachingPolicy, @@ -130,7 +133,9 @@ ConditionalRequestBuilder conditionalRequestBuilder, CacheEntryUpdater entryUpdater, ResponseProtocolCompliance responseCompliance, RequestProtocolCompliance requestCompliance) { - this.maxObjectSizeBytes = DEFAULT_MAX_OBJECT_SIZE_BYTES; + HttpParams params = new BasicHttpParams(); + this.maxObjectSizeBytes = CacheParams.getMaxObjectSizeBytes(params); + this.sharedCache = CacheParams.getSharedCache(params); this.backend = backend; this.validityPolicy = validityPolicy; this.responseCachingPolicy = responseCachingPolicy; @@ -383,7 +388,7 @@ } public boolean isSharedCache() { - return true; + return sharedCache; } Date getCurrentDate() { Index: httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRequirements.java =================================================================== --- httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRequirements.java (revision 979784) +++ httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRequirements.java (working copy) @@ -34,7 +34,6 @@ import org.apache.http.Header; import org.apache.http.HeaderElement; -import org.apache.http.HttpEntity; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; @@ -43,9 +42,8 @@ import org.apache.http.HttpVersion; import org.apache.http.ProtocolVersion; import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.HttpClient; -import org.apache.http.client.cache.HttpCache; import org.apache.http.client.cache.HttpCacheEntry; +import org.apache.http.client.cache.params.CacheParams; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.impl.client.RequestWrapper; import org.apache.http.impl.cookie.DateUtils; @@ -53,12 +51,12 @@ import org.apache.http.message.BasicHttpEntityEnclosingRequest; import org.apache.http.message.BasicHttpRequest; import org.apache.http.message.BasicHttpResponse; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpParams; import org.apache.http.protocol.HttpContext; import org.easymock.Capture; -import org.easymock.IExpectationSetters; import org.easymock.classextension.EasyMock; import org.junit.Assert; -import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -71,89 +69,8 @@ * pass downstream to the backend HttpClient are are conditionally compliant * with the rules for an HTTP/1.1 client. */ -public class TestProtocolRequirements { +public class TestProtocolRequirements extends AbstractProtocolTest { - private static final int MAX_BYTES = 1024; - private static final int MAX_ENTRIES = 100; - private int entityLength = 128; - - private HttpHost host; - private HttpEntity body; - private HttpEntity mockEntity; - private HttpClient mockBackend; - private HttpCache mockCache; - private HttpRequest request; - private HttpResponse originResponse; - - private CachingHttpClient impl; - - @Before - public void setUp() { - host = new HttpHost("foo.example.com"); - - body = HttpTestUtils.makeBody(entityLength); - - request = new BasicHttpRequest("GET", "/foo", HttpVersion.HTTP_1_1); - - originResponse = make200Response(); - - HttpCache cache = new BasicHttpCache(MAX_ENTRIES); - mockBackend = EasyMock.createMock(HttpClient.class); - mockEntity = EasyMock.createMock(HttpEntity.class); - mockCache = EasyMock.createMock(HttpCache.class); - impl = new CachingHttpClient(mockBackend, cache, MAX_BYTES); - } - - private void replayMocks() { - EasyMock.replay(mockBackend); - EasyMock.replay(mockCache); - EasyMock.replay(mockEntity); - } - - private void verifyMocks() { - EasyMock.verify(mockBackend); - EasyMock.verify(mockCache); - EasyMock.verify(mockEntity); - } - - private HttpResponse make200Response() { - HttpResponse out = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); - out.setHeader("Date", DateUtils.formatDate(new Date())); - out.setHeader("Server", "MockOrigin/1.0"); - out.setHeader("Content-Length", "128"); - out.setEntity(makeBody(128)); - return out; - } - - private HttpEntity makeBody(int nbytes) { - return HttpTestUtils.makeBody(nbytes); - } - - private IExpectationSetters backendExpectsAnyRequest() throws Exception { - HttpResponse resp = mockBackend.execute(EasyMock.isA(HttpHost.class), EasyMock - .isA(HttpRequest.class), (HttpContext) EasyMock.isNull()); - return EasyMock.expect(resp); - } - - private void emptyMockCacheExpectsNoPuts() throws Exception { - mockBackend = EasyMock.createMock(HttpClient.class); - mockCache = EasyMock.createMock(HttpCache.class); - mockEntity = EasyMock.createMock(HttpEntity.class); - - impl = new CachingHttpClient(mockBackend, mockCache, MAX_BYTES); - - EasyMock.expect(mockCache.getEntry((String) EasyMock.anyObject())).andReturn(null) - .anyTimes(); - - mockCache.removeEntry(EasyMock.isA(String.class)); - EasyMock.expectLastCall().anyTimes(); - } - - public static HttpRequest eqRequest(HttpRequest in) { - EasyMock.reportMatcher(new RequestEquivalent(in)); - return null; - } - @Test public void testCacheMissOnGETUsesOriginResponse() throws Exception { EasyMock.expect(mockBackend.execute(host, request, (HttpContext) null)).andReturn( @@ -2307,7 +2224,9 @@ mockCache.putEntry(EasyMock.eq("http://foo.example.com/thing"), EasyMock.isA(HttpCacheEntry.class)); - impl = new CachingHttpClient(mockBackend, mockCache, MAX_BYTES); + HttpParams params = new BasicHttpParams(); + CacheParams.setMaxObjectSizeBytes(params, MAX_BYTES); + impl = new CachingHttpClient(mockBackend, mockCache, params); HttpRequest validate = new BasicHttpRequest("GET", "/thing", HttpVersion.HTTP_1_1); validate.setHeader("If-None-Match", "\"etag\""); @@ -2349,7 +2268,9 @@ CacheEntry entry = new CacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes); - impl = new CachingHttpClient(mockBackend, mockCache, MAX_BYTES); + HttpParams params = new BasicHttpParams(); + CacheParams.setMaxObjectSizeBytes(params, MAX_BYTES); + impl = new CachingHttpClient(mockBackend, mockCache, params); EasyMock.expect(mockCache.getEntry("http://foo.example.com/thing")).andReturn(entry); @@ -2390,7 +2311,9 @@ CacheEntry entry = new CacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes); - impl = new CachingHttpClient(mockBackend, mockCache, MAX_BYTES); + HttpParams params = new BasicHttpParams(); + CacheParams.setMaxObjectSizeBytes(params, MAX_BYTES); + impl = new CachingHttpClient(mockBackend, mockCache, params); EasyMock.expect(mockCache.getEntry("http://foo.example.com/thing")).andReturn(entry); EasyMock.expect( @@ -2591,7 +2514,9 @@ CacheEntry entry = new CacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes); - impl = new CachingHttpClient(mockBackend, mockCache, MAX_BYTES); + HttpParams params = new BasicHttpParams(); + CacheParams.setMaxObjectSizeBytes(params, MAX_BYTES); + impl = new CachingHttpClient(mockBackend, mockCache, params); EasyMock.expect(mockCache.getEntry("http://foo.example.com/thing")).andReturn(entry); @@ -2635,7 +2560,9 @@ CacheEntry entry = new CacheEntry(requestTime, responseTime, hdrs, bytes); - impl = new CachingHttpClient(mockBackend, mockCache, MAX_BYTES); + HttpParams params = new BasicHttpParams(); + CacheParams.setMaxObjectSizeBytes(params, MAX_BYTES); + impl = new CachingHttpClient(mockBackend, mockCache, params); HttpResponse validated = make200Response(); validated.setHeader("Cache-Control", "public"); Index: httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseCachingPolicy.java =================================================================== --- httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseCachingPolicy.java (revision 979784) +++ httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseCachingPolicy.java (working copy) @@ -51,15 +51,17 @@ private final int maxObjectSizeBytes; private final Log log = LogFactory.getLog(getClass()); - + private final boolean isSharedCache; + /** * Define a cache policy that limits the size of things that should be stored * in the cache to a maximum of {@link HttpResponse} bytes in size. * * @param maxObjectSizeBytes the size to limit items into the cache */ - public ResponseCachingPolicy(int maxObjectSizeBytes) { + public ResponseCachingPolicy(int maxObjectSizeBytes, boolean isSharedCache) { this.maxObjectSizeBytes = maxObjectSizeBytes; + this.isSharedCache = isSharedCache; } /** @@ -148,7 +150,7 @@ for (HeaderElement elem : header.getElements()) { if (HeaderConstants.CACHE_CONTROL_NO_STORE.equals(elem.getName()) || HeaderConstants.CACHE_CONTROL_NO_CACHE.equals(elem.getName()) - || "private".equals(elem.getName())) { + || (isSharedCache && "private".equals(elem.getName()))) { return true; } } @@ -202,12 +204,14 @@ return false; } - Header[] authNHeaders = request.getHeaders("Authorization"); - if (authNHeaders != null && authNHeaders.length > 0) { - String[] authCacheableParams = { - "s-maxage", "must-revalidate", "public" - }; - return hasCacheControlParameterFrom(response, authCacheableParams); + if (isSharedCache) { + Header[] authNHeaders = request.getHeaders("Authorization"); + if (authNHeaders != null && authNHeaders.length > 0) { + String[] authCacheableParams = { + "s-maxage", "must-revalidate", "public" + }; + return hasCacheControlParameterFrom(response, authCacheableParams); + } } String method = request.getRequestLine().getMethod(); Index: httpclient-cache/src/test/java/org/apache/http/impl/client/cache/AbstractProtocolTest.java =================================================================== --- httpclient-cache/src/test/java/org/apache/http/impl/client/cache/AbstractProtocolTest.java (revision 0) +++ httpclient-cache/src/test/java/org/apache/http/impl/client/cache/AbstractProtocolTest.java (revision 0) @@ -0,0 +1,119 @@ +package org.apache.http.impl.client.cache; + +import java.util.Date; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.HttpVersion; +import org.apache.http.client.HttpClient; +import org.apache.http.client.cache.HttpCache; +import org.apache.http.client.cache.params.CacheParams; +import org.apache.http.impl.cookie.DateUtils; +import org.apache.http.message.BasicHttpRequest; +import org.apache.http.message.BasicHttpResponse; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpParams; +import org.apache.http.protocol.HttpContext; +import org.easymock.IExpectationSetters; +import org.easymock.classextension.EasyMock; +import org.junit.Before; + +public class AbstractProtocolTest { + + protected static final int MAX_BYTES = 1024; + protected static final int MAX_ENTRIES = 100; + protected static final int entityLength = 128; + protected HttpHost host; + protected HttpEntity body; + protected HttpEntity mockEntity; + protected HttpClient mockBackend; + protected HttpCache mockCache; + protected HttpRequest request; + protected HttpResponse originResponse; + protected CachingHttpClient impl; + protected HttpParams params; + protected HttpCache cache; + + public static HttpRequest eqRequest(HttpRequest in) { + EasyMock.reportMatcher(new RequestEquivalent(in)); + return null; + } + + public AbstractProtocolTest() { + super(); + } + + protected void replayMocks() { + EasyMock.replay(mockBackend); + EasyMock.replay(mockCache); + EasyMock.replay(mockEntity); + } + + protected void verifyMocks() { + EasyMock.verify(mockBackend); + EasyMock.verify(mockCache); + EasyMock.verify(mockEntity); + } + + protected HttpResponse make200Response() { + HttpResponse out = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); + out.setHeader("Date", DateUtils.formatDate(new Date())); + out.setHeader("Server", "MockOrigin/1.0"); + out.setHeader("Content-Length", "128"); + out.setEntity(makeBody(128)); + return out; + } + + protected IExpectationSetters backendExpectsAnyRequest() throws Exception { + HttpResponse resp = mockBackend.execute(EasyMock.isA(HttpHost.class), EasyMock + .isA(HttpRequest.class), (HttpContext) EasyMock.isNull()); + return EasyMock.expect(resp); + } + + protected void emptyMockCacheExpectsNoPuts() throws Exception { + mockBackend = EasyMock.createMock(HttpClient.class); + mockCache = EasyMock.createMock(HttpCache.class); + mockEntity = EasyMock.createMock(HttpEntity.class); + + impl = new CachingHttpClient(mockBackend, mockCache, params); + + EasyMock.expect(mockCache.getEntry((String) EasyMock.anyObject())).andReturn(null) + .anyTimes(); + + mockCache.removeEntry(EasyMock.isA(String.class)); + EasyMock.expectLastCall().anyTimes(); + } + + protected HttpEntity makeBody(int nbytes) { + return HttpTestUtils.makeBody(nbytes); + } + + protected void behaveAsNonSharedCache() { + CacheParams.setSharedCache(params, false); + impl = new CachingHttpClient(mockBackend, cache, params); + } + + @Before + public void setUp() { + host = new HttpHost("foo.example.com"); + + body = HttpTestUtils.makeBody(entityLength); + + request = new BasicHttpRequest("GET", "/foo", HttpVersion.HTTP_1_1); + + originResponse = make200Response(); + + cache = new BasicHttpCache(MAX_ENTRIES); + mockBackend = EasyMock.createMock(HttpClient.class); + mockEntity = EasyMock.createMock(HttpEntity.class); + mockCache = EasyMock.createMock(HttpCache.class); + + params = new BasicHttpParams(); + CacheParams.setMaxObjectSizeBytes(params, MAX_BYTES); + impl = new CachingHttpClient(mockBackend, cache, params); + } + +} \ No newline at end of file Index: httpclient-cache/src/main/java/org/apache/http/client/cache/params/CacheParams.java =================================================================== --- httpclient-cache/src/main/java/org/apache/http/client/cache/params/CacheParams.java (revision 0) +++ httpclient-cache/src/main/java/org/apache/http/client/cache/params/CacheParams.java (revision 0) @@ -0,0 +1,68 @@ +package org.apache.http.client.cache.params; + +import org.apache.http.annotation.Immutable; +import org.apache.http.impl.client.cache.CacheDefaults; +import org.apache.http.impl.client.cache.CachingHttpClient; +import org.apache.http.params.HttpParams; + +/** + * An adapter for manipulating HTTP cache parameters in {@link HttpParams}. + */ +@Immutable +public final class CacheParams { + + private CacheParams() { + super(); + } + + private static void checkParamsNotNull(final HttpParams params) { + if (params == null) { + throw new IllegalArgumentException("HTTP parameters may not be null"); + } + } + + /** + * Obtains the maximum object size in bytes that will be cached by a + * {@link CachingHttpClient}. If not configured, + * {@link CacheDefaults#DEFAULT_MAX_OBJECT_SIZE_BYTES} is used instead. + * + * @return maximum cached object size in bytes + */ + public static int getMaxObjectSizeBytes(final HttpParams params) { + checkParamsNotNull(params); + return params.getIntParameter(CachePNames.MAX_OBJECT_SIZE_BYTES, + CacheDefaults.DEFAULT_MAX_OBJECT_SIZE_BYTES); + } + + /** + * Sets the maximum object size that will be cached by a + * {@link CachingHttpClient}. + * @param sz maximum size in bytes + */ + public static void setMaxObjectSizeBytes(final HttpParams params, int sz) { + checkParamsNotNull(params); + params.setIntParameter(CachePNames.MAX_OBJECT_SIZE_BYTES, sz); + } + + /** + * Obtains whether a {@link CachingHttpClient} will behave as a + * shared cache or not. If not set, defaults to true + * (behave as a shared cache). + * @return if the cache behaves as a shared cache + */ + public static boolean getSharedCache(final HttpParams params) { + checkParamsNotNull(params); + return params.getBooleanParameter(CachePNames.SHARED_CACHE, true); + } + + /** + * Sets whether a {@link CachingHttpClient} should behave as a + * shared cache or not. + * @param b should be true for a shared cache, false for a non-shared + * (private) cache + */ + public static void setSharedCache(final HttpParams params, boolean b) { + checkParamsNotNull(params); + params.setBooleanParameter(CachePNames.SHARED_CACHE, b); + } +} Index: httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DoNotTestProtocolRequirements.java =================================================================== --- httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DoNotTestProtocolRequirements.java (revision 979784) +++ httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DoNotTestProtocolRequirements.java (working copy) @@ -41,10 +41,13 @@ import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.cache.HttpCache; +import org.apache.http.client.cache.params.CacheParams; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.impl.cookie.DateUtils; import org.apache.http.message.BasicHttpRequest; import org.apache.http.message.BasicHttpResponse; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpParams; import org.apache.http.protocol.HttpContext; import org.easymock.classextension.EasyMock; import org.junit.Assert; @@ -80,7 +83,9 @@ mockBackend = EasyMock.createMock(HttpClient.class); mockEntity = EasyMock.createMock(HttpEntity.class); mockCache = EasyMock.createMock(HttpCache.class); - impl = new CachingHttpClient(mockBackend, cache, MAX_BYTES); + HttpParams params = new BasicHttpParams(); + CacheParams.setMaxObjectSizeBytes(params, MAX_BYTES); + impl = new CachingHttpClient(mockBackend, cache, params); } private HttpResponse make200Response() { Index: httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheDefaults.java =================================================================== --- httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheDefaults.java (revision 0) +++ httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheDefaults.java (revision 0) @@ -0,0 +1,17 @@ +package org.apache.http.impl.client.cache; + +/** + * Default cache parameter settings. + */ +public final class CacheDefaults { + + private CacheDefaults() { + super(); + } + + /** + * Default maximum cacheable object size (8192 bytes). + */ + public final static int DEFAULT_MAX_OBJECT_SIZE_BYTES = 8192; + +} Index: httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolAllowedBehavior.java =================================================================== --- httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolAllowedBehavior.java (revision 0) +++ httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolAllowedBehavior.java (revision 0) @@ -0,0 +1,64 @@ +package org.apache.http.impl.client.cache; + +import java.net.SocketTimeoutException; +import java.util.Date; + +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.HttpVersion; +import org.apache.http.impl.cookie.DateUtils; +import org.apache.http.message.BasicHttpRequest; +import org.junit.Assert; +import org.junit.Test; + +/** + * This class tests behavior that is allowed (MAY) by the HTTP/1.1 protocol + * specification and for which we have implemented the behavior in the + * {@link CachingHttpClient}. + */ +public class TestProtocolAllowedBehavior extends AbstractProtocolTest { + + @Test + public void testNonSharedCacheReturnsStaleResponseWhenRevalidationFailsForProxyRevalidate() throws Exception { + + HttpRequest req1 = new BasicHttpRequest("GET","/",HttpVersion.HTTP_1_1); + Date now = new Date(); + Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L); + originResponse.setHeader("Date",DateUtils.formatDate(tenSecondsAgo)); + originResponse.setHeader("Cache-Control","max-age=5, proxy-revalidate"); + originResponse.setHeader("Etag","\"etag\""); + + backendExpectsAnyRequest().andReturn(originResponse); + + HttpRequest req2 = new BasicHttpRequest("GET","/",HttpVersion.HTTP_1_1); + backendExpectsAnyRequest().andThrow(new SocketTimeoutException()); + + replayMocks(); + behaveAsNonSharedCache(); + impl.execute(host,req1); + HttpResponse result = impl.execute(host,req2); + verifyMocks(); + + Assert.assertEquals(HttpStatus.SC_OK, result.getStatusLine().getStatusCode()); + } + + @Test + public void testNonSharedCacheMayCacheResponsesWithCacheControlPrivate() throws Exception { + HttpRequest req1 = new BasicHttpRequest("GET","/",HttpVersion.HTTP_1_1); + originResponse.setHeader("Cache-Control","private,max-age=3600"); + + backendExpectsAnyRequest().andReturn(originResponse); + + HttpRequest req2 = new BasicHttpRequest("GET","/",HttpVersion.HTTP_1_1); + + replayMocks(); + behaveAsNonSharedCache(); + impl.execute(host,req1); + HttpResponse result = impl.execute(host,req2); + verifyMocks(); + + Assert.assertEquals(HttpStatus.SC_OK, result.getStatusLine().getStatusCode()); + } + +} Index: httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java =================================================================== --- httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java (revision 979784) +++ httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java (working copy) @@ -51,6 +51,7 @@ import org.apache.http.client.ResponseHandler; import org.apache.http.client.cache.HttpCache; import org.apache.http.client.cache.HttpCacheEntry; +import org.apache.http.client.cache.params.CacheParams; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.conn.ClientConnectionManager; @@ -63,6 +64,7 @@ import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.apache.http.message.BasicHttpResponse; import org.apache.http.message.BasicStatusLine; +import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpParams; import org.apache.http.protocol.HttpContext; import org.easymock.classextension.EasyMock; @@ -884,7 +886,9 @@ HttpCache cacheImpl = new BasicHttpCache(100); - CachingHttpClient cachingClient = new CachingHttpClient(httpClient, cacheImpl, 8192); + HttpParams params = new BasicHttpParams(); + CacheParams.setMaxObjectSizeBytes(params, 8192); + CachingHttpClient cachingClient = new CachingHttpClient(httpClient, cacheImpl, params); HttpUriRequest request = new HttpGet("http://www.fancast.com/static-28262/styles/base.css"); Index: httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolDeviations.java =================================================================== --- httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolDeviations.java (revision 979784) +++ httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolDeviations.java (working copy) @@ -39,11 +39,14 @@ import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.cache.HttpCache; +import org.apache.http.client.cache.params.CacheParams; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.impl.cookie.DateUtils; import org.apache.http.message.BasicHttpEntityEnclosingRequest; import org.apache.http.message.BasicHttpRequest; import org.apache.http.message.BasicHttpResponse; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpParams; import org.apache.http.protocol.HttpContext; import org.easymock.Capture; import org.easymock.classextension.EasyMock; @@ -97,7 +100,10 @@ mockBackend = EasyMock.createMock(HttpClient.class); mockEntity = EasyMock.createMock(HttpEntity.class); mockCache = EasyMock.createMock(HttpCache.class); - impl = new CachingHttpClient(mockBackend, cache, MAX_BYTES); + + HttpParams params = new BasicHttpParams(); + CacheParams.setMaxObjectSizeBytes(params, MAX_BYTES); + impl = new CachingHttpClient(mockBackend, cache, params); } private HttpResponse make200Response() { Index: httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCachingPolicy.java =================================================================== --- httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCachingPolicy.java (revision 979784) +++ httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCachingPolicy.java (working copy) @@ -53,7 +53,7 @@ @Before public void setUp() throws Exception { - policy = new ResponseCachingPolicy(0); + policy = new ResponseCachingPolicy(0, true); request = new BasicHttpRequest("GET","/",HTTP_1_1); response = new BasicHttpResponse( new BasicStatusLine(HTTP_1_1, HttpStatus.SC_OK, "")); @@ -67,6 +67,14 @@ } @Test + public void testResponsesToRequestsWithAuthorizationHeadersAreCacheableByNonSharedCache() { + policy = new ResponseCachingPolicy(0,false); + request = new BasicHttpRequest("GET","/",HTTP_1_1); + request.setHeader("Authorization","Basic dXNlcjpwYXNzd2Q="); + Assert.assertTrue(policy.isResponseCacheable(request,response)); + } + + @Test public void testResponsesToRequestsWithAuthorizationHeadersAreNotCacheable() { request = new BasicHttpRequest("GET","/",HTTP_1_1); request.setHeader("Authorization","Basic dXNlcjpwYXNzd2Q="); @@ -199,9 +207,16 @@ Assert.assertTrue(policy.isResponseCacheable("GET", response)); } - // are we truly a non-shared cache? best be safe @Test - public void testNon206WithPrivateCacheControlIsNotCacheable() { + public void testPrivateCacheControlIsCacheableByNonSharedCache() { + policy = new ResponseCachingPolicy(0, false); + response.setStatusCode(HttpStatus.SC_OK); + response.setHeader("Cache-Control","private"); + Assert.assertTrue(policy.isResponseCacheable("GET", response)); + } + + @Test + public void testNon206WithPrivateCacheControlIsNotCacheableBySharedCache() { int status = getRandomStatus(); response.setStatusCode(status); response.setHeader("Cache-Control", "private"); Index: httpclient-cache/src/main/java/org/apache/http/client/cache/params/CachePNames.java =================================================================== --- httpclient-cache/src/main/java/org/apache/http/client/cache/params/CachePNames.java (revision 0) +++ httpclient-cache/src/main/java/org/apache/http/client/cache/params/CachePNames.java (revision 0) @@ -0,0 +1,22 @@ +package org.apache.http.client.cache.params; + +/** + * Parameter names for HTTP cache configuration. + */ +public interface CachePNames { + /** + * Defines the maximum size of an object that will be cached + * by a {@link org.apache.http.impl.client.cache.CachingHttpClient}. + *

+ * This parameter expects a value of type {@link Integer}. + */ + public static final String MAX_OBJECT_SIZE_BYTES = "http.cache.max-object-size-bytes"; + + /** + * Defines whether a {@link org.apache.http.impl.client.cache.CachingHttpClient} + * will behave as a shared cache or not. + *

+ * This parameter expects a value of type {@link Boolean}. + */ + public static final String SHARED_CACHE = "http.cache.shared-cache"; +}