am 9ac6e90f: Merge "Modify header parser to handle must-revalidate."

* commit '9ac6e90f2b36e76e4860cae7e6ab4458b9a49189':
  Modify header parser to handle must-revalidate.
diff --git a/local.properties b/local.properties
new file mode 100644
index 0000000..4de71c4
--- /dev/null
+++ b/local.properties
@@ -0,0 +1,11 @@
+## This file is automatically generated by Android Studio.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+#
+# Location of the SDK. This is only used by Gradle.
+# For customization when using a Version Control System, please read the
+# header note.
+#Wed Jan 28 16:21:52 PST 2015
+sdk.dir=/usr/local/google/clients/android/gcore-orla/prebuilts/fullsdk/linux
diff --git a/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java b/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java
index 7306052..c3b48d8 100644
--- a/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java
+++ b/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java
@@ -49,6 +49,7 @@
         long maxAge = 0;
         long staleWhileRevalidate = 0;
         boolean hasCacheControl = false;
+        boolean mustRevalidate = false;
 
         String serverEtag = null;
         String headerValue;
@@ -77,7 +78,7 @@
                     } catch (Exception e) {
                     }
                 } else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) {
-                    maxAge = 0;
+                    mustRevalidate = true;
                 }
             }
         }
@@ -98,7 +99,9 @@
         // is more restrictive.
         if (hasCacheControl) {
             softExpire = now + maxAge * 1000;
-            finalExpire = softExpire + staleWhileRevalidate * 1000;
+            finalExpire = mustRevalidate
+                    ? softExpire
+                    : softExpire + staleWhileRevalidate * 1000;
         } else if (serverDate > 0 && serverExpires >= serverDate) {
             // Default semantic for Expire header in HTTP specification is softExpire.
             softExpire = now + (serverExpires - serverDate);
diff --git a/src/test/java/com/android/volley/toolbox/HttpHeaderParserTest.java b/src/test/java/com/android/volley/toolbox/HttpHeaderParserTest.java
index f9230c6..fd8cf51 100644
--- a/src/test/java/com/android/volley/toolbox/HttpHeaderParserTest.java
+++ b/src/test/java/com/android/volley/toolbox/HttpHeaderParserTest.java
@@ -41,6 +41,7 @@
     private static long ONE_MINUTE_MILLIS = 1000L * 60;
     private static long ONE_HOUR_MILLIS = 1000L * 60 * 60;
     private static long ONE_DAY_MILLIS = ONE_HOUR_MILLIS * 24;
+    private static long ONE_WEEK_MILLIS = ONE_DAY_MILLIS * 7;
 
     private NetworkResponse response;
     private Map<String, String> headers;
@@ -135,7 +136,7 @@
 
         assertNotNull(entry);
         assertNull(entry.etag);
-        assertEqualsWithin(now + 24 * ONE_HOUR_MILLIS, entry.ttl, ONE_MINUTE_MILLIS);
+        assertEqualsWithin(now + ONE_DAY_MILLIS, entry.ttl, ONE_MINUTE_MILLIS);
         assertEquals(entry.softTtl, entry.ttl);
     }
 
@@ -153,8 +154,8 @@
 
         assertNotNull(entry);
         assertNull(entry.etag);
-        assertEqualsWithin(now + 24 * ONE_HOUR_MILLIS, entry.softTtl, ONE_MINUTE_MILLIS);
-        assertEqualsWithin(now + 8 * 24 * ONE_HOUR_MILLIS, entry.ttl, ONE_MINUTE_MILLIS);
+        assertEqualsWithin(now + ONE_DAY_MILLIS, entry.softTtl, ONE_MINUTE_MILLIS);
+        assertEqualsWithin(now + ONE_DAY_MILLIS + ONE_WEEK_MILLIS, entry.ttl, ONE_MINUTE_MILLIS);
     }
 
     @Test public void parseCacheHeaders_cacheControlNoCache() {
@@ -168,20 +169,51 @@
         assertNull(entry);
     }
 
-    @Test public void parseCacheHeaders_cacheControlMustRevalidate() {
+    @Test public void parseCacheHeaders_cacheControlMustRevalidateNoMaxAge() {
         long now = System.currentTimeMillis();
         headers.put("Date", rfc1123Date(now));
         headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS));
         headers.put("Cache-Control", "must-revalidate");
 
         Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
-
         assertNotNull(entry);
         assertNull(entry.etag);
         assertEqualsWithin(now, entry.ttl, ONE_MINUTE_MILLIS);
         assertEquals(entry.softTtl, entry.ttl);
     }
 
+    @Test public void parseCacheHeaders_cacheControlMustRevalidateWithMaxAge() {
+        long now = System.currentTimeMillis();
+        headers.put("Date", rfc1123Date(now));
+        headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS));
+        headers.put("Cache-Control", "must-revalidate, max-age=3600");
+
+        Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
+        assertNotNull(entry);
+        assertNull(entry.etag);
+        assertEqualsWithin(now + ONE_HOUR_MILLIS, entry.ttl, ONE_MINUTE_MILLIS);
+        assertEquals(entry.softTtl, entry.ttl);
+    }
+
+    @Test public void parseCacheHeaders_cacheControlMustRevalidateWithMaxAgeAndStale() {
+        long now = System.currentTimeMillis();
+        headers.put("Date", rfc1123Date(now));
+        headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS));
+
+        // - max-age (entry.softTtl) indicates that the asset is fresh for 1 day
+        // - stale-while-revalidate (entry.ttl) indicates that the asset may
+        // continue to be served stale for up to additional 7 days, but this is
+        // ignored in this case because of the must-revalidate header.
+        headers.put("Cache-Control",
+                "must-revalidate, max-age=86400, stale-while-revalidate=604800");
+
+        Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
+        assertNotNull(entry);
+        assertNull(entry.etag);
+        assertEqualsWithin(now + ONE_DAY_MILLIS, entry.softTtl, ONE_MINUTE_MILLIS);
+        assertEquals(entry.softTtl, entry.ttl);
+    }
+
     private void assertEqualsWithin(long expected, long value, long fudgeFactor) {
         long diff = Math.abs(expected - value);
         assertTrue(diff < fudgeFactor);
@@ -253,7 +285,7 @@
 
         assertNotNull(entry);
         assertEquals("Yow!", entry.etag);
-        assertEqualsWithin(now + 24 * ONE_HOUR_MILLIS, entry.ttl, ONE_MINUTE_MILLIS);
+        assertEqualsWithin(now + ONE_DAY_MILLIS, entry.ttl, ONE_MINUTE_MILLIS);
         assertEquals(entry.softTtl, entry.ttl);
         assertEquals("ISO-8859-1", HttpHeaderParser.parseCharset(headers));
     }