CacheControl berücksichtigt die Regeln bzgl. starken vs. schwachen ETag's
authorKai Moritz <kai@coolibri.de>
Mon, 14 Nov 2011 00:29:02 +0000 (01:29 +0100)
committerKai Moritz <kai@coolibri.de>
Sat, 28 Jan 2012 12:04:08 +0000 (13:04 +0100)
cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/CacheControl.java

index 0d79436..77e2004 100644 (file)
@@ -31,6 +31,7 @@ public class CacheControl {
   public static final String HEADER_PRAGMA = "Pragma";
   public static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since";
   public static final String HEADER_IF_NONE_MATCH = "If-None-Match";
+  public static final String HEADER_RANGE = "Range";
 
   private static final ThreadLocal<CacheMethodHandle> tl = new ThreadLocal<CacheMethodHandle>();
 
@@ -162,8 +163,15 @@ public class CacheControl {
      * 2616, Abschnitt 10.3.5} einen ETag-Header enthalten, wenn auch die
      * 200-Antwort einen enthalten hätte.
      */
-    if (eTag != null)
-      response.setHeader(HEADER_ETAG, eTag);
+    if (eTag != null) {
+      StringBuilder builder = new StringBuilder();
+      if (controller.isETagWeak())
+        builder.append("W/");
+      builder.append('"');
+      builder.append(eTag);
+      builder.append('"');
+      response.setHeader(HEADER_ETAG, builder.toString());
+    }
 
 
     if (ifModifiedSince >= lastModified && lastModified > 0) {
@@ -186,10 +194,31 @@ public class CacheControl {
       }
     }
 
-    if (ifNoneMatch != null && ifNoneMatch.equals(eTag)) {
-      log.debug("{}: ETag {} not changed -> 304 ", url, ifNoneMatch);
-      response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
-      return false;
+    if (ifNoneMatch != null) {
+      boolean weak = false;
+      if (ifNoneMatch.startsWith("W/")) {
+        weak = true;
+        ifNoneMatch = ifNoneMatch.substring(3, ifNoneMatch.length() - 1);
+      }
+      else {
+        ifNoneMatch = ifNoneMatch.substring(1, ifNoneMatch.length() - 1);
+      }
+
+      if (!weak || (request.getMethod().equals("GET") && request.getHeader(HEADER_RANGE) == null)) {
+        /**
+         * Die Gleichheit gilt nur, wenn die ETag's der Anfrage _und_ der
+         * Antwort stark sind (starke Gleichheit!), oder wenn die Antwort nur
+         * schwache Gleichheit fordert...
+         */
+        if (ifNoneMatch.equals(eTag) && (controller.isETagWeak() || !weak)) {
+          log.debug("{}: ETag {} not changed -> 304 ", url, ifNoneMatch);
+          response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+          return false;
+        }
+      }
+      else {
+        log.warn("{}: ignoring weak ETag W/\"{}\", because the request was no GET-request or the Range-Header was present!", url, ifNoneMatch);
+      }
     }
 
 
@@ -273,6 +302,7 @@ public class CacheControl {
     int getCacheSeconds(HttpServletRequest request) throws Exception;
     long getLastModified(HttpServletRequest request) throws Exception;
     String getETag(HttpServletRequest request) throws Exception;
+    boolean isETagWeak();
     void cacheControl(HttpServletRequest request, Map<String, String> cacheControlMap) throws Exception;
   }
 
@@ -294,6 +324,7 @@ public class CacheControl {
     private boolean isLastModifiedMethodDefined;
     private boolean isETagMethodDefined;
     private boolean isCacheControlMethodDefined;
+    private boolean weak;
 
 
     ReflectionCacheMethodHandle(Object handler) throws NoSuchMethodException {
@@ -302,7 +333,7 @@ public class CacheControl {
 
       cacheSeconds = CacheControl.this.defaultCacheSeconds;
       lastModified = CacheControl.this.defaultLastModified;
-      eTag = null;
+      eTag = "";
 
       /** Class-Level-Annotations auslesen */
       for (Annotation annotation : handler.getClass().getAnnotations()) {
@@ -325,7 +356,9 @@ public class CacheControl {
           continue;
         }
         if (annotation.annotationType().equals(ETag.class)) {
-          eTag = ((ETag)annotation).value();
+          ETag eTagAnnotation = (ETag)annotation;
+          eTag = eTagAnnotation.value();
+          weak = eTagAnnotation.weak();
           isETagMethodDefined = true;
           continue;
         }
@@ -359,6 +392,7 @@ public class CacheControl {
             if (isETagMethodDefined)
               throw new IllegalArgumentException("Die Annotation @ETag wurde in der Klasse " + handler.getClass().getSimpleName() + " mehrfach verwendet!");
             eTagMethod = method;
+            weak = ((ETag)annotation).weak();
             isETagMethodDefined = true;
             continue;
           }
@@ -427,6 +461,11 @@ public class CacheControl {
         return (String)eTagMethod.invoke(handler, request);
     }
 
+    @Override
+    public boolean isETagWeak() {
+      return weak;
+    }
+
     @Override
     public void cacheControl(
         HttpServletRequest request,