HttpResponseCacheTest.java revision ca8ae42fd6a787757897a680108bdcf7b0d37f13
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
62ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilsonpublic final class DiskResponseCacheTest extends TestCase {
630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private MockWebServer server = new MockWebServer();
64ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson    private DiskResponseCache 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();
72ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        cache = new DiskResponseCache(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();
261ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteSuccessCount());
262ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getWriteAbortCount());
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();
273ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteSuccessCount());
274ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getWriteAbortCount());
275ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getRequestCount());
276ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getHitCount());
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
303ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getRequestCount());
304ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getNetworkCount());
305ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getHitCount());
3060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(suite, connection.getCipherSuite());
3080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(localCerts, toListOrNull(connection.getLocalCertificates()));
3090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(serverCerts, toListOrNull(connection.getServerCertificates()));
3100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(peerPrincipal, connection.getPeerPrincipal());
3110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(localPrincipal, connection.getLocalPrincipal());
3120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
3130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheReturnsInsecureResponseForSecureRequest() throws IOException {
3150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
3160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
3170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("ABC"));
3180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("DEF"));
3190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
3200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        ResponseCache.setDefault(new InsecureResponseCache());
3220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
3240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
3250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
3260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection = (HttpsURLConnection) server.getUrl("/").openConnection(); // not cached!
3280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
3290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("DEF", readAscii(connection));
3300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
3310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
332a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson    public void testResponseCachingAndRedirects() throws Exception {
3330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
3340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
3350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
3360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
3370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Location: /foo"));
3380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
3390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
3400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
3410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("ABC"));
3420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("DEF"));
3430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
3440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
345953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
3460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
3470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
348953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        connection = server.getUrl("/").openConnection(); // cached!
3490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
3500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
351ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(4, cache.getRequestCount()); // 2 requests + 2 redirects
352ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getNetworkCount());
353ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getHitCount());
3540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
3550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
356a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson    public void testRedirectToCachedResult() throws Exception {
357a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        server.enqueue(new MockResponse()
358a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson                .addHeader("Cache-Control: max-age=60")
359a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson                .setBody("ABC"));
360a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        server.enqueue(new MockResponse()
361a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
362a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson                .addHeader("Location: /foo"));
363a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        server.enqueue(new MockResponse().setBody("DEF"));
364a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        server.play();
365a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
366a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("ABC", readAscii(server.getUrl("/foo").openConnection()));
367a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        RecordedRequest request1 = server.takeRequest();
368a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("GET /foo HTTP/1.1", request1.getRequestLine());
369a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals(0, request1.getSequenceNumber());
370a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
371a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("ABC", readAscii(server.getUrl("/bar").openConnection()));
372a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        RecordedRequest request2 = server.takeRequest();
373a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("GET /bar HTTP/1.1", request2.getRequestLine());
374a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals(1, request2.getSequenceNumber());
375a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
376a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        // an unrelated request should reuse the pooled connection
377a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("DEF", readAscii(server.getUrl("/baz").openConnection()));
378a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        RecordedRequest request3 = server.takeRequest();
379a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("GET /baz HTTP/1.1", request3.getRequestLine());
380a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals(2, request3.getSequenceNumber());
381a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson    }
382a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
3830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testSecureResponseCachingAndRedirects() throws IOException {
3840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
3850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
3860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
3870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
3880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
3890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
3900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Location: /foo"));
3910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
3920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
3930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
3940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("ABC"));
3950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("DEF"));
3960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
3970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
3990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
4000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
4010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection = (HttpsURLConnection) server.getUrl("/").openConnection(); // cached!
4030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
4040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
4050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
406ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(4, cache.getRequestCount()); // 2 direct + 2 redirect = 4
407ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getHitCount());
4080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponseCacheRequestHeaders() throws IOException, URISyntaxException {
4110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("ABC"));
4120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
4130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        final AtomicReference<Map<String, List<String>>> requestHeadersRef
4150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                = new AtomicReference<Map<String, List<String>>>();
4160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        ResponseCache.setDefault(new ResponseCache() {
4170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override public CacheResponse get(URI uri, String requestMethod,
4180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    Map<String, List<String>> requestHeaders) throws IOException {
4190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                requestHeadersRef.set(requestHeaders);
4200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                return null;
4210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
4220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override public CacheRequest put(URI uri, URLConnection conn) throws IOException {
4230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                return null;
4240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
4250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        });
4260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
4280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection urlConnection = url.openConnection();
4290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        urlConnection.addRequestProperty("A", "android");
4300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        readAscii(urlConnection);
4310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(Arrays.asList("android"), requestHeadersRef.get().get("A"));
4320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testServerDisconnectsPrematurelyWithContentLengthHeader() throws IOException {
4360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testServerPrematureDisconnect(TransferKind.FIXED_LENGTH);
4370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testServerDisconnectsPrematurelyWithChunkedEncoding() throws IOException {
4400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testServerPrematureDisconnect(TransferKind.CHUNKED);
4410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testServerDisconnectsPrematurelyWithNoLengthHeaders() throws IOException {
4440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        /*
4450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * Intentionally empty. This case doesn't make sense because there's no
4460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * such thing as a premature disconnect when the disconnect itself
4470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * indicates the end of the data stream.
4480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         */
4490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testServerPrematureDisconnect(TransferKind transferKind) throws IOException {
4520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        MockResponse response = new MockResponse();
4530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        transferKind.setBody(response, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 16);
4540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(truncateViolently(response, 16));
4550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("Request #2"));
4560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
4570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        BufferedReader reader = new BufferedReader(new InputStreamReader(
4590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                server.getUrl("/").openConnection().getInputStream()));
4600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCDE", reader.readLine());
4610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
4620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            reader.readLine();
4630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail("This implementation silently ignored a truncated HTTP body.");
4640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (IOException expected) {
465433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        } finally {
466433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            reader.close();
4670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
4680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
469ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteAbortCount());
470ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getWriteSuccessCount());
4710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
4720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("Request #2", readAscii(connection));
473ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteAbortCount());
474ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteSuccessCount());
4750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testClientPrematureDisconnectWithContentLengthHeader() throws IOException {
4780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testClientPrematureDisconnect(TransferKind.FIXED_LENGTH);
4790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testClientPrematureDisconnectWithChunkedEncoding() throws IOException {
4820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testClientPrematureDisconnect(TransferKind.CHUNKED);
4830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testClientPrematureDisconnectWithNoLengthHeaders() throws IOException {
4860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testClientPrematureDisconnect(TransferKind.END_OF_STREAM);
4870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testClientPrematureDisconnect(TransferKind transferKind) throws IOException {
4900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        MockResponse response = new MockResponse();
4910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        transferKind.setBody(response, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 1024);
4920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
4930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("Request #2"));
4940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
4950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
4970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        InputStream in = connection.getInputStream();
4980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCDE", readAscii(connection, 5));
4990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        in.close();
5000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
5010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            in.read();
5020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail("Expected an IOException because the stream is closed.");
5030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (IOException expected) {
5040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
5050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
506ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteAbortCount());
507ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getWriteSuccessCount());
5080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection = server.getUrl("/").openConnection();
50984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("Request #2", readAscii(connection));
510ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteAbortCount());
511ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteSuccessCount());
5120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
514953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public void testDefaultExpirationDateFullyCachedForLessThan24Hours() throws Exception {
51521dddca4064527116af7a1553de502c6d11138daJesse Wilson        //      last modified: 105 seconds ago
51621dddca4064527116af7a1553de502c6d11138daJesse Wilson        //             served:   5 seconds ago
51721dddca4064527116af7a1553de502c6d11138daJesse Wilson        //   default lifetime: (105 - 5) / 10 = 10 seconds
51821dddca4064527116af7a1553de502c6d11138daJesse Wilson        //            expires:  10 seconds from served date = 5 seconds from now
519953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse()
52021dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Last-Modified: " + formatDate(-105, TimeUnit.SECONDS))
521953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Date: " + formatDate(-5, TimeUnit.SECONDS))
522953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .setBody("A"));
523953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.play();
524953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
525953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URL url = server.getUrl("/");
526953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
527953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = url.openConnection();
528953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(connection));
529953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertNull(connection.getHeaderField("Warning"));
53021dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
53121dddca4064527116af7a1553de502c6d11138daJesse Wilson
53221dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testDefaultExpirationDateConditionallyCached() throws Exception {
53321dddca4064527116af7a1553de502c6d11138daJesse Wilson        //      last modified: 115 seconds ago
53421dddca4064527116af7a1553de502c6d11138daJesse Wilson        //             served:  15 seconds ago
53521dddca4064527116af7a1553de502c6d11138daJesse Wilson        //   default lifetime: (115 - 15) / 10 = 10 seconds
53621dddca4064527116af7a1553de502c6d11138daJesse Wilson        //            expires:  10 seconds from served date = 5 seconds ago
53721dddca4064527116af7a1553de502c6d11138daJesse Wilson        String lastModifiedDate = formatDate(-115, TimeUnit.SECONDS);
5380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
53921dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
54021dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Date: " + formatDate(-15, TimeUnit.SECONDS)));
5410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
5420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
5430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
545953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public void testDefaultExpirationDateFullyCachedForMoreThan24Hours() throws Exception {
546953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        //      last modified: 105 days ago
547953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        //             served:   5 days ago
548953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        //   default lifetime: (105 - 5) / 10 = 10 days
549953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        //            expires:  10 days from served date = 5 days from now
550953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse()
551953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-105, TimeUnit.DAYS))
552953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Date: " + formatDate(-5, TimeUnit.DAYS))
553953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .setBody("A"));
554953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.play();
555953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
556953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
557953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
558953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(connection));
559953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("113 HttpURLConnection \"Heuristic expiration\"",
560953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                connection.getHeaderField("Warning"));
561953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
562953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
56321dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testNoDefaultExpirationForUrlsWithQueryString() throws Exception {
56421dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse()
56521dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Last-Modified: " + formatDate(-105, TimeUnit.SECONDS))
56621dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Date: " + formatDate(-5, TimeUnit.SECONDS))
56721dddca4064527116af7a1553de502c6d11138daJesse Wilson                .setBody("A"));
56821dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
56921dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.play();
57021dddca4064527116af7a1553de502c6d11138daJesse Wilson
57121dddca4064527116af7a1553de502c6d11138daJesse Wilson        URL url = server.getUrl("/?foo=bar");
57221dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("A", readAscii(url.openConnection()));
57321dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("B", readAscii(url.openConnection()));
57421dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
57521dddca4064527116af7a1553de502c6d11138daJesse Wilson
5760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testExpirationDateInThePastWithLastModifiedHeader() throws Exception {
5770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
5780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
5790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
5800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
5810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
5820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
5830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
5850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testExpirationDateInThePastWithNoLastModifiedHeader() throws Exception {
5860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse()
5870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
5880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
5900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testExpirationDateInTheFuture() throws Exception {
5910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
5920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
5930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
5950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgePreferredWithMaxAgeAndExpires() throws Exception {
5960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
5970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
5980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS))
5990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
6000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgeInThePastWithDateAndLastModifiedHeaders() throws Exception {
6030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
6040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
6050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Date: " + formatDate(-120, TimeUnit.SECONDS))
6060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
6070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
6080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
6090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
6100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgeInThePastWithDateHeaderButNoLastModifiedHeader() throws Exception {
61384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        /*
61484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson         * Chrome interprets max-age relative to the local clock. Both our cache
61584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson         * and Firefox both use the earlier of the local and server's clock.
61684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson         */
6170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse()
6180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Date: " + formatDate(-120, TimeUnit.SECONDS))
6190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
6200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgeInTheFutureWithDateHeader() throws Exception {
6230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
6240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
6250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
6260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgeInTheFutureWithNoDateHeader() throws Exception {
6290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
6300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
6310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
633adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    public void testMaxAgeWithLastModifiedButNoServedDate() throws Exception {
634adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertFullyCached(new MockResponse()
635adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
636adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
637adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    }
638adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
639adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    public void testMaxAgeInTheFutureWithDateAndLastModifiedHeaders() throws Exception {
640adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertFullyCached(new MockResponse()
641adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
642adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
643adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
644adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    }
645adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
64621dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testMaxAgePreferredOverLowerSharedMaxAge() throws Exception {
64721dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertFullyCached(new MockResponse()
64821dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
64921dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: s-maxage=60")
65021dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: max-age=180"));
65121dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
65221dddca4064527116af7a1553de502c6d11138daJesse Wilson
65321dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testMaxAgePreferredOverHigherMaxAge() throws Exception {
65421dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertNotCached(new MockResponse()
65521dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
65621dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: s-maxage=180")
65721dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: max-age=60"));
65821dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
65921dddca4064527116af7a1553de502c6d11138daJesse Wilson
6600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodOptionsIsNotCached() throws Exception {
6610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("OPTIONS", false);
6620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodGetIsCached() throws Exception {
6650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("GET", true);
6660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodHeadIsNotCached() throws Exception {
6690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // We could support this but choose not to for implementation simplicity
6700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("HEAD", false);
6710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodPostIsNotCached() throws Exception {
6740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // We could support this but choose not to for implementation simplicity
6750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("POST", false);
6760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodPutIsNotCached() throws Exception {
6790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("PUT", false);
6800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodDeleteIsNotCached() throws Exception {
6830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("DELETE", false);
6840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodTraceIsNotCached() throws Exception {
6870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("TRACE", false);
6880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testRequestMethod(String requestMethod, boolean expectCached) throws Exception {
6910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        /*
6920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 1. seed the cache (potentially)
6930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 2. expect a cache hit or miss
6940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         */
6950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
6960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
6970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("X-Response-ID: 1"));
6980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
6990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("X-Response-ID: 2"));
7000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
7010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
7030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection request1 = (HttpURLConnection) url.openConnection();
7050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        request1.setRequestMethod(requestMethod);
7060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        addRequestBodyIfNecessary(requestMethod, request1);
7070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("1", request1.getHeaderField("X-Response-ID"));
7080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
709953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection request2 = url.openConnection();
7100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        if (expectCached) {
7110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertEquals("1", request1.getHeaderField("X-Response-ID"));
7120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } else {
7130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertEquals("2", request2.getHeaderField("X-Response-ID"));
7140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
7150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPostInvalidatesCache() throws Exception {
7180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testMethodInvalidates("POST");
7190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPutInvalidatesCache() throws Exception {
7220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testMethodInvalidates("PUT");
7230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testDeleteMethodInvalidatesCache() throws Exception {
7260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testMethodInvalidates("DELETE");
7270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testMethodInvalidates(String requestMethod) throws Exception {
7300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        /*
7310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 1. seed the cache
7320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 2. invalidate it
7330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 3. expect a cache miss
7340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         */
7350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
7360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
7370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
7380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("C"));
7390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
7400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
7420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
7440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection invalidate = (HttpURLConnection) url.openConnection();
7460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        invalidate.setRequestMethod(requestMethod);
7470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        addRequestBodyIfNecessary(requestMethod, invalidate);
7480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("B", readAscii(invalidate));
7490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("C", readAscii(url.openConnection()));
7510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testEtag() throws Exception {
7540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
7550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("ETag: v1"));
7560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(conditionalRequest.getHeaders().contains("If-None-Match: v1"));
7570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testEtagAndExpirationDateInThePast() throws Exception {
7600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
7610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
7620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("ETag: v1")
7630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
7640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
7650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
7660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-None-Match: v1"));
7670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
7680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testEtagAndExpirationDateInTheFuture() throws Exception {
7710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
7720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("ETag: v1")
7730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
7740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
7750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheControlNoCache() throws Exception {
7780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse().addHeader("Cache-Control: no-cache"));
7790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheControlNoCacheAndExpirationDateInTheFuture() throws Exception {
7820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
7830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
7840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
7850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
7860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: no-cache"));
7870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
7880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
7890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPragmaNoCache() throws Exception {
7920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse().addHeader("Pragma: no-cache"));
7930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPragmaNoCacheAndExpirationDateInTheFuture() throws Exception {
7960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
7970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
7980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
7990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
8000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Pragma: no-cache"));
8010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
8020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
8030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheControlNoStore() throws Exception {
8060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse().addHeader("Cache-Control: no-store"));
8070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheControlNoStoreAndExpirationDateInTheFuture() throws Exception {
8100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse()
8110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
8120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
8130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: no-store"));
8140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPartialRangeResponsesDoNotCorruptCache() throws Exception {
8170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        /*
8180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 1. request a range
8190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 2. request a full document, expecting a cache miss
8200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         */
8210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("AA")
8220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_PARTIAL)
8230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
8240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Content-Range: bytes 1000-1001/2000"));
8250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("BB"));
8260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
8270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
8290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection range = url.openConnection();
8310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        range.addRequestProperty("Range", "bytes=1000-1001");
8320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("AA", readAscii(range));
8330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("BB", readAscii(url.openConnection()));
8350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testServerReturnsDocumentOlderThanCache() throws Exception {
8380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
8390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
8400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
8410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("B")
8420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-4, TimeUnit.HOURS)));
8430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
8440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
8460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
8480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
8490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testNonIdentityEncodingAndConditionalCache() throws Exception {
8520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNonIdentityEncodingCached(new MockResponse()
8530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
8540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
8550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testNonIdentityEncodingAndFullCache() throws Exception {
8580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNonIdentityEncodingCached(new MockResponse()
8590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
8600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
8610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void assertNonIdentityEncodingCached(MockResponse response) throws Exception {
8640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response
8650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody(gzip("ABCABCABC".getBytes("UTF-8")))
8660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Content-Encoding: gzip"));
8670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
8680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
8700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCABCABC", readAscii(server.getUrl("/").openConnection()));
8710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCABCABC", readAscii(server.getUrl("/").openConnection()));
8720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testExpiresDateBeforeModifiedDate() throws Exception {
8750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertConditionallyCached(new MockResponse()
8760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
8770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-2, TimeUnit.HOURS)));
8780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
880adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    public void testRequestMaxAge() throws IOException {
881adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
882adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
883adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Date: " + formatDate(-1, TimeUnit.MINUTES))
884adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
885adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
886adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
887adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.play();
888adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
889adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
890adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
891adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        connection.addRequestProperty("Cache-Control", "max-age=30");
892adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals("B", readAscii(connection));
893adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    }
894adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
895adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    public void testRequestMinFresh() throws IOException {
896adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
897adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Cache-Control: max-age=60")
898adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
899adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
900adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
901adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.play();
902adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
903adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
904adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
905adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        connection.addRequestProperty("Cache-Control", "min-fresh=120");
906adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals("B", readAscii(connection));
907adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    }
908adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
909c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestMaxStale() throws IOException {
910c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("A")
911c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Cache-Control: max-age=120")
912c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Date: " + formatDate(-4, TimeUnit.MINUTES)));
913c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
914c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
915c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
916c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
917c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
918c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
919c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "max-stale=180");
920c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(connection));
921953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("110 HttpURLConnection \"Response is stale\"",
922953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                connection.getHeaderField("Warning"));
923c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
924c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
9252f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    public void testRequestMaxStaleNotHonoredWithMustRevalidate() throws IOException {
9262f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
9272f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .addHeader("Cache-Control: max-age=120, must-revalidate")
9282f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .addHeader("Date: " + formatDate(-4, TimeUnit.MINUTES)));
9292f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
9302f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
9312f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.play();
9322f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
9332f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
9342f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
9352f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        connection.addRequestProperty("Cache-Control", "max-stale=180");
9362f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("B", readAscii(connection));
9372f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    }
9382f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
939c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestOnlyIfCachedWithNoResponseCached() throws IOException {
940c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        // (no responses enqueued)
941c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
942c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
943953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
944c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "only-if-cached");
945c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertBadGateway(connection);
946c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
947c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
948c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestOnlyIfCachedWithFullResponseCached() throws IOException {
949c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("A")
950c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Cache-Control: max-age=30")
951c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
952c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
953c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
954c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
955c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
956c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "only-if-cached");
957c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
958c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
959c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
960c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestOnlyIfCachedWithConditionalResponseCached() throws IOException {
961c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("A")
962c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Cache-Control: max-age=30")
963c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Date: " + formatDate(-1, TimeUnit.MINUTES)));
964c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
965c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
966c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
967953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
968c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "only-if-cached");
969c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertBadGateway(connection);
970c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
971c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
972c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestOnlyIfCachedWithUnhelpfulResponseCached() throws IOException {
973c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("A"));
974c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
975c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
976c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
977953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
978c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "only-if-cached");
979c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertBadGateway(connection);
980c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
981c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
98284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testRequestCacheControlNoCache() throws Exception {
98384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse()
98484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
98584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
98684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Cache-Control: max-age=60")
98784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .setBody("A"));
98884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
98984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
99084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
99184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URL url = server.getUrl("/");
99284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
99384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URLConnection connection = url.openConnection();
99484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.setRequestProperty("Cache-Control", "no-cache");
99584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("B", readAscii(connection));
99684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
99784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
99884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testRequestPragmaNoCache() throws Exception {
99984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse()
100084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
100184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
100284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Cache-Control: max-age=60")
100384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .setBody("A"));
100484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
100584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
100684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
100784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URL url = server.getUrl("/");
100884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
100984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URLConnection connection = url.openConnection();
101084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.setRequestProperty("Pragma", "no-cache");
101184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("B", readAscii(connection));
101284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
101384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
101484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testClientSuppliedIfModifiedSinceWithCachedResult() throws Exception {
101584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        MockResponse response = new MockResponse()
101684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("ETag: v3")
101784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Cache-Control: max-age=0");
101884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        String ifModifiedSinceDate = formatDate(-24, TimeUnit.HOURS);
101984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        RecordedRequest request = assertClientSuppliedCondition(
102084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                response, "If-Modified-Since", ifModifiedSinceDate);
102184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        List<String> headers = request.getHeaders();
102284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + ifModifiedSinceDate));
102384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertFalse(headers.contains("If-None-Match: v3"));
102484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
102584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
102684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testClientSuppliedIfNoneMatchSinceWithCachedResult() throws Exception {
102784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        String lastModifiedDate = formatDate(-3, TimeUnit.MINUTES);
102884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        MockResponse response = new MockResponse()
102984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
103084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
103184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Cache-Control: max-age=0");
103284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        RecordedRequest request = assertClientSuppliedCondition(
103384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                response, "If-None-Match", "v1");
103484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        List<String> headers = request.getHeaders();
103584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertTrue(headers.contains("If-None-Match: v1"));
103684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertFalse(headers.contains("If-Modified-Since: " + lastModifiedDate));
103784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
103884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
103984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    private RecordedRequest assertClientSuppliedCondition(MockResponse seed, String conditionName,
104084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            String conditionValue) throws Exception {
104184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(seed.setBody("A"));
104284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
104384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
104484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
104584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URL url = server.getUrl("/");
104684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
104784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
104884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
104984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.addRequestProperty(conditionName, conditionValue);
105084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, connection.getResponseCode());
105184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("", readAscii(connection));
105284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
105384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.takeRequest(); // seed
105484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        return server.takeRequest();
105584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
105684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
10579531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    public void testSetIfModifiedSince() throws Exception {
10589531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        Date since = new Date();
10599531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        server.enqueue(new MockResponse().setBody("A"));
10609531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        server.play();
10619531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson
10629531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        URL url = server.getUrl("/");
10639531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        URLConnection connection = url.openConnection();
10649531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        connection.setIfModifiedSince(since.getTime());
10659531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        assertEquals("A", readAscii(connection));
10669531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        RecordedRequest request = server.takeRequest();
10679531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        assertTrue(request.getHeaders().contains("If-Modified-Since: " + formatDate(since)));
10689531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    }
10699531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson
107084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testClientSuppliedConditionWithoutCachedResult() throws Exception {
107184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse()
107284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
107384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
107484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
107584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
107684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        String clientIfModifiedSince = formatDate(-24, TimeUnit.HOURS);
107784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.addRequestProperty("If-Modified-Since", clientIfModifiedSince);
107884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, connection.getResponseCode());
107984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("", readAscii(connection));
108084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
1081c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
108221dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testAuthorizationRequestHeaderPreventsCaching() throws Exception {
108321dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse()
108421dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.MINUTES))
108521dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: max-age=60")
108621dddca4064527116af7a1553de502c6d11138daJesse Wilson                .setBody("A"));
108721dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
108821dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.play();
108921dddca4064527116af7a1553de502c6d11138daJesse Wilson
109021dddca4064527116af7a1553de502c6d11138daJesse Wilson        URL url = server.getUrl("/");
1091953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = url.openConnection();
109221dddca4064527116af7a1553de502c6d11138daJesse Wilson        connection.addRequestProperty("Authorization", "password");
109321dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("A", readAscii(connection));
109421dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("B", readAscii(url.openConnection()));
109521dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
109621dddca4064527116af7a1553de502c6d11138daJesse Wilson
109721dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testAuthorizationResponseCachedWithSMaxAge() throws Exception {
109821dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertAuthorizationRequestFullyCached(new MockResponse()
109921dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: s-maxage=60"));
110021dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
110121dddca4064527116af7a1553de502c6d11138daJesse Wilson
110221dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testAuthorizationResponseCachedWithPublic() throws Exception {
110321dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertAuthorizationRequestFullyCached(new MockResponse()
110421dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: public"));
110521dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
110621dddca4064527116af7a1553de502c6d11138daJesse Wilson
110721dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testAuthorizationResponseCachedWithMustRevalidate() throws Exception {
110821dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertAuthorizationRequestFullyCached(new MockResponse()
110921dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: must-revalidate"));
111021dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
111121dddca4064527116af7a1553de502c6d11138daJesse Wilson
111221dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void assertAuthorizationRequestFullyCached(MockResponse response) throws Exception {
111321dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(response
111421dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: max-age=60")
111521dddca4064527116af7a1553de502c6d11138daJesse Wilson                .setBody("A"));
111621dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
111721dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.play();
111821dddca4064527116af7a1553de502c6d11138daJesse Wilson
111921dddca4064527116af7a1553de502c6d11138daJesse Wilson        URL url = server.getUrl("/");
1120953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = url.openConnection();
112121dddca4064527116af7a1553de502c6d11138daJesse Wilson        connection.addRequestProperty("Authorization", "password");
112221dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("A", readAscii(connection));
112321dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("A", readAscii(url.openConnection()));
112421dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
112521dddca4064527116af7a1553de502c6d11138daJesse Wilson
1126953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public void testVaryResponsesAreNotSupported() throws Exception {
1127953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse()
1128953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Cache-Control: max-age=60")
1129953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Vary: Accept-Language")
1130953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .setBody("A"));
1131953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
1132953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.play();
1133953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
1134953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URL url = server.getUrl("/");
1135953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection1 = url.openConnection();
1136953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        connection1.addRequestProperty("Accept-Language", "fr-CA");
1137953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(connection1));
1138953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
1139953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection2 = url.openConnection();
1140953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        connection2.addRequestProperty("Accept-Language", "fr-CA");
1141953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("B", readAscii(connection2));
1142953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
1143953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
1144953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public void testContentLocationDoesNotPopulateCache() throws Exception {
1145953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse()
1146953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Cache-Control: max-age=60")
1147953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Content-Location: /bar")
1148953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .setBody("A"));
1149953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
1150953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.play();
1151953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
1152953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/foo").openConnection()));
1153953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("B", readAscii(server.getUrl("/bar").openConnection()));
1154953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
1155953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
115619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    public void testUseCachesFalseDoesNotWriteToCache() throws Exception {
115719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.enqueue(new MockResponse()
115819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson                .addHeader("Cache-Control: max-age=60")
115919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson                .setBody("A").setBody("A"));
116019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
116119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.play();
116219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
116319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
116419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        connection.setUseCaches(false);
116519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertEquals("A", readAscii(connection));
116619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertEquals("B", readAscii(server.getUrl("/").openConnection()));
116719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    }
116819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
116919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    public void testUseCachesFalseDoesNotReadFromCache() throws Exception {
117019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.enqueue(new MockResponse()
117119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson                .addHeader("Cache-Control: max-age=60")
117219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson                .setBody("A").setBody("A"));
117319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
117419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.play();
117519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
117619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
117719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
117819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        connection.setUseCaches(false);
117919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertEquals("B", readAscii(connection));
118019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    }
118119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
118219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    public void testDefaultUseCachesSetsInitialValueOnly() throws Exception {
118319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URL url = new URL("http://localhost/");
118419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URLConnection c1 = url.openConnection();
118519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URLConnection c2 = url.openConnection();
118619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertTrue(c1.getDefaultUseCaches());
118719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        c1.setDefaultUseCaches(false);
118819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        try {
118919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            assertTrue(c1.getUseCaches());
119019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            assertTrue(c2.getUseCaches());
119119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            URLConnection c3 = url.openConnection();
119219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            assertFalse(c3.getUseCaches());
119319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        } finally {
119419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            c1.setDefaultUseCaches(true);
119519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        }
119619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    }
119719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
11982f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    public void testConnectionIsReturnedToPoolAfterConditionalSuccess() throws Exception {
11992f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse()
12002f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
12012f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .addHeader("Cache-Control: max-age=0")
12022f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .setBody("A"));
12032f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
12042f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
12052f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.play();
12062f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
12072f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/a").openConnection()));
12082f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/a").openConnection()));
12092f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("B", readAscii(server.getUrl("/b").openConnection()));
12102f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
12112f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals(0, server.takeRequest().getSequenceNumber());
12122f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals(1, server.takeRequest().getSequenceNumber());
12132f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals(2, server.takeRequest().getSequenceNumber());
12142f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    }
12152f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
1216757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    public void testStatisticsConditionalCacheMiss() throws Exception {
1217757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse()
1218757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1219757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Cache-Control: max-age=0")
1220757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .setBody("A"));
1221757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
1222757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse().setBody("C"));
1223757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.play();
1224757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1225757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1226ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getRequestCount());
1227ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getNetworkCount());
1228ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getHitCount());
1229757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("B", readAscii(server.getUrl("/").openConnection()));
1230757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("C", readAscii(server.getUrl("/").openConnection()));
1231ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(3, cache.getRequestCount());
1232ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(3, cache.getNetworkCount());
1233ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getHitCount());
1234757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    }
1235757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1236757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    public void testStatisticsConditionalCacheHit() throws Exception {
1237757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse()
1238757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1239757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Cache-Control: max-age=0")
1240757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .setBody("A"));
1241757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1242757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1243757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.play();
1244757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1245757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1246ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getRequestCount());
1247ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getNetworkCount());
1248ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getHitCount());
1249757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1250757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1251ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(3, cache.getRequestCount());
1252ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(3, cache.getNetworkCount());
1253ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getHitCount());
1254757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    }
1255757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1256757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    public void testStatisticsFullCacheHit() throws Exception {
1257757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse()
1258757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Cache-Control: max-age=60")
1259757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .setBody("A"));
1260757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.play();
1261757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1262757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1263ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getRequestCount());
1264ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getNetworkCount());
1265ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getHitCount());
1266757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1267757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1268ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(3, cache.getRequestCount());
1269ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getNetworkCount());
1270ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getHitCount());
1271757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    }
1272757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
12730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
12740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * @param delta the offset from the current date to use. Negative
12750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     *     values yield dates in the past; positive values yield dates in the
12760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     *     future.
12770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
1278953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private String formatDate(long delta, TimeUnit timeUnit) {
12799531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        return formatDate(new Date(System.currentTimeMillis() + timeUnit.toMillis(delta)));
12809531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    }
12819531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson
12829531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    private String formatDate(Date date) {
1283953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        DateFormat rfc1123 = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
1284953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        rfc1123.setTimeZone(TimeZone.getTimeZone("UTC"));
1285953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        return rfc1123.format(date);
12860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
12870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
12880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void addRequestBodyIfNecessary(String requestMethod, HttpURLConnection invalidate)
12890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            throws IOException {
12900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        if (requestMethod.equals("POST") || requestMethod.equals("PUT")) {
12910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            invalidate.setDoOutput(true);
12920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            OutputStream requestBody = invalidate.getOutputStream();
12930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            requestBody.write('x');
12940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            requestBody.close();
12950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
12960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
12970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
12980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void assertNotCached(MockResponse response) throws Exception {
12990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response.setBody("A"));
13000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
13010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
13020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
13040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
13050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("B", readAscii(url.openConnection()));
13060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
13070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
13090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * @return the request with the conditional get headers.
13100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
13110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private RecordedRequest assertConditionallyCached(MockResponse response) throws Exception {
13120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // scenario 1: condition succeeds
13130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response.setBody("A"));
13140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
13150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // scenario 2: condition fails
13170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response.setBody("B"));
13180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("C"));
13190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
13210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL valid = server.getUrl("/valid");
13230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(valid.openConnection()));
13240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(valid.openConnection()));
13250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL invalid = server.getUrl("/invalid");
13270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("B", readAscii(invalid.openConnection()));
13280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("C", readAscii(invalid.openConnection()));
13290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.takeRequest(); // regular get
13310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return server.takeRequest(); // conditional get
13320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
13330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void assertFullyCached(MockResponse response) throws Exception {
13350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response.setBody("A"));
13360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response.setBody("B"));
13370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
13380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
13400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
13410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
13420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
13430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
13450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Shortens the body of {@code response} but not the corresponding headers.
13460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Only useful to test how clients respond to the premature conclusion of
13470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * the HTTP body.
13480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
13490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private MockResponse truncateViolently(MockResponse response, int numBytesToKeep) {
13500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        response.setSocketPolicy(DISCONNECT_AT_END);
13510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = new ArrayList<String>(response.getHeaders());
13520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        response.setBody(Arrays.copyOfRange(response.getBody(), 0, numBytesToKeep));
13530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        response.getHeaders().clear();
13540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        response.getHeaders().addAll(headers);
13550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return response;
13560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
13570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
13590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Reads {@code count} characters from the stream. If the stream is
13600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * exhausted before {@code count} characters can be read, the remaining
13610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * characters are returned and the stream is closed.
13620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
13630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private String readAscii(URLConnection connection, int count) throws IOException {
136484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpURLConnection httpConnection = (HttpURLConnection) connection;
136584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        InputStream in = httpConnection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST
136684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                ? connection.getInputStream()
136784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                : httpConnection.getErrorStream();
13680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        StringBuilder result = new StringBuilder();
13690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        for (int i = 0; i < count; i++) {
13700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            int value = in.read();
13710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            if (value == -1) {
13720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                in.close();
13730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                break;
13740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
13750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            result.append((char) value);
13760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
13770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return result.toString();
13780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
13790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private String readAscii(URLConnection connection) throws IOException {
13810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return readAscii(connection, Integer.MAX_VALUE);
13820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
13830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void reliableSkip(InputStream in, int length) throws IOException {
13850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        while (length > 0) {
13860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            length -= in.skip(length);
13870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
13880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
13890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1390953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private void assertBadGateway(HttpURLConnection connection) throws IOException {
1391c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        try {
1392c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson            connection.getInputStream();
1393c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson            fail();
1394c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        } catch (FileNotFoundException expected) {
1395c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        }
1396953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals(HttpURLConnection.HTTP_BAD_GATEWAY, connection.getResponseCode());
1397953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals(-1, connection.getErrorStream().read());
1398c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
1399c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
14000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    enum TransferKind {
14010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        CHUNKED() {
14020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override void setBody(MockResponse response, byte[] content, int chunkSize)
14030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    throws IOException {
14040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                response.setChunkedBody(content, chunkSize);
14050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
14060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        },
14070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        FIXED_LENGTH() {
14080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override void setBody(MockResponse response, byte[] content, int chunkSize) {
14090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                response.setBody(content);
14100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
14110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        },
14120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        END_OF_STREAM() {
14130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override void setBody(MockResponse response, byte[] content, int chunkSize) {
14140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                response.setBody(content);
14150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                response.setSocketPolicy(DISCONNECT_AT_END);
14160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                for (Iterator<String> h = response.getHeaders().iterator(); h.hasNext(); ) {
14170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    if (h.next().startsWith("Content-Length:")) {
14180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                        h.remove();
14190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                        break;
14200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    }
14210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                }
14220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
14230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        };
14240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        abstract void setBody(MockResponse response, byte[] content, int chunkSize)
14260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                throws IOException;
14270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        void setBody(MockResponse response, String content, int chunkSize) throws IOException {
14290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            setBody(response, content.getBytes("UTF-8"), chunkSize);
14300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
14310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
14320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private <T> List<T> toListOrNull(T[] arrayOrNull) {
14340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return arrayOrNull != null ? Arrays.asList(arrayOrNull) : null;
14350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
14360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
14380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Returns a gzipped copy of {@code bytes}.
14390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
14400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public byte[] gzip(byte[] bytes) throws IOException {
14410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
14420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        OutputStream gzippedOut = new GZIPOutputStream(bytesOut);
14430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        gzippedOut.write(bytes);
14440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        gzippedOut.close();
14450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return bytesOut.toByteArray();
14460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
14470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1448433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson    private class InsecureResponseCache extends ResponseCache {
14490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        @Override public CacheRequest put(URI uri, URLConnection connection) throws IOException {
1450433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            return cache.put(uri, connection);
14510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
14520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        @Override public CacheResponse get(URI uri, String requestMethod,
14540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                Map<String, List<String>> requestHeaders) throws IOException {
1455433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            final CacheResponse response = cache.get(uri, requestMethod, requestHeaders);
14560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            if (response instanceof SecureCacheResponse) {
14570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                return new CacheResponse() {
14580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    @Override public InputStream getBody() throws IOException {
14590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                        return response.getBody();
14600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    }
14610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    @Override public Map<String, List<String>> getHeaders() throws IOException {
14620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                        return response.getHeaders();
14630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    }
14640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                };
14650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
14660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            return response;
14670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
14680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
14690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson}
1470