You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by jo...@apache.org on 2011/01/12 18:35:29 UTC

svn commit: r1058247 - in /httpcomponents/httpclient/trunk/httpclient-cache/src: main/java/org/apache/http/impl/client/cache/ test/java/org/apache/http/impl/client/cache/

Author: jonm
Date: Wed Jan 12 17:35:29 2011
New Revision: 1058247

URL: http://svn.apache.org/viewvc?rev=1058247&view=rev
Log:
Implementation fix and acceptance tests for protocol recommendation:

"Many HTTP/1.0 cache implementations will treat an Expires value that
is less than or equal to the response Date value as being equivalent
to the Cache-Control response directive "no-cache". If an HTTP/1.1
cache receives such a response, and the response does not include a
Cache-Control header field, it SHOULD consider the response to be non-
cacheable in order to retain compatibility with HTTP/1.0 servers."

http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.3

Also had to update a few other test cases that incidentally ran afoul
of this recommendation.

Modified:
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseCachingPolicy.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRecommendations.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCachingPolicy.java

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseCachingPolicy.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseCachingPolicy.java?rev=1058247&r1=1058246&r2=1058247&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseCachingPolicy.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseCachingPolicy.java Wed Jan 12 17:35:29 2011
@@ -26,6 +26,8 @@
  */
 package org.apache.http.impl.client.cache;
 
+import java.util.Date;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.http.Header;
@@ -196,6 +198,7 @@ class ResponseCachingPolicy {
             log.debug("Response was not cacheable.");
             return false;
         }
+        
         String[] uncacheableRequestDirectives = { "no-store" };
         if (hasCacheControlParameterFrom(request,uncacheableRequestDirectives)) {
             return false;
@@ -207,6 +210,10 @@ class ResponseCachingPolicy {
             return false;
         }
 
+        if (expiresHeaderLessOrEqualToDateHeaderAndNoCacheControl(response)) {
+            return false;
+        }
+        
         if (sharedCache) {
             Header[] authNHeaders = request.getHeaders("Authorization");
             if (authNHeaders != null && authNHeaders.length > 0) {
@@ -221,6 +228,21 @@ class ResponseCachingPolicy {
         return isResponseCacheable(method, response);
     }
 
+    private boolean expiresHeaderLessOrEqualToDateHeaderAndNoCacheControl(
+            HttpResponse response) {
+        if (response.getFirstHeader("Cache-Control") != null) return false;
+        Header expiresHdr = response.getFirstHeader("Expires");
+        Header dateHdr = response.getFirstHeader("Date");
+        if (expiresHdr == null || dateHdr == null) return false;
+        try {
+            Date expires = DateUtils.parseDate(expiresHdr.getValue());
+            Date date = DateUtils.parseDate(dateHdr.getValue());
+            return expires.equals(date) || expires.before(date);
+        } catch (DateParseException dpe) {
+            return false;
+        }
+    }
+
     private boolean from1_0Origin(HttpResponse response) {
         Header via = response.getFirstHeader("Via");
         if (via != null) {

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRecommendations.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRecommendations.java?rev=1058247&r1=1058246&r2=1058247&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRecommendations.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRecommendations.java Wed Jan 12 17:35:29 2011
@@ -717,6 +717,7 @@ public class TestProtocolRecommendations
         resp1.setHeader("ETag","\"etag\"");
         resp1.setHeader("Date", formatDate(now));
         resp1.setHeader("Expires",formatDate(oneSecondAgo));
+        resp1.setHeader("Cache-Control", "public");
 
         backendExpectsAnyRequest().andReturn(resp1);
 
@@ -1268,4 +1269,68 @@ public class TestProtocolRecommendations
         
         assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result));
     }
+    
+    /*
+     * "Many HTTP/1.0 cache implementations will treat an Expires value
+     * that is less than or equal to the response Date value as being
+     * equivalent to the Cache-Control response directive 'no-cache'.
+     * If an HTTP/1.1 cache receives such a response, and the response
+     * does not include a Cache-Control header field, it SHOULD consider
+     * the response to be non-cacheable in order to retain compatibility
+     * with HTTP/1.0 servers."
+     * 
+     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.3
+     */
+    @Test
+    public void expiresEqualToDateWithNoCacheControlIsNotCacheable()
+        throws Exception {
+        HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
+        HttpResponse resp1 = HttpTestUtils.make200Response();
+        resp1.setHeader("Date", formatDate(now));
+        resp1.setHeader("Expires", formatDate(now));
+        resp1.removeHeaders("Cache-Control");
+        
+        backendExpectsAnyRequest().andReturn(resp1);
+        
+        HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
+        req2.setHeader("Cache-Control", "max-stale=1000");
+        HttpResponse resp2 = HttpTestUtils.make200Response();
+        resp2.setHeader("ETag", "\"etag2\"");
+        
+        backendExpectsAnyRequest().andReturn(resp2);
+        
+        replayMocks();
+        impl.execute(host, req1);
+        HttpResponse result = impl.execute(host, req2);
+        verifyMocks();
+        
+        assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result));
+    }
+    
+    @Test
+    public void expiresPriorToDateWithNoCacheControlIsNotCacheable()
+        throws Exception {
+        HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
+        HttpResponse resp1 = HttpTestUtils.make200Response();
+        resp1.setHeader("Date", formatDate(now));
+        resp1.setHeader("Expires", formatDate(tenSecondsAgo));
+        resp1.removeHeaders("Cache-Control");
+        
+        backendExpectsAnyRequest().andReturn(resp1);
+        
+        HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
+        req2.setHeader("Cache-Control", "max-stale=1000");
+        HttpResponse resp2 = HttpTestUtils.make200Response();
+        resp2.setHeader("ETag", "\"etag2\"");
+        
+        backendExpectsAnyRequest().andReturn(resp2);
+        
+        replayMocks();
+        impl.execute(host, req1);
+        HttpResponse result = impl.execute(host, req2);
+        verifyMocks();
+        
+        assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result));
+    }
+
 }

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCachingPolicy.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCachingPolicy.java?rev=1058247&r1=1058246&r2=1058247&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCachingPolicy.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCachingPolicy.java Wed Jan 12 17:35:29 2011
@@ -51,9 +51,16 @@ public class TestResponseCachingPolicy {
     private int[] acceptableCodes = new int[] { HttpStatus.SC_OK,
             HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION, HttpStatus.SC_MULTIPLE_CHOICES,
             HttpStatus.SC_MOVED_PERMANENTLY, HttpStatus.SC_GONE };
+    private Date now;
+    private Date tenSecondsFromNow;
+    private Date sixSecondsAgo;
 
     @Before
     public void setUp() throws Exception {
+        now = new Date();
+        sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
+        tenSecondsFromNow = new Date(now.getTime() + 10 * 1000L);
+        
         policy = new ResponseCachingPolicy(0, true);
         request = new BasicHttpRequest("GET","/",HTTP_1_1);
         response = new BasicHttpResponse(
@@ -332,8 +339,6 @@ public class TestResponseCachingPolicy {
 
     @Test
     public void testResponsesWithMultipleDateHeadersAreNotCacheable() {
-        Date now = new Date();
-        Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
         response.addHeader("Date", formatDate(now));
         response.addHeader("Date", formatDate(sixSecondsAgo));
         Assert.assertFalse(policy.isResponseCacheable("GET", response));
@@ -381,7 +386,8 @@ public class TestResponseCachingPolicy {
     @Test
     public void testResponsesToGETWithQueryParamsAndExplicitCachingAreCacheable() {
         request = new BasicHttpRequest("GET", "/foo?s=bar");
-        response.setHeader("Expires", formatDate(new Date()));
+        response.setHeader("Date", formatDate(now));
+        response.setHeader("Expires", formatDate(tenSecondsFromNow));
         Assert.assertTrue(policy.isResponseCacheable(request, response));
     }
 
@@ -396,8 +402,6 @@ public class TestResponseCachingPolicy {
     public void getsWithQueryParametersDirectlyFrom1_0OriginsAreNotCacheableEvenWithExpires() {
         request = new BasicHttpRequest("GET", "/foo?s=bar");
         response = new BasicHttpResponse(HttpVersion.HTTP_1_0, HttpStatus.SC_OK, "OK");
-        Date now = new Date();
-        Date tenSecondsFromNow = new Date(now.getTime() + 10 * 1000L);
         response.setHeader("Date", formatDate(now));
         response.setHeader("Expires", formatDate(tenSecondsFromNow));
         Assert.assertFalse(policy.isResponseCacheable(request, response));
@@ -424,8 +428,6 @@ public class TestResponseCachingPolicy {
     @Test
     public void getsWithQueryParametersFrom1_0OriginsViaExplicitProxiesAreNotCacheableEvenWithExpires() {
         request = new BasicHttpRequest("GET", "/foo?s=bar");
-        Date now = new Date();
-        Date tenSecondsFromNow = new Date(now.getTime() + 10 * 1000L);
         response.setHeader("Date", formatDate(now));
         response.setHeader("Expires", formatDate(tenSecondsFromNow));
         response.setHeader("Via", "HTTP/1.0 someproxy");
@@ -435,8 +437,6 @@ public class TestResponseCachingPolicy {
     @Test
     public void getsWithQueryParametersFrom1_1OriginsVia1_0ProxiesAreCacheableWithExpires() {
         request = new BasicHttpRequest("GET", "/foo?s=bar");
-        Date now = new Date();
-        Date tenSecondsFromNow = new Date(now.getTime() + 10 * 1000L);
         response = new BasicHttpResponse(HttpVersion.HTTP_1_0, HttpStatus.SC_OK, "OK");
         response.setHeader("Date", formatDate(now));
         response.setHeader("Expires", formatDate(tenSecondsFromNow));
@@ -444,6 +444,22 @@ public class TestResponseCachingPolicy {
         Assert.assertTrue(policy.isResponseCacheable(request, response));
     }
     
+    @Test
+    public void notCacheableIfExpiresEqualsDateAndNoCacheControl() {
+        response.setHeader("Date", formatDate(now));
+        response.setHeader("Expires", formatDate(now));
+        response.removeHeaders("Cache-Control");
+        Assert.assertFalse(policy.isResponseCacheable(request, response));
+    }
+
+    @Test
+    public void notCacheableIfExpiresPrecedesDateAndNoCacheControl() {
+        response.setHeader("Date", formatDate(now));
+        response.setHeader("Expires", formatDate(sixSecondsAgo));
+        response.removeHeaders("Cache-Control");
+        Assert.assertFalse(policy.isResponseCacheable(request, response));
+    }
+
     private int getRandomStatus() {
         int rnd = (new Random()).nextInt(acceptableCodes.length);