1d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru/*
2d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Copyright (C) 2011 The Android Open Source Project
3d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru *
4d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Licensed under the Apache License, Version 2.0 (the "License");
5d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * you may not use this file except in compliance with the License.
6d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * You may obtain a copy of the License at
7d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru *
8d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru *      http://www.apache.org/licenses/LICENSE-2.0
9d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru *
10d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Unless required by applicable law or agreed to in writing, software
11d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * distributed under the License is distributed on an "AS IS" BASIS,
12d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * See the License for the specific language governing permissions and
14d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * limitations under the License.
15d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */
16d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
17d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Querupackage com.android.volley.toolbox;
18d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
19d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.Cache;
20d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.NetworkResponse;
21d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
22ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmannimport org.apache.http.Header;
23ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmannimport org.apache.http.message.BasicHeader;
24b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cbFicus Kirkpatrickimport org.junit.Before;
25b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cbFicus Kirkpatrickimport org.junit.Test;
26b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cbFicus Kirkpatrickimport org.junit.runner.RunWith;
27b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cbFicus Kirkpatrickimport org.robolectric.RobolectricTestRunner;
28ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann
29d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.text.DateFormat;
30d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.text.SimpleDateFormat;
31d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.util.Date;
32d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.util.HashMap;
338611e831aebbec7d6eebf02c8180e746c26dc05cRalph Bergmannimport java.util.Locale;
34d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.util.Map;
35d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
36b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cbFicus Kirkpatrickimport static org.junit.Assert.*;
37b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cbFicus Kirkpatrick
38b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cbFicus Kirkpatrick@RunWith(RobolectricTestRunner.class)
39b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cbFicus Kirkpatrickpublic class HttpHeaderParserTest {
40d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
41d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private static long ONE_MINUTE_MILLIS = 1000L * 60;
42d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private static long ONE_HOUR_MILLIS = 1000L * 60 * 60;
439324df1b8046548587ffec89ec755264f6fbb097Ralph Bergmann    private static long ONE_DAY_MILLIS = ONE_HOUR_MILLIS * 24;
44782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro    private static long ONE_WEEK_MILLIS = ONE_DAY_MILLIS * 7;
45d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
46d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private NetworkResponse response;
47d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private Map<String, String> headers;
48d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
49b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cbFicus Kirkpatrick    @Before public void setUp() throws Exception {
50d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers = new HashMap<String, String>();
51d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        response = new NetworkResponse(0, null, headers, false);
52d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
53d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
54b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cbFicus Kirkpatrick    @Test public void parseCacheHeaders_noHeaders() {
55d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
56d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
57d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertNotNull(entry);
58d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertNull(entry.etag);
59d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEquals(0, entry.serverDate);
609324df1b8046548587ffec89ec755264f6fbb097Ralph Bergmann        assertEquals(0, entry.lastModified);
61d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEquals(0, entry.ttl);
62d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEquals(0, entry.softTtl);
63d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
64d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
65b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cbFicus Kirkpatrick    @Test public void parseCacheHeaders_headersSet() {
66e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru        headers.put("MyCustomHeader", "42");
67e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru
68e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru        Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
69e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru
70e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru        assertNotNull(entry);
71e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru        assertNotNull(entry.responseHeaders);
72e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru        assertEquals(1, entry.responseHeaders.size());
73e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru        assertEquals("42", entry.responseHeaders.get("MyCustomHeader"));
74e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru    }
75e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru
76b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cbFicus Kirkpatrick    @Test public void parseCacheHeaders_etag() {
77d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("ETag", "Yow!");
78d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
79d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
80d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
81d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertNotNull(entry);
82d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEquals("Yow!", entry.etag);
83d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
84d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
85b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cbFicus Kirkpatrick    @Test public void parseCacheHeaders_normalExpire() {
86d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        long now = System.currentTimeMillis();
87d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Date", rfc1123Date(now));
889324df1b8046548587ffec89ec755264f6fbb097Ralph Bergmann        headers.put("Last-Modified", rfc1123Date(now - ONE_DAY_MILLIS));
89d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS));
90d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
91d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
92d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
93d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertNotNull(entry);
94d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertNull(entry.etag);
95d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEqualsWithin(entry.serverDate, now, ONE_MINUTE_MILLIS);
969324df1b8046548587ffec89ec755264f6fbb097Ralph Bergmann        assertEqualsWithin(entry.lastModified, (now - ONE_DAY_MILLIS), ONE_MINUTE_MILLIS);
97d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertTrue(entry.softTtl >= (now + ONE_HOUR_MILLIS));
98d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertTrue(entry.ttl == entry.softTtl);
99d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
100d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
101b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cbFicus Kirkpatrick    @Test public void parseCacheHeaders_expiresInPast() {
102d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        long now = System.currentTimeMillis();
103d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Date", rfc1123Date(now));
104d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Expires", rfc1123Date(now - ONE_HOUR_MILLIS));
105d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
106d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
107d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
108d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertNotNull(entry);
109d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertNull(entry.etag);
110d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEqualsWithin(entry.serverDate, now, ONE_MINUTE_MILLIS);
111d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEquals(0, entry.ttl);
112d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEquals(0, entry.softTtl);
113d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
114d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
115b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cbFicus Kirkpatrick    @Test public void parseCacheHeaders_serverRelative() {
116d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
117d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        long now = System.currentTimeMillis();
118d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        // Set "current" date as one hour in the future
119d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Date", rfc1123Date(now + ONE_HOUR_MILLIS));
120d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        // TTL four hours in the future, so should be three hours from now
121d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Expires", rfc1123Date(now + 4 * ONE_HOUR_MILLIS));
122d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
123d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
124d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
125d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEqualsWithin(now + 3 * ONE_HOUR_MILLIS, entry.ttl, ONE_MINUTE_MILLIS);
126d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEquals(entry.softTtl, entry.ttl);
127d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
128d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
129b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cbFicus Kirkpatrick    @Test public void parseCacheHeaders_cacheControlOverridesExpires() {
130d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        long now = System.currentTimeMillis();
131d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Date", rfc1123Date(now));
132d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS));
133d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Cache-Control", "public, max-age=86400");
134d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
135d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
136d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
137d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertNotNull(entry);
138d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertNull(entry.etag);
139782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        assertEqualsWithin(now + ONE_DAY_MILLIS, entry.ttl, ONE_MINUTE_MILLIS);
140d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEquals(entry.softTtl, entry.ttl);
141d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
142d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
1438e33d93dc1aae9bb9dbde3b80af8a76ba28f0e19Ralph Bergmann    @Test public void testParseCacheHeaders_staleWhileRevalidate() {
1448e33d93dc1aae9bb9dbde3b80af8a76ba28f0e19Ralph Bergmann        long now = System.currentTimeMillis();
1458e33d93dc1aae9bb9dbde3b80af8a76ba28f0e19Ralph Bergmann        headers.put("Date", rfc1123Date(now));
1468e33d93dc1aae9bb9dbde3b80af8a76ba28f0e19Ralph Bergmann        headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS));
1478e33d93dc1aae9bb9dbde3b80af8a76ba28f0e19Ralph Bergmann
1488e33d93dc1aae9bb9dbde3b80af8a76ba28f0e19Ralph Bergmann        // - max-age (entry.softTtl) indicates that the asset is fresh for 1 day
1498e33d93dc1aae9bb9dbde3b80af8a76ba28f0e19Ralph Bergmann        // - stale-while-revalidate (entry.ttl) indicates that the asset may
1508e33d93dc1aae9bb9dbde3b80af8a76ba28f0e19Ralph Bergmann        // continue to be served stale for up to additional 7 days
1518e33d93dc1aae9bb9dbde3b80af8a76ba28f0e19Ralph Bergmann        headers.put("Cache-Control", "max-age=86400, stale-while-revalidate=604800");
1528e33d93dc1aae9bb9dbde3b80af8a76ba28f0e19Ralph Bergmann
1538e33d93dc1aae9bb9dbde3b80af8a76ba28f0e19Ralph Bergmann        Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
1548e33d93dc1aae9bb9dbde3b80af8a76ba28f0e19Ralph Bergmann
1558e33d93dc1aae9bb9dbde3b80af8a76ba28f0e19Ralph Bergmann        assertNotNull(entry);
1568e33d93dc1aae9bb9dbde3b80af8a76ba28f0e19Ralph Bergmann        assertNull(entry.etag);
157782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        assertEqualsWithin(now + ONE_DAY_MILLIS, entry.softTtl, ONE_MINUTE_MILLIS);
158782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        assertEqualsWithin(now + ONE_DAY_MILLIS + ONE_WEEK_MILLIS, entry.ttl, ONE_MINUTE_MILLIS);
1598e33d93dc1aae9bb9dbde3b80af8a76ba28f0e19Ralph Bergmann    }
1608e33d93dc1aae9bb9dbde3b80af8a76ba28f0e19Ralph Bergmann
161b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cbFicus Kirkpatrick    @Test public void parseCacheHeaders_cacheControlNoCache() {
162d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        long now = System.currentTimeMillis();
163d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Date", rfc1123Date(now));
164d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS));
165d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Cache-Control", "no-cache");
166d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
167d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
168d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
169d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertNull(entry);
170d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
171d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
172782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro    @Test public void parseCacheHeaders_cacheControlMustRevalidateNoMaxAge() {
173d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        long now = System.currentTimeMillis();
174d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Date", rfc1123Date(now));
175d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS));
176d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Cache-Control", "must-revalidate");
177d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
178d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
179d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertNotNull(entry);
180d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertNull(entry.etag);
181d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEqualsWithin(now, entry.ttl, ONE_MINUTE_MILLIS);
182d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEquals(entry.softTtl, entry.ttl);
183d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
184d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
185782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro    @Test public void parseCacheHeaders_cacheControlMustRevalidateWithMaxAge() {
186782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        long now = System.currentTimeMillis();
187782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        headers.put("Date", rfc1123Date(now));
188782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS));
189782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        headers.put("Cache-Control", "must-revalidate, max-age=3600");
190782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro
191782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
192782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        assertNotNull(entry);
193782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        assertNull(entry.etag);
194782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        assertEqualsWithin(now + ONE_HOUR_MILLIS, entry.ttl, ONE_MINUTE_MILLIS);
195782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        assertEquals(entry.softTtl, entry.ttl);
196782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro    }
197782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro
198782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro    @Test public void parseCacheHeaders_cacheControlMustRevalidateWithMaxAgeAndStale() {
199782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        long now = System.currentTimeMillis();
200782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        headers.put("Date", rfc1123Date(now));
201782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS));
202782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro
203782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        // - max-age (entry.softTtl) indicates that the asset is fresh for 1 day
204782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        // - stale-while-revalidate (entry.ttl) indicates that the asset may
205782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        // continue to be served stale for up to additional 7 days, but this is
206782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        // ignored in this case because of the must-revalidate header.
207782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        headers.put("Cache-Control",
208782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro                "must-revalidate, max-age=86400, stale-while-revalidate=604800");
209782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro
210782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
211782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        assertNotNull(entry);
212782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        assertNull(entry.etag);
213782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        assertEqualsWithin(now + ONE_DAY_MILLIS, entry.softTtl, ONE_MINUTE_MILLIS);
214782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        assertEquals(entry.softTtl, entry.ttl);
215782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro    }
216782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro
217d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private void assertEqualsWithin(long expected, long value, long fudgeFactor) {
218d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        long diff = Math.abs(expected - value);
219d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertTrue(diff < fudgeFactor);
220d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
221d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
222d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    private static String rfc1123Date(long millis) {
2238611e831aebbec7d6eebf02c8180e746c26dc05cRalph Bergmann        DateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH);
224d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        return df.format(new Date(millis));
225d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
226d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
227d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    // --------------------------
228d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
229b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cbFicus Kirkpatrick    @Test public void parseCharset() {
230d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        // Like the ones we usually see
231d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Content-Type", "text/plain; charset=utf-8");
232d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEquals("utf-8", HttpHeaderParser.parseCharset(headers));
233d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
2346bafd7d28fc7947f263feb7134fc8a70084357c3Zdeněk Kořán        // Charset specified, ignore default charset
2356bafd7d28fc7947f263feb7134fc8a70084357c3Zdeněk Kořán        headers.put("Content-Type", "text/plain; charset=utf-8");
2366bafd7d28fc7947f263feb7134fc8a70084357c3Zdeněk Kořán        assertEquals("utf-8", HttpHeaderParser.parseCharset(headers, "ISO-8859-1"));
2376bafd7d28fc7947f263feb7134fc8a70084357c3Zdeněk Kořán
238d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        // Extra whitespace
239d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Content-Type", "text/plain;    charset=utf-8 ");
240d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEquals("utf-8", HttpHeaderParser.parseCharset(headers));
241d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
242d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        // Extra parameters
243d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Content-Type", "text/plain; charset=utf-8; frozzle=bar");
244d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEquals("utf-8", HttpHeaderParser.parseCharset(headers));
245d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
246d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        // No Content-Type header
247d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.clear();
248d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEquals("ISO-8859-1", HttpHeaderParser.parseCharset(headers));
249d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
2506bafd7d28fc7947f263feb7134fc8a70084357c3Zdeněk Kořán        // No Content-Type header, use default charset
2516bafd7d28fc7947f263feb7134fc8a70084357c3Zdeněk Kořán        headers.clear();
2526bafd7d28fc7947f263feb7134fc8a70084357c3Zdeněk Kořán        assertEquals("utf-8", HttpHeaderParser.parseCharset(headers, "utf-8"));
2536bafd7d28fc7947f263feb7134fc8a70084357c3Zdeněk Kořán
254d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        // Empty value
255d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Content-Type", "text/plain; charset=");
256d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEquals("ISO-8859-1", HttpHeaderParser.parseCharset(headers));
257d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
258d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        // None specified
259d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Content-Type", "text/plain");
260d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEquals("ISO-8859-1", HttpHeaderParser.parseCharset(headers));
261d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru
2626bafd7d28fc7947f263feb7134fc8a70084357c3Zdeněk Kořán        // None charset specified, use default charset
2636bafd7d28fc7947f263feb7134fc8a70084357c3Zdeněk Kořán        headers.put("Content-Type", "application/json");
2646bafd7d28fc7947f263feb7134fc8a70084357c3Zdeněk Kořán        assertEquals("utf-8", HttpHeaderParser.parseCharset(headers, "utf-8"));
2656bafd7d28fc7947f263feb7134fc8a70084357c3Zdeněk Kořán
266d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        // None specified, extra semicolon
267d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        headers.put("Content-Type", "text/plain;");
268d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru        assertEquals("ISO-8859-1", HttpHeaderParser.parseCharset(headers));
269d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru    }
270ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann
271b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cbFicus Kirkpatrick    @Test public void parseCaseInsensitive() {
272ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann
273ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann        long now = System.currentTimeMillis();
274ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann
275ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann        Header[] headersArray = new Header[5];
276ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann        headersArray[0] = new BasicHeader("eTAG", "Yow!");
277ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann        headersArray[1] = new BasicHeader("DATE", rfc1123Date(now));
278ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann        headersArray[2] = new BasicHeader("expires", rfc1123Date(now + ONE_HOUR_MILLIS));
279ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann        headersArray[3] = new BasicHeader("cache-control", "public, max-age=86400");
280ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann        headersArray[4] = new BasicHeader("content-type", "text/plain");
281ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann
282ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann        Map<String, String> headers = BasicNetwork.convertHeaders(headersArray);
283ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann        NetworkResponse response = new NetworkResponse(0, null, headers, false);
284ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann        Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response);
285ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann
286ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann        assertNotNull(entry);
287ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann        assertEquals("Yow!", entry.etag);
288782b52fd6f5c62f5b38148432acbc4e79351fd0bDave Santoro        assertEqualsWithin(now + ONE_DAY_MILLIS, entry.ttl, ONE_MINUTE_MILLIS);
289ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann        assertEquals(entry.softTtl, entry.ttl);
290ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann        assertEquals("ISO-8859-1", HttpHeaderParser.parseCharset(headers));
291ba53551261044a9811835c1fb89d4ec48a88a43bRalph Bergmann    }
292d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru}
293