am 0cc8d620: Merge "Uses the "Last-Modified" header for "If-Modified-Since""

* commit '0cc8d620fe12412f90ba5121bdffb1a4e2f032c2':
  Uses the "Last-Modified" header for "If-Modified-Since"
diff --git a/src/main/java/com/android/volley/Cache.java b/src/main/java/com/android/volley/Cache.java
index eafd118..f1ec757 100644
--- a/src/main/java/com/android/volley/Cache.java
+++ b/src/main/java/com/android/volley/Cache.java
@@ -74,6 +74,9 @@
         /** Date of this response as reported by the server. */
         public long serverDate;
 
+        /** The last modified date for the requested object. */
+        public long lastModified;
+
         /** TTL for this record. */
         public long ttl;
 
diff --git a/src/main/java/com/android/volley/toolbox/BasicNetwork.java b/src/main/java/com/android/volley/toolbox/BasicNetwork.java
index bc1cfdb..4b1603b 100644
--- a/src/main/java/com/android/volley/toolbox/BasicNetwork.java
+++ b/src/main/java/com/android/volley/toolbox/BasicNetwork.java
@@ -212,8 +212,8 @@
             headers.put("If-None-Match", entry.etag);
         }
 
-        if (entry.serverDate > 0) {
-            Date refTime = new Date(entry.serverDate);
+        if (entry.lastModified > 0) {
+            Date refTime = new Date(entry.lastModified);
             headers.put("If-Modified-Since", DateUtils.formatDate(refTime));
         }
     }
diff --git a/src/main/java/com/android/volley/toolbox/DiskBasedCache.java b/src/main/java/com/android/volley/toolbox/DiskBasedCache.java
index b283788..036b55a 100644
--- a/src/main/java/com/android/volley/toolbox/DiskBasedCache.java
+++ b/src/main/java/com/android/volley/toolbox/DiskBasedCache.java
@@ -349,6 +349,9 @@
         /** Date of this response as reported by the server. */
         public long serverDate;
 
+        /** The last modified date for the requested object. */
+        public long lastModified;
+
         /** TTL for this record. */
         public long ttl;
 
@@ -370,6 +373,7 @@
             this.size = entry.data.length;
             this.etag = entry.etag;
             this.serverDate = entry.serverDate;
+            this.lastModified = entry.lastModified;
             this.ttl = entry.ttl;
             this.softTtl = entry.softTtl;
             this.responseHeaders = entry.responseHeaders;
@@ -396,6 +400,13 @@
             entry.ttl = readLong(is);
             entry.softTtl = readLong(is);
             entry.responseHeaders = readStringStringMap(is);
+
+            try {
+                entry.lastModified = readLong(is);
+            } catch (EOFException e) {
+                // the old cache entry format doesn't know lastModified
+            }
+
             return entry;
         }
 
@@ -407,6 +418,7 @@
             e.data = data;
             e.etag = etag;
             e.serverDate = serverDate;
+            e.lastModified = lastModified;
             e.ttl = ttl;
             e.softTtl = softTtl;
             e.responseHeaders = responseHeaders;
@@ -426,6 +438,7 @@
                 writeLong(os, ttl);
                 writeLong(os, softTtl);
                 writeStringStringMap(responseHeaders, os);
+                writeLong(os, lastModified);
                 os.flush();
                 return true;
             } catch (IOException e) {
diff --git a/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java b/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java
index cb08432..e342c9e 100644
--- a/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java
+++ b/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java
@@ -42,6 +42,7 @@
         Map<String, String> headers = response.headers;
 
         long serverDate = 0;
+        long lastModified = 0;
         long serverExpires = 0;
         long softExpire = 0;
         long maxAge = 0;
@@ -79,6 +80,11 @@
             serverExpires = parseDateAsEpoch(headerValue);
         }
 
+        headerValue = headers.get("Last-Modified");
+        if (headerValue != null) {
+            lastModified = parseDateAsEpoch(headerValue);
+        }
+
         serverEtag = headers.get("ETag");
 
         // Cache-Control takes precedence over an Expires header, even if both exist and Expires
@@ -96,6 +102,7 @@
         entry.softTtl = softExpire;
         entry.ttl = entry.softTtl;
         entry.serverDate = serverDate;
+        entry.lastModified = lastModified;
         entry.responseHeaders = headers;
 
         return entry;
diff --git a/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java b/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java
index 4b2955d..d9d49e9 100644
--- a/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java
+++ b/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java
@@ -34,6 +34,7 @@
         Cache.Entry e = new Cache.Entry();
         e.data = new byte[8];
         e.serverDate = 1234567L;
+        e.lastModified = 13572468L;
         e.ttl = 9876543L;
         e.softTtl = 8765432L;
         e.etag = "etag";
@@ -48,6 +49,7 @@
 
         assertEquals(first.key, second.key);
         assertEquals(first.serverDate, second.serverDate);
+        assertEquals(first.lastModified, second.lastModified);
         assertEquals(first.ttl, second.ttl);
         assertEquals(first.softTtl, second.softTtl);
         assertEquals(first.etag, second.etag);
@@ -121,4 +123,43 @@
         assertEquals(DiskBasedCache.readStringStringMap(bais), emptyKey);
         assertEquals(DiskBasedCache.readStringStringMap(bais), emptyValue);
     }
+
+    // Test deserializing the old format into the new one.
+    public void testCacheHeaderSerializationOldToNewFormat() throws Exception {
+
+        final int CACHE_MAGIC = 0x20140623;
+        final String key = "key";
+        final String etag = "etag";
+        final long serverDate = 1234567890l;
+        final long ttl = 1357924680l;
+        final long softTtl = 2468013579l;
+
+        Map<String, String> responseHeaders = new HashMap<String, String>();
+        responseHeaders.put("first", "thing");
+        responseHeaders.put("second", "item");
+
+        // write old sytle header (without lastModified)
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        DiskBasedCache.writeInt(baos, CACHE_MAGIC);
+        DiskBasedCache.writeString(baos, key);
+        DiskBasedCache.writeString(baos, etag == null ? "" : etag);
+        DiskBasedCache.writeLong(baos, serverDate);
+        DiskBasedCache.writeLong(baos, ttl);
+        DiskBasedCache.writeLong(baos, softTtl);
+        DiskBasedCache.writeStringStringMap(responseHeaders, baos);
+
+        // read / test new style header (with lastModified)
+        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+        CacheHeader cacheHeader = CacheHeader.readHeader(bais);
+
+        assertEquals(cacheHeader.key, key);
+        assertEquals(cacheHeader.etag, etag);
+        assertEquals(cacheHeader.serverDate, serverDate);
+        assertEquals(cacheHeader.ttl, ttl);
+        assertEquals(cacheHeader.softTtl, softTtl);
+        assertEquals(cacheHeader.responseHeaders, responseHeaders);
+
+        // the old format doesn't know lastModified
+        assertEquals(cacheHeader.lastModified, 0);
+    }
 }
diff --git a/src/test/java/com/android/volley/toolbox/HttpHeaderParserTest.java b/src/test/java/com/android/volley/toolbox/HttpHeaderParserTest.java
index b8c4847..01ff2c2 100644
--- a/src/test/java/com/android/volley/toolbox/HttpHeaderParserTest.java
+++ b/src/test/java/com/android/volley/toolbox/HttpHeaderParserTest.java
@@ -40,6 +40,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 NetworkResponse response;
     private Map<String, String> headers;
@@ -55,6 +56,7 @@
         assertNotNull(entry);
         assertNull(entry.etag);
         assertEquals(0, entry.serverDate);
+        assertEquals(0, entry.lastModified);
         assertEquals(0, entry.ttl);
         assertEquals(0, entry.softTtl);
     }
@@ -82,6 +84,7 @@
     @Test public void parseCacheHeaders_normalExpire() {
         long now = System.currentTimeMillis();
         headers.put("Date", rfc1123Date(now));
+        headers.put("Last-Modified", rfc1123Date(now - ONE_DAY_MILLIS));
         headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS));
 
         Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
@@ -89,6 +92,7 @@
         assertNotNull(entry);
         assertNull(entry.etag);
         assertEqualsWithin(entry.serverDate, now, ONE_MINUTE_MILLIS);
+        assertEqualsWithin(entry.lastModified, (now - ONE_DAY_MILLIS), ONE_MINUTE_MILLIS);
         assertTrue(entry.softTtl >= (now + ONE_HOUR_MILLIS));
         assertTrue(entry.ttl == entry.softTtl);
     }
diff --git a/src/test/java/com/android/volley/utils/CacheTestUtils.java b/src/test/java/com/android/volley/utils/CacheTestUtils.java
index cd2b8e7..898d055 100644
--- a/src/test/java/com/android/volley/utils/CacheTestUtils.java
+++ b/src/test/java/com/android/volley/utils/CacheTestUtils.java
@@ -24,7 +24,7 @@
             entry.data = new byte[random.nextInt(1024)];
         }
         entry.etag = String.valueOf(random.nextLong());
-        entry.serverDate = random.nextLong();
+        entry.lastModified = random.nextLong();
         entry.ttl = isExpired ? 0 : Long.MAX_VALUE;
         entry.softTtl = needsRefresh ? 0 : Long.MAX_VALUE;
         return entry;