HttpResponseCacheTest.java revision 757afaa7afe96791a3cc612c9e3c4597a7321c7e
10c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson/*
20c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * Copyright (C) 2011 The Android Open Source Project
30c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson *
40c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License");
50c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * you may not use this file except in compliance with the License.
60c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * You may obtain a copy of the License at
70c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson *
80c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson *      http://www.apache.org/licenses/LICENSE-2.0
90c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson *
100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * Unless required by applicable law or agreed to in writing, software
110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * See the License for the specific language governing permissions and
140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * limitations under the License.
150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson */
160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
17757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilsonpackage libcore.net.http;
180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.BufferedReader;
200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.ByteArrayOutputStream;
21433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilsonimport java.io.File;
22c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilsonimport java.io.FileNotFoundException;
230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.IOException;
240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.InputStream;
250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.InputStreamReader;
260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.OutputStream;
270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.CacheRequest;
280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.CacheResponse;
290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.HttpURLConnection;
300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.ResponseCache;
310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.SecureCacheResponse;
320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.URI;
330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.URISyntaxException;
340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.URL;
350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.URLConnection;
360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.security.Principal;
370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.security.cert.Certificate;
38953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.text.DateFormat;
39953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.text.SimpleDateFormat;
400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.ArrayList;
410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.Arrays;
420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.Collections;
430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.Date;
440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.Iterator;
450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.List;
46953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.util.Locale;
470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.Map;
48953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.util.TimeZone;
49433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilsonimport java.util.UUID;
500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.concurrent.TimeUnit;
510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.concurrent.atomic.AtomicInteger;
520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.concurrent.atomic.AtomicReference;
530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.zip.GZIPOutputStream;
540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport javax.net.ssl.HttpsURLConnection;
550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport junit.framework.TestCase;
560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport libcore.javax.net.ssl.TestSSLContext;
570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport tests.http.MockResponse;
580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport tests.http.MockWebServer;
590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport tests.http.RecordedRequest;
600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport static tests.http.SocketPolicy.DISCONNECT_AT_END;
610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonpublic final class HttpResponseCacheTest extends TestCase {
630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private MockWebServer server = new MockWebServer();
64433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson    private HttpResponseCache cache;
650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    @Override protected void setUp() throws Exception {
670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        super.setUp();
68433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson
69433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        String tmp = System.getProperty("java.io.tmpdir");
70433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        File cacheDir = new File(tmp, "HttpCache-" + UUID.randomUUID());
71433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        cacheDir.mkdir();
72433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        cache = new HttpResponseCache(cacheDir, Integer.MAX_VALUE);
730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        ResponseCache.setDefault(cache);
740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    @Override protected void tearDown() throws Exception {
770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.shutdown();
78433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        ResponseCache.setDefault(null);
79433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        cache.delete();
800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        super.tearDown();
810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Test that response caching is consistent with the RI and the spec.
850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4
860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
8784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testResponseCachingByResponseCode() throws Exception {
880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // Test each documented HTTP/1.1 code, plus the first unused value in each range.
890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // We can't test 100 because it's not really a response.
920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // assertCached(false, 100);
930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 101);
940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 102);
950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(true,  200);
960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 201);
970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 202);
980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(true,  203);
990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 204);
1000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 205);
1010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(true,  206);
1020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 207);
10384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertCached(true,  300);
1040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(true,  301);
1050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        for (int i = 302; i <= 308; ++i) {
1060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertCached(false, i);
1070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        for (int i = 400; i <= 406; ++i) {
1090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertCached(false, i);
1100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // (See test_responseCaching_407.)
1120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 408);
1130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 409);
1140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // (See test_responseCaching_410.)
1150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        for (int i = 411; i <= 418; ++i) {
1160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertCached(false, i);
1170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        for (int i = 500; i <= 506; ++i) {
1190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertCached(false, i);
1200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
1220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
1240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Response code 407 should only come from proxy servers. Android's client
1250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * throws if it is sent by an origin server.
1260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
1270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testOriginServerSends407() throws Exception {
1280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(407));
1290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
1300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
1320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
1330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
1340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            conn.getResponseCode();
1350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail();
1360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (IOException expected) {
1370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
1390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void test_responseCaching_410() throws Exception {
1410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // the HTTP spec permits caching 410s, but the RI doesn't.
14284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertCached(true, 410);
1430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
1440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void assertCached(boolean shouldPut, int responseCode) throws Exception {
1460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server = new MockWebServer();
1470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
1480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
1500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setResponseCode(responseCode)
1510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("ABCDE")
1520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("WWW-Authenticate: challenge"));
1530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
1540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
1560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
1570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(responseCode, conn.getResponseCode());
1580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // exhaust the content stream
16084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        readAscii(conn);
1610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
162433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        CacheResponse cached = cache.get(url.toURI(), "GET",
163433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson                Collections.<String, List<String>>emptyMap());
164433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        if (shouldPut) {
165433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            assertNotNull(Integer.toString(responseCode), cached);
166433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            cached.getBody().close();
167433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        } else {
168433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            assertNull(Integer.toString(responseCode), cached);
169433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        }
1700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.shutdown(); // tearDown() isn't sufficient; this test starts multiple servers
1710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
1720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
1740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Test that we can interrogate the response when the cache is being
1750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * populated. http://code.google.com/p/android/issues/detail?id=7787
1760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
1770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponseCacheCallbackApis() throws Exception {
1780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        final String body = "ABCDE";
1790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        final AtomicInteger cacheCount = new AtomicInteger();
1800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
1820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setStatus("HTTP/1.1 200 Fantastic")
1830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("fgh: ijk")
1840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody(body));
1850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
1860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        ResponseCache.setDefault(new ResponseCache() {
1880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override public CacheResponse get(URI uri, String requestMethod,
1890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    Map<String, List<String>> requestHeaders) throws IOException {
1900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                return null;
1910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
1920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override public CacheRequest put(URI uri, URLConnection conn) throws IOException {
1930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                HttpURLConnection httpConnection = (HttpURLConnection) conn;
194953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                try {
195953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                    httpConnection.getRequestProperties();
196953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                    fail();
197953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                } catch (IllegalStateException expected) {
198953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                }
199953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                try {
200953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                    httpConnection.addRequestProperty("K", "V");
201953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                    fail();
202953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                } catch (IllegalStateException expected) {
203953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                }
2040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                assertEquals("HTTP/1.1 200 Fantastic", httpConnection.getHeaderField(null));
2050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                assertEquals(Arrays.asList("HTTP/1.1 200 Fantastic"),
2060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                        httpConnection.getHeaderFields().get(null));
2070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                assertEquals(200, httpConnection.getResponseCode());
2080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                assertEquals("Fantastic", httpConnection.getResponseMessage());
2090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                assertEquals(body.length(), httpConnection.getContentLength());
2100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                assertEquals("ijk", httpConnection.getHeaderField("fgh"));
2110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                try {
2120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    httpConnection.getInputStream(); // the RI doesn't forbid this, but it should
2130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    fail();
2140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                } catch (IOException expected) {
2150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                }
2160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                cacheCount.incrementAndGet();
2170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                return null;
2180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
2190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        });
2200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
222953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = url.openConnection();
2230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(body, readAscii(connection));
2240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(1, cacheCount.get());
2250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponseCachingAndInputStreamSkipWithFixedLength() throws IOException {
2290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testResponseCaching(TransferKind.FIXED_LENGTH);
2300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponseCachingAndInputStreamSkipWithChunkedEncoding() throws IOException {
2330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testResponseCaching(TransferKind.CHUNKED);
2340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponseCachingAndInputStreamSkipWithNoLengthHeaders() throws IOException {
2370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testResponseCaching(TransferKind.END_OF_STREAM);
2380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
2410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * HttpURLConnection.getInputStream().skip(long) causes ResponseCache corruption
2420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * http://code.google.com/p/android/issues/detail?id=8175
2430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
2440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testResponseCaching(TransferKind transferKind) throws IOException {
2450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        MockResponse response = new MockResponse()
2460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
2470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
2480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setStatus("HTTP/1.1 200 Fantastic");
2490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        transferKind.setBody(response, "I love puppies but hate spiders", 1);
2500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
2510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
2520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // Make sure that calling skip() doesn't omit bytes from the cache.
2540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection();
2550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        InputStream in = urlConnection.getInputStream();
2560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("I love ", readAscii(urlConnection, "I love ".length()));
2570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        reliableSkip(in, "puppies but hate ".length());
2580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("spiders", readAscii(urlConnection, "spiders".length()));
2590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(-1, in.read());
2600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        in.close();
2610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(1, cache.getSuccessCount());
2620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(0, cache.getAbortCount());
2630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        urlConnection = (HttpURLConnection) server.getUrl("/").openConnection(); // cached!
2650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        in = urlConnection.getInputStream();
2660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("I love puppies but hate spiders",
2670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                readAscii(urlConnection, "I love puppies but hate spiders".length()));
2680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(200, urlConnection.getResponseCode());
2690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("Fantastic", urlConnection.getResponseMessage());
2700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(-1, in.read());
272433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        in.close();
2730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(1, cache.getMissCount());
274757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(1, cache.getHeadersHitCount());
2750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(1, cache.getSuccessCount());
2760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(0, cache.getAbortCount());
2770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testSecureResponseCaching() throws IOException {
2800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
2810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
2820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
2830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
2840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
2850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("ABC"));
2860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
2870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
2890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
2900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
2910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // OpenJDK 6 fails on this line, complaining that the connection isn't open yet
2930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String suite = connection.getCipherSuite();
2940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<Certificate> localCerts = toListOrNull(connection.getLocalCertificates());
2950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<Certificate> serverCerts = toListOrNull(connection.getServerCertificates());
2960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        Principal peerPrincipal = connection.getPeerPrincipal();
2970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        Principal localPrincipal = connection.getLocalPrincipal();
2980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection = (HttpsURLConnection) server.getUrl("/").openConnection(); // cached!
3000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
3010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
3020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(1, cache.getMissCount());
304757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(1, cache.getHeadersHitCount());
3050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(suite, connection.getCipherSuite());
3070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(localCerts, toListOrNull(connection.getLocalCertificates()));
3080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(serverCerts, toListOrNull(connection.getServerCertificates()));
3090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(peerPrincipal, connection.getPeerPrincipal());
3100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(localPrincipal, connection.getLocalPrincipal());
3110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
3120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheReturnsInsecureResponseForSecureRequest() throws IOException {
3140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
3150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
3160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("ABC"));
3170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("DEF"));
3180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
3190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        ResponseCache.setDefault(new InsecureResponseCache());
3210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
3230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
3240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
3250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection = (HttpsURLConnection) server.getUrl("/").openConnection(); // not cached!
3270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
3280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("DEF", readAscii(connection));
3290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
3300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
331a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson    public void testResponseCachingAndRedirects() throws Exception {
3320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
3330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
3340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
3350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
3360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Location: /foo"));
3370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
3380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
3390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
3400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("ABC"));
3410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("DEF"));
3420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
3430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
344953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
3450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
3460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
347953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        connection = server.getUrl("/").openConnection(); // cached!
3480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
3490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(2, cache.getMissCount()); // 1 redirect + 1 final response = 2
351757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(2, cache.getHeadersHitCount());
3520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
3530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
354a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson    public void testRedirectToCachedResult() throws Exception {
355a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        server.enqueue(new MockResponse()
356a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson                .addHeader("Cache-Control: max-age=60")
357a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson                .setBody("ABC"));
358a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        server.enqueue(new MockResponse()
359a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
360a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson                .addHeader("Location: /foo"));
361a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        server.enqueue(new MockResponse().setBody("DEF"));
362a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        server.play();
363a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
364a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("ABC", readAscii(server.getUrl("/foo").openConnection()));
365a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        RecordedRequest request1 = server.takeRequest();
366a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("GET /foo HTTP/1.1", request1.getRequestLine());
367a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals(0, request1.getSequenceNumber());
368a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
369a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("ABC", readAscii(server.getUrl("/bar").openConnection()));
370a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        RecordedRequest request2 = server.takeRequest();
371a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("GET /bar HTTP/1.1", request2.getRequestLine());
372a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals(1, request2.getSequenceNumber());
373a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
374a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        // an unrelated request should reuse the pooled connection
375a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("DEF", readAscii(server.getUrl("/baz").openConnection()));
376a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        RecordedRequest request3 = server.takeRequest();
377a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("GET /baz HTTP/1.1", request3.getRequestLine());
378a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals(2, request3.getSequenceNumber());
379a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson    }
380a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
3810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testSecureResponseCachingAndRedirects() throws IOException {
3820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
3830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
3840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
3850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
3860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
3870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
3880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Location: /foo"));
3890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
3900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
3910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
3920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("ABC"));
3930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("DEF"));
3940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
3950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
3970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
3980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
3990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection = (HttpsURLConnection) server.getUrl("/").openConnection(); // cached!
4010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
4020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
4030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(2, cache.getMissCount()); // 1 redirect + 1 final response = 2
405757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(2, cache.getHeadersHitCount());
4060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponseCacheRequestHeaders() throws IOException, URISyntaxException {
4090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("ABC"));
4100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
4110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        final AtomicReference<Map<String, List<String>>> requestHeadersRef
4130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                = new AtomicReference<Map<String, List<String>>>();
4140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        ResponseCache.setDefault(new ResponseCache() {
4150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override public CacheResponse get(URI uri, String requestMethod,
4160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    Map<String, List<String>> requestHeaders) throws IOException {
4170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                requestHeadersRef.set(requestHeaders);
4180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                return null;
4190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
4200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override public CacheRequest put(URI uri, URLConnection conn) throws IOException {
4210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                return null;
4220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
4230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        });
4240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
4260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection urlConnection = url.openConnection();
4270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        urlConnection.addRequestProperty("A", "android");
4280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        readAscii(urlConnection);
4290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(Arrays.asList("android"), requestHeadersRef.get().get("A"));
4300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testServerDisconnectsPrematurelyWithContentLengthHeader() throws IOException {
4340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testServerPrematureDisconnect(TransferKind.FIXED_LENGTH);
4350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testServerDisconnectsPrematurelyWithChunkedEncoding() throws IOException {
4380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testServerPrematureDisconnect(TransferKind.CHUNKED);
4390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testServerDisconnectsPrematurelyWithNoLengthHeaders() throws IOException {
4420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        /*
4430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * Intentionally empty. This case doesn't make sense because there's no
4440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * such thing as a premature disconnect when the disconnect itself
4450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * indicates the end of the data stream.
4460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         */
4470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testServerPrematureDisconnect(TransferKind transferKind) throws IOException {
4500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        MockResponse response = new MockResponse();
4510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        transferKind.setBody(response, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 16);
4520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(truncateViolently(response, 16));
4530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("Request #2"));
4540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
4550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        BufferedReader reader = new BufferedReader(new InputStreamReader(
4570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                server.getUrl("/").openConnection().getInputStream()));
4580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCDE", reader.readLine());
4590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
4600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            reader.readLine();
4610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail("This implementation silently ignored a truncated HTTP body.");
4620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (IOException expected) {
463433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        } finally {
464433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            reader.close();
4650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
4660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(1, cache.getAbortCount());
4680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(0, cache.getSuccessCount());
4690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
4700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("Request #2", readAscii(connection));
4710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(1, cache.getAbortCount());
4720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(1, cache.getSuccessCount());
4730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testClientPrematureDisconnectWithContentLengthHeader() throws IOException {
4760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testClientPrematureDisconnect(TransferKind.FIXED_LENGTH);
4770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testClientPrematureDisconnectWithChunkedEncoding() throws IOException {
4800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testClientPrematureDisconnect(TransferKind.CHUNKED);
4810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testClientPrematureDisconnectWithNoLengthHeaders() throws IOException {
4840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testClientPrematureDisconnect(TransferKind.END_OF_STREAM);
4850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testClientPrematureDisconnect(TransferKind transferKind) throws IOException {
4880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        MockResponse response = new MockResponse();
4890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        transferKind.setBody(response, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 1024);
4900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
4910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("Request #2"));
4920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
4930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
4950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        InputStream in = connection.getInputStream();
4960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCDE", readAscii(connection, 5));
4970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        in.close();
4980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
4990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            in.read();
5000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail("Expected an IOException because the stream is closed.");
5010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (IOException expected) {
5020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
5030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
5040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(1, cache.getAbortCount());
5050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(0, cache.getSuccessCount());
5060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection = server.getUrl("/").openConnection();
50784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("Request #2", readAscii(connection));
5080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(1, cache.getAbortCount());
5090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(1, cache.getSuccessCount());
5100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
512953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public void testDefaultExpirationDateFullyCachedForLessThan24Hours() throws Exception {
51321dddca4064527116af7a1553de502c6d11138daJesse Wilson        //      last modified: 105 seconds ago
51421dddca4064527116af7a1553de502c6d11138daJesse Wilson        //             served:   5 seconds ago
51521dddca4064527116af7a1553de502c6d11138daJesse Wilson        //   default lifetime: (105 - 5) / 10 = 10 seconds
51621dddca4064527116af7a1553de502c6d11138daJesse Wilson        //            expires:  10 seconds from served date = 5 seconds from now
517953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse()
51821dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Last-Modified: " + formatDate(-105, TimeUnit.SECONDS))
519953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Date: " + formatDate(-5, TimeUnit.SECONDS))
520953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .setBody("A"));
521953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.play();
522953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
523953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URL url = server.getUrl("/");
524953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
525953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = url.openConnection();
526953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(connection));
527953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertNull(connection.getHeaderField("Warning"));
52821dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
52921dddca4064527116af7a1553de502c6d11138daJesse Wilson
53021dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testDefaultExpirationDateConditionallyCached() throws Exception {
53121dddca4064527116af7a1553de502c6d11138daJesse Wilson        //      last modified: 115 seconds ago
53221dddca4064527116af7a1553de502c6d11138daJesse Wilson        //             served:  15 seconds ago
53321dddca4064527116af7a1553de502c6d11138daJesse Wilson        //   default lifetime: (115 - 15) / 10 = 10 seconds
53421dddca4064527116af7a1553de502c6d11138daJesse Wilson        //            expires:  10 seconds from served date = 5 seconds ago
53521dddca4064527116af7a1553de502c6d11138daJesse Wilson        String lastModifiedDate = formatDate(-115, TimeUnit.SECONDS);
5360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
53721dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
53821dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Date: " + formatDate(-15, TimeUnit.SECONDS)));
5390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
5400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
5410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
543953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public void testDefaultExpirationDateFullyCachedForMoreThan24Hours() throws Exception {
544953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        //      last modified: 105 days ago
545953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        //             served:   5 days ago
546953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        //   default lifetime: (105 - 5) / 10 = 10 days
547953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        //            expires:  10 days from served date = 5 days from now
548953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse()
549953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-105, TimeUnit.DAYS))
550953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Date: " + formatDate(-5, TimeUnit.DAYS))
551953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .setBody("A"));
552953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.play();
553953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
554953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
555953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
556953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(connection));
557953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("113 HttpURLConnection \"Heuristic expiration\"",
558953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                connection.getHeaderField("Warning"));
559953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
560953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
56121dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testNoDefaultExpirationForUrlsWithQueryString() throws Exception {
56221dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse()
56321dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Last-Modified: " + formatDate(-105, TimeUnit.SECONDS))
56421dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Date: " + formatDate(-5, TimeUnit.SECONDS))
56521dddca4064527116af7a1553de502c6d11138daJesse Wilson                .setBody("A"));
56621dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
56721dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.play();
56821dddca4064527116af7a1553de502c6d11138daJesse Wilson
56921dddca4064527116af7a1553de502c6d11138daJesse Wilson        URL url = server.getUrl("/?foo=bar");
57021dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("A", readAscii(url.openConnection()));
57121dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("B", readAscii(url.openConnection()));
57221dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
57321dddca4064527116af7a1553de502c6d11138daJesse Wilson
5740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testExpirationDateInThePastWithLastModifiedHeader() throws Exception {
5750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
5760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
5770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
5780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
5790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
5800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
5810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
5830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testExpirationDateInThePastWithNoLastModifiedHeader() throws Exception {
5840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse()
5850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
5860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
5880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testExpirationDateInTheFuture() throws Exception {
5890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
5900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
5910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
5930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgePreferredWithMaxAgeAndExpires() throws Exception {
5940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
5950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
5960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS))
5970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
5980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgeInThePastWithDateAndLastModifiedHeaders() throws Exception {
6010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
6020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
6030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Date: " + formatDate(-120, TimeUnit.SECONDS))
6040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
6050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
6060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
6070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
6080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgeInThePastWithDateHeaderButNoLastModifiedHeader() throws Exception {
61184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        /*
61284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson         * Chrome interprets max-age relative to the local clock. Both our cache
61384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson         * and Firefox both use the earlier of the local and server's clock.
61484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson         */
6150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse()
6160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Date: " + formatDate(-120, TimeUnit.SECONDS))
6170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
6180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgeInTheFutureWithDateHeader() throws Exception {
6210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
6220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
6230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
6240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgeInTheFutureWithNoDateHeader() throws Exception {
6270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
6280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
6290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
631adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    public void testMaxAgeWithLastModifiedButNoServedDate() throws Exception {
632adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertFullyCached(new MockResponse()
633adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
634adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
635adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    }
636adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
637adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    public void testMaxAgeInTheFutureWithDateAndLastModifiedHeaders() throws Exception {
638adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertFullyCached(new MockResponse()
639adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
640adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
641adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
642adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    }
643adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
64421dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testMaxAgePreferredOverLowerSharedMaxAge() throws Exception {
64521dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertFullyCached(new MockResponse()
64621dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
64721dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: s-maxage=60")
64821dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: max-age=180"));
64921dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
65021dddca4064527116af7a1553de502c6d11138daJesse Wilson
65121dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testMaxAgePreferredOverHigherMaxAge() throws Exception {
65221dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertNotCached(new MockResponse()
65321dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
65421dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: s-maxage=180")
65521dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: max-age=60"));
65621dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
65721dddca4064527116af7a1553de502c6d11138daJesse Wilson
6580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodOptionsIsNotCached() throws Exception {
6590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("OPTIONS", false);
6600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodGetIsCached() throws Exception {
6630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("GET", true);
6640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodHeadIsNotCached() throws Exception {
6670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // We could support this but choose not to for implementation simplicity
6680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("HEAD", false);
6690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodPostIsNotCached() throws Exception {
6720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // We could support this but choose not to for implementation simplicity
6730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("POST", false);
6740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodPutIsNotCached() throws Exception {
6770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("PUT", false);
6780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodDeleteIsNotCached() throws Exception {
6810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("DELETE", false);
6820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodTraceIsNotCached() throws Exception {
6850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("TRACE", false);
6860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testRequestMethod(String requestMethod, boolean expectCached) throws Exception {
6890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        /*
6900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 1. seed the cache (potentially)
6910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 2. expect a cache hit or miss
6920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         */
6930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
6940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
6950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("X-Response-ID: 1"));
6960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
6970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("X-Response-ID: 2"));
6980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
6990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
7010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection request1 = (HttpURLConnection) url.openConnection();
7030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        request1.setRequestMethod(requestMethod);
7040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        addRequestBodyIfNecessary(requestMethod, request1);
7050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("1", request1.getHeaderField("X-Response-ID"));
7060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
707953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection request2 = url.openConnection();
7080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        if (expectCached) {
7090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertEquals("1", request1.getHeaderField("X-Response-ID"));
7100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } else {
7110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertEquals("2", request2.getHeaderField("X-Response-ID"));
7120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
7130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPostInvalidatesCache() throws Exception {
7160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testMethodInvalidates("POST");
7170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPutInvalidatesCache() throws Exception {
7200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testMethodInvalidates("PUT");
7210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testDeleteMethodInvalidatesCache() throws Exception {
7240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testMethodInvalidates("DELETE");
7250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testMethodInvalidates(String requestMethod) throws Exception {
7280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        /*
7290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 1. seed the cache
7300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 2. invalidate it
7310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 3. expect a cache miss
7320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         */
7330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
7340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
7350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
7360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("C"));
7370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
7380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
7400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
7420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection invalidate = (HttpURLConnection) url.openConnection();
7440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        invalidate.setRequestMethod(requestMethod);
7450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        addRequestBodyIfNecessary(requestMethod, invalidate);
7460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("B", readAscii(invalidate));
7470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("C", readAscii(url.openConnection()));
7490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testEtag() throws Exception {
7520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
7530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("ETag: v1"));
7540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(conditionalRequest.getHeaders().contains("If-None-Match: v1"));
7550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testEtagAndExpirationDateInThePast() throws Exception {
7580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
7590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
7600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("ETag: v1")
7610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
7620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
7630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
7640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-None-Match: v1"));
7650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
7660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testEtagAndExpirationDateInTheFuture() throws Exception {
7690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
7700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("ETag: v1")
7710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
7720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
7730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheControlNoCache() throws Exception {
7760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse().addHeader("Cache-Control: no-cache"));
7770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheControlNoCacheAndExpirationDateInTheFuture() throws Exception {
7800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
7810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
7820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
7830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
7840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: no-cache"));
7850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
7860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
7870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPragmaNoCache() throws Exception {
7900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse().addHeader("Pragma: no-cache"));
7910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPragmaNoCacheAndExpirationDateInTheFuture() throws Exception {
7940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
7950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
7960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
7970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
7980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Pragma: no-cache"));
7990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
8000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
8010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheControlNoStore() throws Exception {
8040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse().addHeader("Cache-Control: no-store"));
8050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheControlNoStoreAndExpirationDateInTheFuture() throws Exception {
8080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse()
8090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
8100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
8110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: no-store"));
8120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPartialRangeResponsesDoNotCorruptCache() throws Exception {
8150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        /*
8160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 1. request a range
8170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 2. request a full document, expecting a cache miss
8180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         */
8190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("AA")
8200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_PARTIAL)
8210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
8220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Content-Range: bytes 1000-1001/2000"));
8230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("BB"));
8240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
8250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
8270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection range = url.openConnection();
8290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        range.addRequestProperty("Range", "bytes=1000-1001");
8300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("AA", readAscii(range));
8310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("BB", readAscii(url.openConnection()));
8330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testServerReturnsDocumentOlderThanCache() throws Exception {
8360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
8370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
8380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
8390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("B")
8400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-4, TimeUnit.HOURS)));
8410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
8420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
8440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
8460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
8470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testNonIdentityEncodingAndConditionalCache() throws Exception {
8500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNonIdentityEncodingCached(new MockResponse()
8510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
8520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
8530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testNonIdentityEncodingAndFullCache() throws Exception {
8560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNonIdentityEncodingCached(new MockResponse()
8570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
8580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
8590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void assertNonIdentityEncodingCached(MockResponse response) throws Exception {
8620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response
8630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody(gzip("ABCABCABC".getBytes("UTF-8")))
8640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Content-Encoding: gzip"));
8650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
8660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
8680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCABCABC", readAscii(server.getUrl("/").openConnection()));
8690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCABCABC", readAscii(server.getUrl("/").openConnection()));
8700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testExpiresDateBeforeModifiedDate() throws Exception {
8730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertConditionallyCached(new MockResponse()
8740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
8750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-2, TimeUnit.HOURS)));
8760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
878adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    public void testRequestMaxAge() throws IOException {
879adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
880adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
881adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Date: " + formatDate(-1, TimeUnit.MINUTES))
882adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
883adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
884adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
885adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.play();
886adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
887adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
888adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
889adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        connection.addRequestProperty("Cache-Control", "max-age=30");
890adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals("B", readAscii(connection));
891adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    }
892adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
893adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    public void testRequestMinFresh() throws IOException {
894adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
895adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Cache-Control: max-age=60")
896adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
897adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
898adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
899adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.play();
900adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
901adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
902adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
903adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        connection.addRequestProperty("Cache-Control", "min-fresh=120");
904adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals("B", readAscii(connection));
905adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    }
906adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
907c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestMaxStale() throws IOException {
908c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("A")
909c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Cache-Control: max-age=120")
910c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Date: " + formatDate(-4, TimeUnit.MINUTES)));
911c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
912c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
913c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
914c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
915c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
916c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
917c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "max-stale=180");
918c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(connection));
919953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("110 HttpURLConnection \"Response is stale\"",
920953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                connection.getHeaderField("Warning"));
921c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
922c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
9232f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    public void testRequestMaxStaleNotHonoredWithMustRevalidate() throws IOException {
9242f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
9252f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .addHeader("Cache-Control: max-age=120, must-revalidate")
9262f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .addHeader("Date: " + formatDate(-4, TimeUnit.MINUTES)));
9272f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
9282f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
9292f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.play();
9302f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
9312f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
9322f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
9332f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        connection.addRequestProperty("Cache-Control", "max-stale=180");
9342f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("B", readAscii(connection));
9352f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    }
9362f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
937c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestOnlyIfCachedWithNoResponseCached() throws IOException {
938c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        // (no responses enqueued)
939c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
940c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
941953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
942c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "only-if-cached");
943c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertBadGateway(connection);
944c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
945c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
946c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestOnlyIfCachedWithFullResponseCached() throws IOException {
947c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("A")
948c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Cache-Control: max-age=30")
949c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
950c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
951c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
952c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
953c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
954c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "only-if-cached");
955c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
956c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
957c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
958c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestOnlyIfCachedWithConditionalResponseCached() throws IOException {
959c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("A")
960c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Cache-Control: max-age=30")
961c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Date: " + formatDate(-1, TimeUnit.MINUTES)));
962c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
963c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
964c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
965953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
966c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "only-if-cached");
967c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertBadGateway(connection);
968c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
969c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
970c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestOnlyIfCachedWithUnhelpfulResponseCached() throws IOException {
971c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("A"));
972c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
973c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
974c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
975953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
976c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "only-if-cached");
977c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertBadGateway(connection);
978c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
979c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
98084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testRequestCacheControlNoCache() throws Exception {
98184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse()
98284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
98384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
98484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Cache-Control: max-age=60")
98584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .setBody("A"));
98684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
98784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
98884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
98984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URL url = server.getUrl("/");
99084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
99184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URLConnection connection = url.openConnection();
99284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.setRequestProperty("Cache-Control", "no-cache");
99384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("B", readAscii(connection));
99484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
99584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
99684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testRequestPragmaNoCache() throws Exception {
99784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse()
99884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
99984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
100084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Cache-Control: max-age=60")
100184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .setBody("A"));
100284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
100384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
100484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
100584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URL url = server.getUrl("/");
100684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
100784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URLConnection connection = url.openConnection();
100884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.setRequestProperty("Pragma", "no-cache");
100984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("B", readAscii(connection));
101084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
101184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
101284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testClientSuppliedIfModifiedSinceWithCachedResult() throws Exception {
101384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        MockResponse response = new MockResponse()
101484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("ETag: v3")
101584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Cache-Control: max-age=0");
101684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        String ifModifiedSinceDate = formatDate(-24, TimeUnit.HOURS);
101784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        RecordedRequest request = assertClientSuppliedCondition(
101884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                response, "If-Modified-Since", ifModifiedSinceDate);
101984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        List<String> headers = request.getHeaders();
102084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + ifModifiedSinceDate));
102184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertFalse(headers.contains("If-None-Match: v3"));
102284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
102384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
102484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testClientSuppliedIfNoneMatchSinceWithCachedResult() throws Exception {
102584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        String lastModifiedDate = formatDate(-3, TimeUnit.MINUTES);
102684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        MockResponse response = new MockResponse()
102784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
102884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
102984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Cache-Control: max-age=0");
103084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        RecordedRequest request = assertClientSuppliedCondition(
103184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                response, "If-None-Match", "v1");
103284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        List<String> headers = request.getHeaders();
103384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertTrue(headers.contains("If-None-Match: v1"));
103484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertFalse(headers.contains("If-Modified-Since: " + lastModifiedDate));
103584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
103684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
103784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    private RecordedRequest assertClientSuppliedCondition(MockResponse seed, String conditionName,
103884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            String conditionValue) throws Exception {
103984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(seed.setBody("A"));
104084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
104184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
104284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
104384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URL url = server.getUrl("/");
104484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
104584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
104684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
104784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.addRequestProperty(conditionName, conditionValue);
104884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, connection.getResponseCode());
104984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("", readAscii(connection));
105084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
105184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.takeRequest(); // seed
105284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        return server.takeRequest();
105384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
105484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
10559531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    public void testSetIfModifiedSince() throws Exception {
10569531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        Date since = new Date();
10579531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        server.enqueue(new MockResponse().setBody("A"));
10589531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        server.play();
10599531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson
10609531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        URL url = server.getUrl("/");
10619531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        URLConnection connection = url.openConnection();
10629531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        connection.setIfModifiedSince(since.getTime());
10639531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        assertEquals("A", readAscii(connection));
10649531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        RecordedRequest request = server.takeRequest();
10659531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        assertTrue(request.getHeaders().contains("If-Modified-Since: " + formatDate(since)));
10669531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    }
10679531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson
106884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testClientSuppliedConditionWithoutCachedResult() throws Exception {
106984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse()
107084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
107184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
107284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
107384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
107484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        String clientIfModifiedSince = formatDate(-24, TimeUnit.HOURS);
107584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.addRequestProperty("If-Modified-Since", clientIfModifiedSince);
107684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, connection.getResponseCode());
107784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("", readAscii(connection));
107884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
1079c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
108021dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testAuthorizationRequestHeaderPreventsCaching() throws Exception {
108121dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse()
108221dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.MINUTES))
108321dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: max-age=60")
108421dddca4064527116af7a1553de502c6d11138daJesse Wilson                .setBody("A"));
108521dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
108621dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.play();
108721dddca4064527116af7a1553de502c6d11138daJesse Wilson
108821dddca4064527116af7a1553de502c6d11138daJesse Wilson        URL url = server.getUrl("/");
1089953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = url.openConnection();
109021dddca4064527116af7a1553de502c6d11138daJesse Wilson        connection.addRequestProperty("Authorization", "password");
109121dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("A", readAscii(connection));
109221dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("B", readAscii(url.openConnection()));
109321dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
109421dddca4064527116af7a1553de502c6d11138daJesse Wilson
109521dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testAuthorizationResponseCachedWithSMaxAge() throws Exception {
109621dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertAuthorizationRequestFullyCached(new MockResponse()
109721dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: s-maxage=60"));
109821dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
109921dddca4064527116af7a1553de502c6d11138daJesse Wilson
110021dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testAuthorizationResponseCachedWithPublic() throws Exception {
110121dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertAuthorizationRequestFullyCached(new MockResponse()
110221dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: public"));
110321dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
110421dddca4064527116af7a1553de502c6d11138daJesse Wilson
110521dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testAuthorizationResponseCachedWithMustRevalidate() throws Exception {
110621dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertAuthorizationRequestFullyCached(new MockResponse()
110721dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: must-revalidate"));
110821dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
110921dddca4064527116af7a1553de502c6d11138daJesse Wilson
111021dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void assertAuthorizationRequestFullyCached(MockResponse response) throws Exception {
111121dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(response
111221dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: max-age=60")
111321dddca4064527116af7a1553de502c6d11138daJesse Wilson                .setBody("A"));
111421dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
111521dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.play();
111621dddca4064527116af7a1553de502c6d11138daJesse Wilson
111721dddca4064527116af7a1553de502c6d11138daJesse Wilson        URL url = server.getUrl("/");
1118953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = url.openConnection();
111921dddca4064527116af7a1553de502c6d11138daJesse Wilson        connection.addRequestProperty("Authorization", "password");
112021dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("A", readAscii(connection));
112121dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("A", readAscii(url.openConnection()));
112221dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
112321dddca4064527116af7a1553de502c6d11138daJesse Wilson
1124953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public void testVaryResponsesAreNotSupported() throws Exception {
1125953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse()
1126953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Cache-Control: max-age=60")
1127953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Vary: Accept-Language")
1128953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .setBody("A"));
1129953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
1130953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.play();
1131953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
1132953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URL url = server.getUrl("/");
1133953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection1 = url.openConnection();
1134953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        connection1.addRequestProperty("Accept-Language", "fr-CA");
1135953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(connection1));
1136953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
1137953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection2 = url.openConnection();
1138953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        connection2.addRequestProperty("Accept-Language", "fr-CA");
1139953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("B", readAscii(connection2));
1140953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
1141953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
1142953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public void testContentLocationDoesNotPopulateCache() throws Exception {
1143953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse()
1144953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Cache-Control: max-age=60")
1145953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Content-Location: /bar")
1146953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .setBody("A"));
1147953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
1148953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.play();
1149953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
1150953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/foo").openConnection()));
1151953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("B", readAscii(server.getUrl("/bar").openConnection()));
1152953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
1153953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
115419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    public void testUseCachesFalseDoesNotWriteToCache() throws Exception {
115519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.enqueue(new MockResponse()
115619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson                .addHeader("Cache-Control: max-age=60")
115719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson                .setBody("A").setBody("A"));
115819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
115919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.play();
116019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
116119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
116219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        connection.setUseCaches(false);
116319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertEquals("A", readAscii(connection));
116419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertEquals("B", readAscii(server.getUrl("/").openConnection()));
116519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    }
116619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
116719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    public void testUseCachesFalseDoesNotReadFromCache() throws Exception {
116819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.enqueue(new MockResponse()
116919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson                .addHeader("Cache-Control: max-age=60")
117019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson                .setBody("A").setBody("A"));
117119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
117219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.play();
117319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
117419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
117519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
117619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        connection.setUseCaches(false);
117719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertEquals("B", readAscii(connection));
117819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    }
117919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
118019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    public void testDefaultUseCachesSetsInitialValueOnly() throws Exception {
118119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URL url = new URL("http://localhost/");
118219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URLConnection c1 = url.openConnection();
118319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URLConnection c2 = url.openConnection();
118419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertTrue(c1.getDefaultUseCaches());
118519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        c1.setDefaultUseCaches(false);
118619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        try {
118719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            assertTrue(c1.getUseCaches());
118819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            assertTrue(c2.getUseCaches());
118919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            URLConnection c3 = url.openConnection();
119019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            assertFalse(c3.getUseCaches());
119119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        } finally {
119219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            c1.setDefaultUseCaches(true);
119319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        }
119419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    }
119519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
11962f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    public void testConnectionIsReturnedToPoolAfterConditionalSuccess() throws Exception {
11972f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse()
11982f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
11992f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .addHeader("Cache-Control: max-age=0")
12002f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .setBody("A"));
12012f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
12022f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
12032f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.play();
12042f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
12052f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/a").openConnection()));
12062f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/a").openConnection()));
12072f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("B", readAscii(server.getUrl("/b").openConnection()));
12082f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
12092f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals(0, server.takeRequest().getSequenceNumber());
12102f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals(1, server.takeRequest().getSequenceNumber());
12112f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals(2, server.takeRequest().getSequenceNumber());
12122f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    }
12132f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
1214757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    public void testStatisticsConditionalCacheMiss() throws Exception {
1215757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse()
1216757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1217757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Cache-Control: max-age=0")
1218757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .setBody("A"));
1219757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
1220757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse().setBody("C"));
1221757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.play();
1222757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1223757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1224757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(1, cache.getMissCount());
1225757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(0, cache.getHeadersHitCount());
1226757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(0, cache.getBodyHitCount());
1227757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("B", readAscii(server.getUrl("/").openConnection()));
1228757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("C", readAscii(server.getUrl("/").openConnection()));
1229757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(1, cache.getMissCount());
1230757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(2, cache.getHeadersHitCount());
1231757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(0, cache.getBodyHitCount());
1232757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    }
1233757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1234757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    public void testStatisticsConditionalCacheHit() throws Exception {
1235757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse()
1236757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1237757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Cache-Control: max-age=0")
1238757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .setBody("A"));
1239757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1240757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1241757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.play();
1242757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1243757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1244757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(1, cache.getMissCount());
1245757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(0, cache.getHeadersHitCount());
1246757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(0, cache.getBodyHitCount());
1247757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1248757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1249757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(1, cache.getMissCount());
1250757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(2, cache.getHeadersHitCount());
1251757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(2, cache.getBodyHitCount());
1252757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    }
1253757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1254757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    public void testStatisticsFullCacheHit() throws Exception {
1255757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse()
1256757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Cache-Control: max-age=60")
1257757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .setBody("A"));
1258757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.play();
1259757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1260757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1261757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(1, cache.getMissCount());
1262757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(0, cache.getHeadersHitCount());
1263757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(0, cache.getBodyHitCount());
1264757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1265757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1266757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(1, cache.getMissCount());
1267757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(2, cache.getHeadersHitCount());
1268757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals(2, cache.getBodyHitCount());
1269757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    }
1270757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
12710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
12720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * @param delta the offset from the current date to use. Negative
12730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     *     values yield dates in the past; positive values yield dates in the
12740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     *     future.
12750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
1276953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private String formatDate(long delta, TimeUnit timeUnit) {
12779531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        return formatDate(new Date(System.currentTimeMillis() + timeUnit.toMillis(delta)));
12789531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    }
12799531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson
12809531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    private String formatDate(Date date) {
1281953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        DateFormat rfc1123 = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
1282953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        rfc1123.setTimeZone(TimeZone.getTimeZone("UTC"));
1283953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        return rfc1123.format(date);
12840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
12850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
12860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void addRequestBodyIfNecessary(String requestMethod, HttpURLConnection invalidate)
12870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            throws IOException {
12880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        if (requestMethod.equals("POST") || requestMethod.equals("PUT")) {
12890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            invalidate.setDoOutput(true);
12900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            OutputStream requestBody = invalidate.getOutputStream();
12910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            requestBody.write('x');
12920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            requestBody.close();
12930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
12940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
12950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
12960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void assertNotCached(MockResponse response) throws Exception {
12970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response.setBody("A"));
12980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
12990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
13000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
13020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
13030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("B", readAscii(url.openConnection()));
13040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
13050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
13070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * @return the request with the conditional get headers.
13080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
13090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private RecordedRequest assertConditionallyCached(MockResponse response) throws Exception {
13100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // scenario 1: condition succeeds
13110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response.setBody("A"));
13120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
13130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // scenario 2: condition fails
13150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response.setBody("B"));
13160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("C"));
13170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
13190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL valid = server.getUrl("/valid");
13210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(valid.openConnection()));
13220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(valid.openConnection()));
13230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL invalid = server.getUrl("/invalid");
13250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("B", readAscii(invalid.openConnection()));
13260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("C", readAscii(invalid.openConnection()));
13270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.takeRequest(); // regular get
13290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return server.takeRequest(); // conditional get
13300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
13310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void assertFullyCached(MockResponse response) throws Exception {
13330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response.setBody("A"));
13340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response.setBody("B"));
13350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
13360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
13380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
13390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
13400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
13410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
13430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Shortens the body of {@code response} but not the corresponding headers.
13440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Only useful to test how clients respond to the premature conclusion of
13450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * the HTTP body.
13460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
13470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private MockResponse truncateViolently(MockResponse response, int numBytesToKeep) {
13480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        response.setSocketPolicy(DISCONNECT_AT_END);
13490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = new ArrayList<String>(response.getHeaders());
13500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        response.setBody(Arrays.copyOfRange(response.getBody(), 0, numBytesToKeep));
13510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        response.getHeaders().clear();
13520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        response.getHeaders().addAll(headers);
13530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return response;
13540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
13550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
13570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Reads {@code count} characters from the stream. If the stream is
13580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * exhausted before {@code count} characters can be read, the remaining
13590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * characters are returned and the stream is closed.
13600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
13610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private String readAscii(URLConnection connection, int count) throws IOException {
136284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpURLConnection httpConnection = (HttpURLConnection) connection;
136384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        InputStream in = httpConnection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST
136484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                ? connection.getInputStream()
136584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                : httpConnection.getErrorStream();
13660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        StringBuilder result = new StringBuilder();
13670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        for (int i = 0; i < count; i++) {
13680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            int value = in.read();
13690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            if (value == -1) {
13700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                in.close();
13710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                break;
13720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
13730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            result.append((char) value);
13740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
13750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return result.toString();
13760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
13770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private String readAscii(URLConnection connection) throws IOException {
13790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return readAscii(connection, Integer.MAX_VALUE);
13800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
13810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void reliableSkip(InputStream in, int length) throws IOException {
13830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        while (length > 0) {
13840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            length -= in.skip(length);
13850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
13860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
13870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1388953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private void assertBadGateway(HttpURLConnection connection) throws IOException {
1389c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        try {
1390c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson            connection.getInputStream();
1391c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson            fail();
1392c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        } catch (FileNotFoundException expected) {
1393c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        }
1394953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals(HttpURLConnection.HTTP_BAD_GATEWAY, connection.getResponseCode());
1395953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals(-1, connection.getErrorStream().read());
1396c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
1397c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
13980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    enum TransferKind {
13990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        CHUNKED() {
14000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override void setBody(MockResponse response, byte[] content, int chunkSize)
14010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    throws IOException {
14020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                response.setChunkedBody(content, chunkSize);
14030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
14040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        },
14050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        FIXED_LENGTH() {
14060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override void setBody(MockResponse response, byte[] content, int chunkSize) {
14070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                response.setBody(content);
14080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
14090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        },
14100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        END_OF_STREAM() {
14110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override void setBody(MockResponse response, byte[] content, int chunkSize) {
14120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                response.setBody(content);
14130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                response.setSocketPolicy(DISCONNECT_AT_END);
14140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                for (Iterator<String> h = response.getHeaders().iterator(); h.hasNext(); ) {
14150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    if (h.next().startsWith("Content-Length:")) {
14160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                        h.remove();
14170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                        break;
14180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    }
14190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                }
14200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
14210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        };
14220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        abstract void setBody(MockResponse response, byte[] content, int chunkSize)
14240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                throws IOException;
14250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        void setBody(MockResponse response, String content, int chunkSize) throws IOException {
14270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            setBody(response, content.getBytes("UTF-8"), chunkSize);
14280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
14290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
14300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private <T> List<T> toListOrNull(T[] arrayOrNull) {
14320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return arrayOrNull != null ? Arrays.asList(arrayOrNull) : null;
14330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
14340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
14360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Returns a gzipped copy of {@code bytes}.
14370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
14380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public byte[] gzip(byte[] bytes) throws IOException {
14390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
14400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        OutputStream gzippedOut = new GZIPOutputStream(bytesOut);
14410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        gzippedOut.write(bytes);
14420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        gzippedOut.close();
14430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return bytesOut.toByteArray();
14440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
14450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1446433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson    private class InsecureResponseCache extends ResponseCache {
14470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        @Override public CacheRequest put(URI uri, URLConnection connection) throws IOException {
1448433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            return cache.put(uri, connection);
14490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
14500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        @Override public CacheResponse get(URI uri, String requestMethod,
14520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                Map<String, List<String>> requestHeaders) throws IOException {
1453433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            final CacheResponse response = cache.get(uri, requestMethod, requestHeaders);
14540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            if (response instanceof SecureCacheResponse) {
14550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                return new CacheResponse() {
14560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    @Override public InputStream getBody() throws IOException {
14570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                        return response.getBody();
14580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    }
14590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    @Override public Map<String, List<String>> getHeaders() throws IOException {
14600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                        return response.getHeaders();
14610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    }
14620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                };
14630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
14640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            return response;
14650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
14660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
14670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson}
1468