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 2013/08/30 18:01:38 UTC

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

Author: jonm
Date: Fri Aug 30 16:01:37 2013
New Revision: 1519005

URL: http://svn.apache.org/r1519005
Log:
HTTPCLIENT-1371: Weak ETag Validation is Useful On PUT With If-Match
Contributed by James Leigh <james at 3roundstones dot com>

Modified:
    httpcomponents/httpclient/trunk/RELEASE_NOTES.txt
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java-deprecated/org/apache/http/impl/client/cache/CachingHttpClient.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheConfig.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingExec.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/RequestProtocolCompliance.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestRequestProtocolCompliance.java

Modified: httpcomponents/httpclient/trunk/RELEASE_NOTES.txt
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/RELEASE_NOTES.txt?rev=1519005&r1=1519004&r2=1519005&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/RELEASE_NOTES.txt (original)
+++ httpcomponents/httpclient/trunk/RELEASE_NOTES.txt Fri Aug 30 16:01:37 2013
@@ -34,6 +34,8 @@ This release also includes all fixes fro
 
 Changelog
 -------------------
+* [HTTPCLIENT-1371] Weak ETag Validation is Useful On PUT With If-Match
+  Contributed by James Leigh <james at 3roundstones dot com>
 
 * [HTTPCLIENT-1384] Expose CacheInvalidator interface.
   Contributed by Nicolas Richeton <nicolas.richeton at free.fr>

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java-deprecated/org/apache/http/impl/client/cache/CachingHttpClient.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java-deprecated/org/apache/http/impl/client/cache/CachingHttpClient.java?rev=1519005&r1=1519004&r2=1519005&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java-deprecated/org/apache/http/impl/client/cache/CachingHttpClient.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java-deprecated/org/apache/http/impl/client/cache/CachingHttpClient.java Fri Aug 30 16:01:37 2013
@@ -182,7 +182,7 @@ public class CachingHttpClient implement
         this.conditionalRequestBuilder = new ConditionalRequestBuilder();
 
         this.responseCompliance = new ResponseProtocolCompliance();
-        this.requestCompliance = new RequestProtocolCompliance();
+        this.requestCompliance = new RequestProtocolCompliance(config.isWeakETagOnPutDeleteAllowed());
 
         this.asynchRevalidator = makeAsynchronousValidator(config);
     }

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheConfig.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheConfig.java?rev=1519005&r1=1519004&r2=1519005&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheConfig.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheConfig.java Fri Aug 30 16:01:37 2013
@@ -65,6 +65,14 @@ import org.apache.http.util.Args;
  * adherence to the existing standard, but you may want to
  * {@link Builder#setAllow303Caching(boolean) enable it}.
  *
+ * <p><b>Weak ETags on PUT/DELETE If-Match requests</b>. RFC2616 explicitly
+ * prohibits the use of weak validators in non-GET requests, however, the
+ * HTTPbis working group says while the limitation for weak validators on ranged
+ * requests makes sense, weak ETag validation is useful on full non-GET
+ * requests; e.g., PUT with If-Match. This behavior is off by default, to err on
+ * the side of a conservative adherence to the existing standard, but you may
+ * want to {@link Builder#setWeakETagOnPutDeleteAllowed(boolean) enable it}.
+ *
  * <p><b>Heuristic caching</b>. Per RFC2616, a cache may cache certain cache
  * entries even if no explicit cache control headers are set by the origin.
  * This behavior is off by default, but you may want to turn this on if you
@@ -113,6 +121,10 @@ public class CacheConfig implements Clon
      */
     public final static boolean DEFAULT_303_CACHING_ENABLED = false;
 
+    /** Default setting to allow weak tags on PUT/DELETE methods
+     */
+    public final static boolean DEFAULT_WEAK_ETAG_ON_PUTDELETE_ALLOWED = false;
+
     /** Default setting for heuristic caching
      */
     public final static boolean DEFAULT_HEURISTIC_CACHING_ENABLED = false;
@@ -153,6 +165,7 @@ public class CacheConfig implements Clon
     private int maxCacheEntries;
     private int maxUpdateRetries;
     private boolean allow303Caching;
+    private boolean weakETagOnPutDeleteAllowed;
     private boolean heuristicCachingEnabled;
     private float heuristicCoefficient;
     private long heuristicDefaultLifetime;
@@ -172,8 +185,9 @@ public class CacheConfig implements Clon
         this.maxObjectSize = DEFAULT_MAX_OBJECT_SIZE_BYTES;
         this.maxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES;
         this.maxUpdateRetries = DEFAULT_MAX_UPDATE_RETRIES;
-        this.allow303Caching = false;
-        this.heuristicCachingEnabled = false;
+        this.allow303Caching = DEFAULT_303_CACHING_ENABLED;
+        this.weakETagOnPutDeleteAllowed = DEFAULT_WEAK_ETAG_ON_PUTDELETE_ALLOWED;
+        this.heuristicCachingEnabled = DEFAULT_HEURISTIC_CACHING_ENABLED;
         this.heuristicCoefficient = DEFAULT_HEURISTIC_COEFFICIENT;
         this.heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME;
         this.isSharedCache = true;
@@ -188,6 +202,7 @@ public class CacheConfig implements Clon
             final int maxCacheEntries,
             final int maxUpdateRetries,
             final boolean allow303Caching,
+            final boolean weakETagOnPutDeleteAllowed,
             final boolean heuristicCachingEnabled,
             final float heuristicCoefficient,
             final long heuristicDefaultLifetime,
@@ -202,6 +217,7 @@ public class CacheConfig implements Clon
         this.maxCacheEntries = maxCacheEntries;
         this.maxUpdateRetries = maxUpdateRetries;
         this.allow303Caching = allow303Caching;
+        this.weakETagOnPutDeleteAllowed = weakETagOnPutDeleteAllowed;
         this.heuristicCachingEnabled = heuristicCachingEnabled;
         this.heuristicCoefficient = heuristicCoefficient;
         this.heuristicDefaultLifetime = heuristicDefaultLifetime;
@@ -313,6 +329,14 @@ public class CacheConfig implements Clon
     }
 
     /**
+     * Returns whether weak etags is allowed with PUT/DELETE methods.
+     * @return {@code true} if it is allowed.
+     */
+    public boolean isWeakETagOnPutDeleteAllowed() {
+        return weakETagOnPutDeleteAllowed;
+    }
+
+    /**
      * Returns whether heuristic caching is enabled.
      * @return {@code true} if it is enabled.
      */
@@ -519,6 +543,7 @@ public class CacheConfig implements Clon
         private int maxCacheEntries;
         private int maxUpdateRetries;
         private boolean allow303Caching;
+        private boolean weakETagOnPutDeleteAllowed;
         private boolean heuristicCachingEnabled;
         private float heuristicCoefficient;
         private long heuristicDefaultLifetime;
@@ -533,7 +558,8 @@ public class CacheConfig implements Clon
             this.maxObjectSize = DEFAULT_MAX_OBJECT_SIZE_BYTES;
             this.maxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES;
             this.maxUpdateRetries = DEFAULT_MAX_UPDATE_RETRIES;
-            this.allow303Caching = false;
+            this.allow303Caching = DEFAULT_303_CACHING_ENABLED;
+            this.weakETagOnPutDeleteAllowed = DEFAULT_WEAK_ETAG_ON_PUTDELETE_ALLOWED;
             this.heuristicCachingEnabled = false;
             this.heuristicCoefficient = DEFAULT_HEURISTIC_COEFFICIENT;
             this.heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME;
@@ -580,6 +606,16 @@ public class CacheConfig implements Clon
         }
 
         /**
+         * Allows or disallows weak etags to be used with PUT/DELETE If-Match requests.
+         * @param weakETagOnPutDeleteAllowed should be {@code true} to
+         *   permit weak etags, {@code false} to reject them.
+         */
+        public Builder setWeakETagOnPutDeleteAllowed(final boolean weakETagOnPutDeleteAllowed) {
+            this.weakETagOnPutDeleteAllowed = weakETagOnPutDeleteAllowed;
+            return this;
+        }
+
+        /**
          * Enables or disables heuristic caching.
          * @param heuristicCachingEnabled should be {@code true} to
          *   permit heuristic caching, {@code false} to enable it.
@@ -690,6 +726,7 @@ public class CacheConfig implements Clon
                     maxCacheEntries,
                     maxUpdateRetries,
                     allow303Caching,
+                    weakETagOnPutDeleteAllowed,
                     heuristicCachingEnabled,
                     heuristicCoefficient,
                     heuristicDefaultLifetime,
@@ -710,6 +747,7 @@ public class CacheConfig implements Clon
                 .append(", maxCacheEntries=").append(this.maxCacheEntries)
                 .append(", maxUpdateRetries=").append(this.maxUpdateRetries)
                 .append(", 303CachingEnabled=").append(this.allow303Caching)
+                .append(", weakETagOnPutDeleteAllowed=").append(this.weakETagOnPutDeleteAllowed)
                 .append(", heuristicCachingEnabled=").append(this.heuristicCachingEnabled)
                 .append(", heuristicCoefficient=").append(this.heuristicCoefficient)
                 .append(", heuristicDefaultLifetime=").append(this.heuristicDefaultLifetime)

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingExec.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingExec.java?rev=1519005&r1=1519004&r2=1519005&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingExec.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingExec.java Fri Aug 30 16:01:37 2013
@@ -139,7 +139,7 @@ public class CachingExec implements Clie
         this.suitabilityChecker = new CachedResponseSuitabilityChecker(this.validityPolicy, config);
         this.conditionalRequestBuilder = new ConditionalRequestBuilder();
         this.responseCompliance = new ResponseProtocolCompliance();
-        this.requestCompliance = new RequestProtocolCompliance();
+        this.requestCompliance = new RequestProtocolCompliance(config.isWeakETagOnPutDeleteAllowed());
         this.responseCachingPolicy = new ResponseCachingPolicy(
                 this.cacheConfig.getMaxObjectSize(), this.cacheConfig.isSharedCache(),
                 this.cacheConfig.isNeverCacheHTTP10ResponsesWithQuery(), this.cacheConfig.is303CachingEnabled());

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/RequestProtocolCompliance.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/RequestProtocolCompliance.java?rev=1519005&r1=1519004&r2=1519005&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/RequestProtocolCompliance.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/RequestProtocolCompliance.java Fri Aug 30 16:01:37 2013
@@ -54,6 +54,17 @@ import org.apache.http.protocol.HTTP;
  */
 @Immutable
 class RequestProtocolCompliance {
+    private final boolean weakETagOnPutDeleteAllowed;
+
+    public RequestProtocolCompliance() {
+        super();
+        this.weakETagOnPutDeleteAllowed = false;
+    }
+
+    public RequestProtocolCompliance(final boolean weakETagOnPutDeleteAllowed) {
+        super();
+        this.weakETagOnPutDeleteAllowed = weakETagOnPutDeleteAllowed;
+    }
 
     private static final List<String> disallowedWithNoCache =
         Arrays.asList(HeaderConstants.CACHE_CONTROL_MIN_FRESH, HeaderConstants.CACHE_CONTROL_MAX_STALE, HeaderConstants.CACHE_CONTROL_MAX_AGE);
@@ -73,9 +84,11 @@ class RequestProtocolCompliance {
             theErrors.add(anError);
         }
 
-        anError = requestHasWeekETagForPUTOrDELETEIfMatch(request);
-        if (anError != null) {
-            theErrors.add(anError);
+        if (!weakETagOnPutDeleteAllowed) {
+            anError = requestHasWeekETagForPUTOrDELETEIfMatch(request);
+            if (anError != null) {
+                theErrors.add(anError);
+            }
         }
 
         anError = requestContainsNoCacheDirectiveWithFieldName(request);

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestRequestProtocolCompliance.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestRequestProtocolCompliance.java?rev=1519005&r1=1519004&r2=1519005&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestRequestProtocolCompliance.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestRequestProtocolCompliance.java Fri Aug 30 16:01:37 2013
@@ -30,10 +30,13 @@ import static org.junit.Assert.assertEqu
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import java.util.Arrays;
+
 import org.apache.http.HttpEntityEnclosingRequest;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpVersion;
 import org.apache.http.ProtocolVersion;
+import org.apache.http.client.methods.HttpPut;
 import org.apache.http.client.methods.HttpRequestWrapper;
 import org.apache.http.message.BasicHttpEntityEnclosingRequest;
 import org.apache.http.message.BasicHttpRequest;
@@ -48,7 +51,35 @@ public class TestRequestProtocolComplian
     @Before
     public void setUp() {
         req = HttpTestUtils.makeDefaultRequest();
-        impl = new RequestProtocolCompliance();
+        impl = new RequestProtocolCompliance(false);
+    }
+
+    @Test
+    public void testRequestWithWeakETagAndRange() throws Exception {
+        req.setHeader("Range", "bytes=0-499");
+        req.setHeader("If-Range", "W/\"weak\"");
+        assertEquals(1, impl.requestIsFatallyNonCompliant(req).size());
+    }
+
+    @Test
+    public void testRequestWithWeekETagForPUTOrDELETEIfMatch() throws Exception {
+        req = new HttpPut("http://example.com/");
+        req.setHeader("If-Match", "W/\"weak\"");
+        assertEquals(1, impl.requestIsFatallyNonCompliant(req).size());
+    }
+
+    @Test
+    public void testRequestWithWeekETagForPUTOrDELETEIfMatchAllowed() throws Exception {
+        req = new HttpPut("http://example.com/");
+        req.setHeader("If-Match", "W/\"weak\"");
+        impl = new RequestProtocolCompliance(true);
+        assertEquals(Arrays.asList(), impl.requestIsFatallyNonCompliant(req));
+    }
+
+    @Test
+    public void testRequestContainsNoCacheDirectiveWithFieldName() throws Exception {
+        req.setHeader("Cache-Control", "no-cache=false");
+        assertEquals(1, impl.requestIsFatallyNonCompliant(req).size());
     }
 
     @Test