HttpHeaderParser { public static Cache.Entry parseCacheHeaders(NetworkResponse response) { long now = System.currentTimeMillis(); Headers headers = response.headers; long serverDate = 0; long lastModified = 0; long serverExpires = 0; long softExpire = 0; long finalExpire = 0; long maxAge = 0; long staleWhileRevalidate = 0; boolean hasCacheControl = false; boolean mustRevalidate = false; String serverEtag = null; String headerValue; List<String> headerValues; headerValue = headers.get("Date"); if (headerValue != null) { serverDate = parseDateAsEpoch(headerValue); } headerValues = headers.values("Cache-Control"); if (headerValues != null && headerValues.size() > 0) { hasCacheControl = true; for (int i = 0; i < headerValues.size(); i++) { String[] tokens = headerValues.get(i).trim().split(","); for (int j = 0; j < tokens.length; j++) { String token = tokens[j].trim(); if (token.equals("no-cache") || token.equals("no-store")) { return null; } else if (token.startsWith("max-age=")) { try { maxAge = Long.parseLong(token.substring(8)); } catch (Exception e) { } } else if (token.startsWith("stale-while-revalidate=")) { try { staleWhileRevalidate = Long.parseLong(token.substring(23)); } catch (Exception e) { } } else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) { mustRevalidate = true; } } } } headerValue = headers.get("Expires"); if (headerValue != null) { serverExpires = parseDateAsEpoch(headerValue); } headerValue = headers.get("Last-Modified"); if (headerValue != null) { lastModified = parseDateAsEpoch(headerValue); } serverEtag = headers.get("ETag"); if (hasCacheControl) { softExpire = now + maxAge * 1000; finalExpire = mustRevalidate ? softExpire : softExpire + staleWhileRevalidate * 1000; } else if (serverDate > 0 && serverExpires >= serverDate) { softExpire = now + (serverExpires - serverDate); finalExpire = softExpire; } Cache.Entry entry = new Cache.Entry(); entry.data = response.data; entry.etag = serverEtag; entry.softTtl = softExpire; entry.ttl = finalExpire; entry.serverDate = serverDate; entry.lastModified = lastModified; entry.responseHeaders = headers; return entry; }  static Cache.Entry parseCacheHeaders(NetworkResponse response); static long parseDateAsEpoch(String dateStr); static String parseCharset(Map<String, String> headers, String defaultCharset); static String parseCharset(Map<String, String> headers);  }
@Test public void parseCacheHeaders_noHeaders() { NetworkResponse response = new NetworkResponse(0, new byte[0], Headers.of(headers), 0); Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); assertNotNull(entry); assertNull(entry.etag); assertEquals(0, entry.serverDate); assertEquals(0, entry.lastModified); assertEquals(0, entry.ttl); assertEquals(0, entry.softTtl); }
@Test public void parseCacheHeaders_headersSet() { headers.put("MyCustomHeader", "42"); NetworkResponse response = new NetworkResponse(0, new byte[0], Headers.of(headers), 0); Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); assertNotNull(entry); assertNotNull(entry.responseHeaders); assertEquals(1, entry.responseHeaders.size()); assertEquals("42", entry.responseHeaders.get("MyCustomHeader")); }
@Test public void parseCacheHeaders_etag() { headers.put("ETag", "Yow!"); NetworkResponse response = new NetworkResponse(0, new byte[0], Headers.of(headers), 0); Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); assertNotNull(entry); assertEquals("Yow!", entry.etag); }
@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)); NetworkResponse response = new NetworkResponse(0, new byte[0], Headers.of(headers), 0); Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); 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); }
@Test public void parseCacheHeaders_expiresInPast() { long now = System.currentTimeMillis(); headers.put("Date", rfc1123Date(now)); headers.put("Expires", rfc1123Date(now - ONE_HOUR_MILLIS)); NetworkResponse response = new NetworkResponse(0, new byte[0], Headers.of(headers), 0); Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); assertNotNull(entry); assertNull(entry.etag); assertEqualsWithin(entry.serverDate, now, ONE_MINUTE_MILLIS); assertEquals(0, entry.ttl); assertEquals(0, entry.softTtl); }
@Test public void parseCacheHeaders_serverRelative() { long now = System.currentTimeMillis(); headers.put("Date", rfc1123Date(now + ONE_HOUR_MILLIS)); headers.put("Expires", rfc1123Date(now + 4 * ONE_HOUR_MILLIS)); NetworkResponse response = new NetworkResponse(0, new byte[0], Headers.of(headers), 0); Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); assertEqualsWithin(now + 3 * ONE_HOUR_MILLIS, entry.ttl, ONE_MINUTE_MILLIS); assertEquals(entry.softTtl, entry.ttl); }
@Test public void parseCacheHeaders_cacheControlOverridesExpires() { long now = System.currentTimeMillis(); headers.put("Date", rfc1123Date(now)); headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS)); headers.put("Cache-Control", "public, max-age=86400"); NetworkResponse response = new NetworkResponse(0, null, Headers.of(headers), 0); Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); assertNotNull(entry); assertNull(entry.etag); assertEqualsWithin(now + ONE_DAY_MILLIS, entry.ttl, ONE_MINUTE_MILLIS); assertEquals(entry.softTtl, entry.ttl); }
@Test public void testParseCacheHeaders_staleWhileRevalidate() { long now = System.currentTimeMillis(); headers.put("Date", rfc1123Date(now)); headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS)); headers.put("Cache-Control", "max-age=86400, stale-while-revalidate=604800"); NetworkResponse response = new NetworkResponse(0, new byte[0], Headers.of(headers), 0); Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); assertNotNull(entry); assertNull(entry.etag); 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() { long now = System.currentTimeMillis(); headers.put("Date", rfc1123Date(now)); headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS)); headers.put("Cache-Control", "no-cache"); NetworkResponse response = new NetworkResponse(0, new byte[0], Headers.of(headers), 0); Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); assertNull(entry); }
@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"); NetworkResponse response = new NetworkResponse(0, new byte[0], Headers.of(headers), 0); 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"); NetworkResponse response = new NetworkResponse(200, new byte[0], Headers.of(headers), 0); 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)); headers.put("Cache-Control", "must-revalidate, max-age=86400, stale-while-revalidate=604800"); NetworkResponse response = new NetworkResponse(0, null, Headers.of(headers), 0); 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); }