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
1909336c914b4fc813e493acc82469b9ad89fd8694Jesse Wilsonimport com.google.mockwebserver.MockResponse;
2009336c914b4fc813e493acc82469b9ad89fd8694Jesse Wilsonimport com.google.mockwebserver.MockWebServer;
2109336c914b4fc813e493acc82469b9ad89fd8694Jesse Wilsonimport com.google.mockwebserver.RecordedRequest;
2209336c914b4fc813e493acc82469b9ad89fd8694Jesse Wilsonimport static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_END;
230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.BufferedReader;
240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.ByteArrayOutputStream;
25433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilsonimport java.io.File;
26c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilsonimport java.io.FileNotFoundException;
270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.IOException;
280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.InputStream;
290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.InputStreamReader;
300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.OutputStream;
31e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilsonimport java.lang.reflect.InvocationHandler;
320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.CacheRequest;
330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.CacheResponse;
34cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilsonimport java.net.CookieHandler;
35cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilsonimport java.net.CookieManager;
36cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilsonimport java.net.HttpCookie;
370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.HttpURLConnection;
380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.ResponseCache;
390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.SecureCacheResponse;
400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.URI;
410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.URISyntaxException;
420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.URL;
430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.URLConnection;
440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.security.Principal;
450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.security.cert.Certificate;
46953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.text.DateFormat;
47953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.text.SimpleDateFormat;
480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.ArrayList;
490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.Arrays;
500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.Collections;
510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.Date;
52e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilsonimport java.util.Deque;
530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.Iterator;
540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.List;
55953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.util.Locale;
560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.Map;
57953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.util.TimeZone;
58433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilsonimport java.util.UUID;
590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.concurrent.TimeUnit;
600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.concurrent.atomic.AtomicInteger;
610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.concurrent.atomic.AtomicReference;
620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.zip.GZIPOutputStream;
630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport javax.net.ssl.HttpsURLConnection;
640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport junit.framework.TestCase;
650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport libcore.javax.net.ssl.TestSSLContext;
66e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilsonimport tests.io.MockOs;
670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
68396af46f45fd302e6309fc48ac1fd68cbb739edbJesse Wilsonpublic final class HttpResponseCacheTest extends TestCase {
690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private MockWebServer server = new MockWebServer();
70396af46f45fd302e6309fc48ac1fd68cbb739edbJesse Wilson    private HttpResponseCache cache;
71cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson    private final MockOs mockOs = new MockOs();
72cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson    private final CookieManager cookieManager = new CookieManager();
730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    @Override protected void setUp() throws Exception {
750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        super.setUp();
76433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson
77433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        String tmp = System.getProperty("java.io.tmpdir");
78433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        File cacheDir = new File(tmp, "HttpCache-" + UUID.randomUUID());
79396af46f45fd302e6309fc48ac1fd68cbb739edbJesse Wilson        cache = new HttpResponseCache(cacheDir, Integer.MAX_VALUE);
800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        ResponseCache.setDefault(cache);
81e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        mockOs.install();
82cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        CookieHandler.setDefault(cookieManager);
830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    @Override protected void tearDown() throws Exception {
86e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        mockOs.uninstall();
870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.shutdown();
88433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        ResponseCache.setDefault(null);
89fb4a6392a04b1f3a1124b3db6bae51d8cbfa53f8Jesse Wilson        cache.getCache().delete();
90cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        CookieHandler.setDefault(null);
910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        super.tearDown();
920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Test that response caching is consistent with the RI and the spec.
960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4
970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
9884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testResponseCachingByResponseCode() throws Exception {
990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // Test each documented HTTP/1.1 code, plus the first unused value in each range.
1000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
1010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // We can't test 100 because it's not really a response.
1030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // assertCached(false, 100);
1040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 101);
1050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 102);
1060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(true,  200);
1070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 201);
1080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 202);
1090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(true,  203);
1100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 204);
1110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 205);
1124bc4b39a74774099b381ec9fd9ee0e2691ee7650Jesse Wilson        assertCached(false, 206); // we don't cache partial responses
1130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 207);
11484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertCached(true,  300);
1150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(true,  301);
1160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        for (int i = 302; i <= 308; ++i) {
1170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertCached(false, i);
1180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        for (int i = 400; i <= 406; ++i) {
1200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertCached(false, i);
1210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // (See test_responseCaching_407.)
1230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 408);
1240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 409);
1250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // (See test_responseCaching_410.)
1260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        for (int i = 411; i <= 418; ++i) {
1270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertCached(false, i);
1280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        for (int i = 500; i <= 506; ++i) {
1300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertCached(false, i);
1310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
1330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
1350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Response code 407 should only come from proxy servers. Android's client
1360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * throws if it is sent by an origin server.
1370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
1380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testOriginServerSends407() throws Exception {
1390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(407));
1400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
1410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
1430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
1440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
1450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            conn.getResponseCode();
1460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail();
1470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (IOException expected) {
1480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
1500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void test_responseCaching_410() throws Exception {
1520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // the HTTP spec permits caching 410s, but the RI doesn't.
15384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertCached(true, 410);
1540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
1550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void assertCached(boolean shouldPut, int responseCode) throws Exception {
1570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server = new MockWebServer();
1585d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        MockResponse response = new MockResponse()
1590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
1610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setResponseCode(responseCode)
1620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("ABCDE")
1635d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson                .addHeader("WWW-Authenticate: challenge");
1645d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        if (responseCode == HttpURLConnection.HTTP_PROXY_AUTH) {
1655d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson            response.addHeader("Proxy-Authenticate: Basic realm=\"protected area\"");
1665d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        } else if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
1675d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson            response.addHeader("WWW-Authenticate: Basic realm=\"protected area\"");
1685d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        }
1695d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        server.enqueue(response);
1700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
1710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
1730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
1740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(responseCode, conn.getResponseCode());
1750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // exhaust the content stream
17784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        readAscii(conn);
1780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
179433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        CacheResponse cached = cache.get(url.toURI(), "GET",
180433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson                Collections.<String, List<String>>emptyMap());
181433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        if (shouldPut) {
182433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            assertNotNull(Integer.toString(responseCode), cached);
183433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            cached.getBody().close();
184433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        } else {
185433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            assertNull(Integer.toString(responseCode), cached);
186433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        }
1870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.shutdown(); // tearDown() isn't sufficient; this test starts multiple servers
1880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
1890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
1910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Test that we can interrogate the response when the cache is being
1920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * populated. http://code.google.com/p/android/issues/detail?id=7787
1930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
1940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponseCacheCallbackApis() throws Exception {
1950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        final String body = "ABCDE";
1960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        final AtomicInteger cacheCount = new AtomicInteger();
1970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
1990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setStatus("HTTP/1.1 200 Fantastic")
2000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("fgh: ijk")
2010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody(body));
2020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
2030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        ResponseCache.setDefault(new ResponseCache() {
2050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override public CacheResponse get(URI uri, String requestMethod,
2060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    Map<String, List<String>> requestHeaders) throws IOException {
2070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                return null;
2080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
2090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override public CacheRequest put(URI uri, URLConnection conn) throws IOException {
2100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                HttpURLConnection httpConnection = (HttpURLConnection) conn;
211953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                try {
212953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                    httpConnection.getRequestProperties();
213953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                    fail();
214953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                } catch (IllegalStateException expected) {
215953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                }
216953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                try {
217953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                    httpConnection.addRequestProperty("K", "V");
218953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                    fail();
219953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                } catch (IllegalStateException expected) {
220953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                }
2210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                assertEquals("HTTP/1.1 200 Fantastic", httpConnection.getHeaderField(null));
2220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                assertEquals(Arrays.asList("HTTP/1.1 200 Fantastic"),
2230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                        httpConnection.getHeaderFields().get(null));
2240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                assertEquals(200, httpConnection.getResponseCode());
2250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                assertEquals("Fantastic", httpConnection.getResponseMessage());
2260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                assertEquals(body.length(), httpConnection.getContentLength());
2270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                assertEquals("ijk", httpConnection.getHeaderField("fgh"));
2280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                try {
2290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    httpConnection.getInputStream(); // the RI doesn't forbid this, but it should
2300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    fail();
2310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                } catch (IOException expected) {
2320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                }
2330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                cacheCount.incrementAndGet();
2340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                return null;
2350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
2360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        });
2370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
239953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = url.openConnection();
2400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(body, readAscii(connection));
2410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(1, cacheCount.get());
2420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponseCachingAndInputStreamSkipWithFixedLength() throws IOException {
2460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testResponseCaching(TransferKind.FIXED_LENGTH);
2470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponseCachingAndInputStreamSkipWithChunkedEncoding() throws IOException {
2500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testResponseCaching(TransferKind.CHUNKED);
2510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponseCachingAndInputStreamSkipWithNoLengthHeaders() throws IOException {
2540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testResponseCaching(TransferKind.END_OF_STREAM);
2550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
2580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * HttpURLConnection.getInputStream().skip(long) causes ResponseCache corruption
2590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * http://code.google.com/p/android/issues/detail?id=8175
2600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
2610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testResponseCaching(TransferKind transferKind) throws IOException {
2620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        MockResponse response = new MockResponse()
2630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
2640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
2650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setStatus("HTTP/1.1 200 Fantastic");
2660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        transferKind.setBody(response, "I love puppies but hate spiders", 1);
2670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
2680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
2690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // Make sure that calling skip() doesn't omit bytes from the cache.
2710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection();
2720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        InputStream in = urlConnection.getInputStream();
2730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("I love ", readAscii(urlConnection, "I love ".length()));
2740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        reliableSkip(in, "puppies but hate ".length());
2750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("spiders", readAscii(urlConnection, "spiders".length()));
2760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(-1, in.read());
2770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        in.close();
278ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteSuccessCount());
279ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getWriteAbortCount());
2800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        urlConnection = (HttpURLConnection) server.getUrl("/").openConnection(); // cached!
2820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        in = urlConnection.getInputStream();
2830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("I love puppies but hate spiders",
2840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                readAscii(urlConnection, "I love puppies but hate spiders".length()));
2850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(200, urlConnection.getResponseCode());
2860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("Fantastic", urlConnection.getResponseMessage());
2870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(-1, in.read());
289433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        in.close();
290ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteSuccessCount());
291ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getWriteAbortCount());
292ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getRequestCount());
293ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getHitCount());
2940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testSecureResponseCaching() throws IOException {
2970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
2980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
2990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
3000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
3010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
3020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("ABC"));
3030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
3040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
3060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
3070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
3080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // OpenJDK 6 fails on this line, complaining that the connection isn't open yet
3100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String suite = connection.getCipherSuite();
3110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<Certificate> localCerts = toListOrNull(connection.getLocalCertificates());
3120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<Certificate> serverCerts = toListOrNull(connection.getServerCertificates());
3130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        Principal peerPrincipal = connection.getPeerPrincipal();
3140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        Principal localPrincipal = connection.getLocalPrincipal();
3150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection = (HttpsURLConnection) server.getUrl("/").openConnection(); // cached!
3170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
3180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
3190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
320ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getRequestCount());
321ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getNetworkCount());
322ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getHitCount());
3230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(suite, connection.getCipherSuite());
3250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(localCerts, toListOrNull(connection.getLocalCertificates()));
3260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(serverCerts, toListOrNull(connection.getServerCertificates()));
3270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(peerPrincipal, connection.getPeerPrincipal());
3280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(localPrincipal, connection.getLocalPrincipal());
3290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
3300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheReturnsInsecureResponseForSecureRequest() throws IOException {
3320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
3330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
3340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("ABC"));
3350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("DEF"));
3360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
3370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        ResponseCache.setDefault(new InsecureResponseCache());
3390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
3410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
3420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
3430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection = (HttpsURLConnection) server.getUrl("/").openConnection(); // not cached!
3450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
3460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("DEF", readAscii(connection));
3470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
3480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
349a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson    public void testResponseCachingAndRedirects() throws Exception {
3500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
3510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
3520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
3530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
3540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Location: /foo"));
3550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
3560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
3570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
3580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("ABC"));
3590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("DEF"));
3600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
3610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
362953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
3630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
3640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
365953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        connection = server.getUrl("/").openConnection(); // cached!
3660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
3670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
368ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(4, cache.getRequestCount()); // 2 requests + 2 redirects
369ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getNetworkCount());
370ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getHitCount());
3710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
3720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
373a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson    public void testRedirectToCachedResult() throws Exception {
374a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        server.enqueue(new MockResponse()
375a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson                .addHeader("Cache-Control: max-age=60")
376a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson                .setBody("ABC"));
377a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        server.enqueue(new MockResponse()
378a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
379a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson                .addHeader("Location: /foo"));
380a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        server.enqueue(new MockResponse().setBody("DEF"));
381a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        server.play();
382a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
383a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("ABC", readAscii(server.getUrl("/foo").openConnection()));
384a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        RecordedRequest request1 = server.takeRequest();
385a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("GET /foo HTTP/1.1", request1.getRequestLine());
386a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals(0, request1.getSequenceNumber());
387a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
388a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("ABC", readAscii(server.getUrl("/bar").openConnection()));
389a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        RecordedRequest request2 = server.takeRequest();
390a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("GET /bar HTTP/1.1", request2.getRequestLine());
391a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals(1, request2.getSequenceNumber());
392a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
393a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        // an unrelated request should reuse the pooled connection
394a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("DEF", readAscii(server.getUrl("/baz").openConnection()));
395a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        RecordedRequest request3 = server.takeRequest();
396a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("GET /baz HTTP/1.1", request3.getRequestLine());
397a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals(2, request3.getSequenceNumber());
398a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson    }
399a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
4000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testSecureResponseCachingAndRedirects() throws IOException {
4010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
4020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
4030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
4040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
4050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
4060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
4070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Location: /foo"));
4080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
4090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
4100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
4110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("ABC"));
4120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("DEF"));
4130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
4140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
4160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
4170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
4180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection = (HttpsURLConnection) server.getUrl("/").openConnection(); // cached!
4200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
4210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
4220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
423ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(4, cache.getRequestCount()); // 2 direct + 2 redirect = 4
424ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getHitCount());
4250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponseCacheRequestHeaders() throws IOException, URISyntaxException {
4280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("ABC"));
4290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
4300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        final AtomicReference<Map<String, List<String>>> requestHeadersRef
4320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                = new AtomicReference<Map<String, List<String>>>();
4330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        ResponseCache.setDefault(new ResponseCache() {
4340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override public CacheResponse get(URI uri, String requestMethod,
4350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    Map<String, List<String>> requestHeaders) throws IOException {
4360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                requestHeadersRef.set(requestHeaders);
4370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                return null;
4380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
4390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override public CacheRequest put(URI uri, URLConnection conn) throws IOException {
4400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                return null;
4410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
4420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        });
4430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
4450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection urlConnection = url.openConnection();
4460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        urlConnection.addRequestProperty("A", "android");
4470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        readAscii(urlConnection);
4480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(Arrays.asList("android"), requestHeadersRef.get().get("A"));
4490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testServerDisconnectsPrematurelyWithContentLengthHeader() throws IOException {
4530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testServerPrematureDisconnect(TransferKind.FIXED_LENGTH);
4540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testServerDisconnectsPrematurelyWithChunkedEncoding() throws IOException {
4570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testServerPrematureDisconnect(TransferKind.CHUNKED);
4580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testServerDisconnectsPrematurelyWithNoLengthHeaders() throws IOException {
4610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        /*
4620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * Intentionally empty. This case doesn't make sense because there's no
4630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * such thing as a premature disconnect when the disconnect itself
4640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * indicates the end of the data stream.
4650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         */
4660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testServerPrematureDisconnect(TransferKind transferKind) throws IOException {
4690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        MockResponse response = new MockResponse();
4700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        transferKind.setBody(response, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 16);
4710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(truncateViolently(response, 16));
4720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("Request #2"));
4730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
4740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        BufferedReader reader = new BufferedReader(new InputStreamReader(
4760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                server.getUrl("/").openConnection().getInputStream()));
4770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCDE", reader.readLine());
4780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
4790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            reader.readLine();
4800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail("This implementation silently ignored a truncated HTTP body.");
4810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (IOException expected) {
482433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        } finally {
483433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            reader.close();
4840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
4850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
486ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteAbortCount());
487ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getWriteSuccessCount());
4880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
4890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("Request #2", readAscii(connection));
490ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteAbortCount());
491ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteSuccessCount());
4920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testClientPrematureDisconnectWithContentLengthHeader() throws IOException {
4950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testClientPrematureDisconnect(TransferKind.FIXED_LENGTH);
4960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testClientPrematureDisconnectWithChunkedEncoding() throws IOException {
4990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testClientPrematureDisconnect(TransferKind.CHUNKED);
5000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
5020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testClientPrematureDisconnectWithNoLengthHeaders() throws IOException {
5030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testClientPrematureDisconnect(TransferKind.END_OF_STREAM);
5040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
5060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testClientPrematureDisconnect(TransferKind transferKind) throws IOException {
5070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        MockResponse response = new MockResponse();
5080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        transferKind.setBody(response, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 1024);
5090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
5100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("Request #2"));
5110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
5120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
5130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
5140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        InputStream in = connection.getInputStream();
5150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCDE", readAscii(connection, 5));
5160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        in.close();
5170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
5180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            in.read();
5190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail("Expected an IOException because the stream is closed.");
5200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (IOException expected) {
5210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
5220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
523ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteAbortCount());
524ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getWriteSuccessCount());
5250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection = server.getUrl("/").openConnection();
52684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("Request #2", readAscii(connection));
527ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteAbortCount());
528ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteSuccessCount());
5290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
531953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public void testDefaultExpirationDateFullyCachedForLessThan24Hours() throws Exception {
53221dddca4064527116af7a1553de502c6d11138daJesse Wilson        //      last modified: 105 seconds ago
53321dddca4064527116af7a1553de502c6d11138daJesse Wilson        //             served:   5 seconds ago
53421dddca4064527116af7a1553de502c6d11138daJesse Wilson        //   default lifetime: (105 - 5) / 10 = 10 seconds
53521dddca4064527116af7a1553de502c6d11138daJesse Wilson        //            expires:  10 seconds from served date = 5 seconds from now
536953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse()
53721dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Last-Modified: " + formatDate(-105, TimeUnit.SECONDS))
538953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Date: " + formatDate(-5, TimeUnit.SECONDS))
539953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .setBody("A"));
540953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.play();
541953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
542953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URL url = server.getUrl("/");
543953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
544953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = url.openConnection();
545953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(connection));
546953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertNull(connection.getHeaderField("Warning"));
54721dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
54821dddca4064527116af7a1553de502c6d11138daJesse Wilson
54921dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testDefaultExpirationDateConditionallyCached() throws Exception {
55021dddca4064527116af7a1553de502c6d11138daJesse Wilson        //      last modified: 115 seconds ago
55121dddca4064527116af7a1553de502c6d11138daJesse Wilson        //             served:  15 seconds ago
55221dddca4064527116af7a1553de502c6d11138daJesse Wilson        //   default lifetime: (115 - 15) / 10 = 10 seconds
55321dddca4064527116af7a1553de502c6d11138daJesse Wilson        //            expires:  10 seconds from served date = 5 seconds ago
55421dddca4064527116af7a1553de502c6d11138daJesse Wilson        String lastModifiedDate = formatDate(-115, TimeUnit.SECONDS);
5550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
55621dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
55721dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Date: " + formatDate(-15, TimeUnit.SECONDS)));
5580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
5590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
5600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
562953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public void testDefaultExpirationDateFullyCachedForMoreThan24Hours() throws Exception {
563953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        //      last modified: 105 days ago
564953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        //             served:   5 days ago
565953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        //   default lifetime: (105 - 5) / 10 = 10 days
566953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        //            expires:  10 days from served date = 5 days from now
567953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse()
568953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-105, TimeUnit.DAYS))
569953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Date: " + formatDate(-5, TimeUnit.DAYS))
570953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .setBody("A"));
571953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.play();
572953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
573953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
574953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
575953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(connection));
576953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("113 HttpURLConnection \"Heuristic expiration\"",
577953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                connection.getHeaderField("Warning"));
578953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
579953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
58021dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testNoDefaultExpirationForUrlsWithQueryString() throws Exception {
58121dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse()
58221dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Last-Modified: " + formatDate(-105, TimeUnit.SECONDS))
58321dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Date: " + formatDate(-5, TimeUnit.SECONDS))
58421dddca4064527116af7a1553de502c6d11138daJesse Wilson                .setBody("A"));
58521dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
58621dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.play();
58721dddca4064527116af7a1553de502c6d11138daJesse Wilson
58821dddca4064527116af7a1553de502c6d11138daJesse Wilson        URL url = server.getUrl("/?foo=bar");
58921dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("A", readAscii(url.openConnection()));
59021dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("B", readAscii(url.openConnection()));
59121dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
59221dddca4064527116af7a1553de502c6d11138daJesse Wilson
5930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testExpirationDateInThePastWithLastModifiedHeader() throws Exception {
5940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
5950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
5960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
5970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
5980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
5990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
6000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testExpirationDateInThePastWithNoLastModifiedHeader() throws Exception {
6030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse()
6040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
6050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testExpirationDateInTheFuture() throws Exception {
6080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
6090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
6100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgePreferredWithMaxAgeAndExpires() throws Exception {
6130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
6140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
6150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS))
6160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
6170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgeInThePastWithDateAndLastModifiedHeaders() throws Exception {
6200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
6210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
6220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Date: " + formatDate(-120, TimeUnit.SECONDS))
6230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
6240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
6250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
6260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
6270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgeInThePastWithDateHeaderButNoLastModifiedHeader() throws Exception {
63084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        /*
63184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson         * Chrome interprets max-age relative to the local clock. Both our cache
63284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson         * and Firefox both use the earlier of the local and server's clock.
63384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson         */
6340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse()
6350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Date: " + formatDate(-120, TimeUnit.SECONDS))
6360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
6370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgeInTheFutureWithDateHeader() throws Exception {
6400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
6410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
6420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
6430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgeInTheFutureWithNoDateHeader() throws Exception {
6460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
6470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
6480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
650adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    public void testMaxAgeWithLastModifiedButNoServedDate() throws Exception {
651adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertFullyCached(new MockResponse()
652adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
653adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
654adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    }
655adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
656adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    public void testMaxAgeInTheFutureWithDateAndLastModifiedHeaders() throws Exception {
657adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertFullyCached(new MockResponse()
658adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
659adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
660adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
661adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    }
662adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
66321dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testMaxAgePreferredOverLowerSharedMaxAge() throws Exception {
66421dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertFullyCached(new MockResponse()
66521dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
66621dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: s-maxage=60")
66721dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: max-age=180"));
66821dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
66921dddca4064527116af7a1553de502c6d11138daJesse Wilson
67021dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testMaxAgePreferredOverHigherMaxAge() throws Exception {
67121dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertNotCached(new MockResponse()
67221dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
67321dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: s-maxage=180")
67421dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: max-age=60"));
67521dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
67621dddca4064527116af7a1553de502c6d11138daJesse Wilson
6770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodOptionsIsNotCached() throws Exception {
6780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("OPTIONS", false);
6790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodGetIsCached() throws Exception {
6820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("GET", true);
6830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodHeadIsNotCached() throws Exception {
6860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // We could support this but choose not to for implementation simplicity
6870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("HEAD", false);
6880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodPostIsNotCached() throws Exception {
6910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // We could support this but choose not to for implementation simplicity
6920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("POST", false);
6930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodPutIsNotCached() throws Exception {
6960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("PUT", false);
6970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodDeleteIsNotCached() throws Exception {
7000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("DELETE", false);
7010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodTraceIsNotCached() throws Exception {
7040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("TRACE", false);
7050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testRequestMethod(String requestMethod, boolean expectCached) throws Exception {
7080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        /*
7090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 1. seed the cache (potentially)
7100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 2. expect a cache hit or miss
7110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         */
7120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
7130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
7140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("X-Response-ID: 1"));
7150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
7160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("X-Response-ID: 2"));
7170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
7180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
7200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection request1 = (HttpURLConnection) url.openConnection();
7220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        request1.setRequestMethod(requestMethod);
7230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        addRequestBodyIfNecessary(requestMethod, request1);
7240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("1", request1.getHeaderField("X-Response-ID"));
7250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
726953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection request2 = url.openConnection();
7270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        if (expectCached) {
7280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertEquals("1", request1.getHeaderField("X-Response-ID"));
7290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } else {
7300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertEquals("2", request2.getHeaderField("X-Response-ID"));
7310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
7320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPostInvalidatesCache() throws Exception {
7350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testMethodInvalidates("POST");
7360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPutInvalidatesCache() throws Exception {
7390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testMethodInvalidates("PUT");
7400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testDeleteMethodInvalidatesCache() throws Exception {
7430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testMethodInvalidates("DELETE");
7440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testMethodInvalidates(String requestMethod) throws Exception {
7470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        /*
7480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 1. seed the cache
7490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 2. invalidate it
7500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 3. expect a cache miss
7510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         */
7520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
7530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
7540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
7550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("C"));
7560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
7570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
7590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
7610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection invalidate = (HttpURLConnection) url.openConnection();
7630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        invalidate.setRequestMethod(requestMethod);
7640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        addRequestBodyIfNecessary(requestMethod, invalidate);
7650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("B", readAscii(invalidate));
7660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("C", readAscii(url.openConnection()));
7680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testEtag() throws Exception {
7710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
7720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("ETag: v1"));
7730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(conditionalRequest.getHeaders().contains("If-None-Match: v1"));
7740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testEtagAndExpirationDateInThePast() throws Exception {
7770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
7780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
7790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("ETag: v1")
7800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
7810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
7820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
7830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-None-Match: v1"));
7840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
7850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testEtagAndExpirationDateInTheFuture() throws Exception {
7880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
7890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("ETag: v1")
7900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
7910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
7920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheControlNoCache() throws Exception {
7950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse().addHeader("Cache-Control: no-cache"));
7960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheControlNoCacheAndExpirationDateInTheFuture() throws Exception {
7990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
8000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
8010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
8020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
8030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: no-cache"));
8040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
8050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
8060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPragmaNoCache() throws Exception {
8090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse().addHeader("Pragma: no-cache"));
8100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPragmaNoCacheAndExpirationDateInTheFuture() throws Exception {
8130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
8140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
8150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
8160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
8170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Pragma: no-cache"));
8180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
8190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
8200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheControlNoStore() throws Exception {
8230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse().addHeader("Cache-Control: no-store"));
8240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheControlNoStoreAndExpirationDateInTheFuture() throws Exception {
8270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse()
8280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
8290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
8300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: no-store"));
8310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPartialRangeResponsesDoNotCorruptCache() throws Exception {
8340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        /*
8350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 1. request a range
8360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 2. request a full document, expecting a cache miss
8370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         */
8380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("AA")
8390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_PARTIAL)
8400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
8410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Content-Range: bytes 1000-1001/2000"));
8420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("BB"));
8430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
8440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
8460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection range = url.openConnection();
8480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        range.addRequestProperty("Range", "bytes=1000-1001");
8490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("AA", readAscii(range));
8500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("BB", readAscii(url.openConnection()));
8520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testServerReturnsDocumentOlderThanCache() throws Exception {
8550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
8560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
8570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
8580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("B")
8590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-4, TimeUnit.HOURS)));
8600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
8610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
8630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
8650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
8660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testNonIdentityEncodingAndConditionalCache() throws Exception {
8690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNonIdentityEncodingCached(new MockResponse()
8700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
8710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
8720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testNonIdentityEncodingAndFullCache() throws Exception {
8750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNonIdentityEncodingCached(new MockResponse()
8760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
8770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
8780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void assertNonIdentityEncodingCached(MockResponse response) throws Exception {
8810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response
8820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody(gzip("ABCABCABC".getBytes("UTF-8")))
8830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Content-Encoding: gzip"));
8840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
8850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
8870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCABCABC", readAscii(server.getUrl("/").openConnection()));
8880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCABCABC", readAscii(server.getUrl("/").openConnection()));
8890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testExpiresDateBeforeModifiedDate() throws Exception {
8920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertConditionallyCached(new MockResponse()
8930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
8940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-2, TimeUnit.HOURS)));
8950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
897adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    public void testRequestMaxAge() throws IOException {
898adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
899adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
900adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Date: " + formatDate(-1, TimeUnit.MINUTES))
901adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
902adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
903adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
904adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.play();
905adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
906adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
907adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
908adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        connection.addRequestProperty("Cache-Control", "max-age=30");
909adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals("B", readAscii(connection));
910adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    }
911adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
912adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    public void testRequestMinFresh() throws IOException {
913adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
914adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Cache-Control: max-age=60")
915adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
916adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
917adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
918adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.play();
919adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
920adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
921adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
922adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        connection.addRequestProperty("Cache-Control", "min-fresh=120");
923adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals("B", readAscii(connection));
924adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    }
925adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
926c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestMaxStale() throws IOException {
927c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("A")
928c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Cache-Control: max-age=120")
929c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Date: " + formatDate(-4, TimeUnit.MINUTES)));
930c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
931c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
932c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
933c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
934c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
935c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
936c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "max-stale=180");
937c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(connection));
938953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("110 HttpURLConnection \"Response is stale\"",
939953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                connection.getHeaderField("Warning"));
940c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
941c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
9422f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    public void testRequestMaxStaleNotHonoredWithMustRevalidate() throws IOException {
9432f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
9442f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .addHeader("Cache-Control: max-age=120, must-revalidate")
9452f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .addHeader("Date: " + formatDate(-4, TimeUnit.MINUTES)));
9462f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
9472f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
9482f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.play();
9492f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
9502f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
9512f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
9522f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        connection.addRequestProperty("Cache-Control", "max-stale=180");
9532f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("B", readAscii(connection));
9542f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    }
9552f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
956c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestOnlyIfCachedWithNoResponseCached() throws IOException {
957c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        // (no responses enqueued)
958c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
959c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
960953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
961c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "only-if-cached");
962fddea0213028dd6d467f316584fac0f6e0745ce9Elliott Hughes        assertGatewayTimeout(connection);
963c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
964c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
965c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestOnlyIfCachedWithFullResponseCached() throws IOException {
966c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("A")
967c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Cache-Control: max-age=30")
968c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
969c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
970c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
971c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
972c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
973c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "only-if-cached");
974c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
975c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
976c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
977c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestOnlyIfCachedWithConditionalResponseCached() throws IOException {
978c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("A")
979c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Cache-Control: max-age=30")
980c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Date: " + formatDate(-1, TimeUnit.MINUTES)));
981c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
982c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
983c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
984953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
985c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "only-if-cached");
986fddea0213028dd6d467f316584fac0f6e0745ce9Elliott Hughes        assertGatewayTimeout(connection);
987c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
988c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
989c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestOnlyIfCachedWithUnhelpfulResponseCached() throws IOException {
990c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("A"));
991c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
992c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
993c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
994953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
995c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "only-if-cached");
996fddea0213028dd6d467f316584fac0f6e0745ce9Elliott Hughes        assertGatewayTimeout(connection);
997c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
998c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
99984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testRequestCacheControlNoCache() throws Exception {
100084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse()
100184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
100284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
100384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Cache-Control: max-age=60")
100484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .setBody("A"));
100584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
100684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
100784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
100884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URL url = server.getUrl("/");
100984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
101084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URLConnection connection = url.openConnection();
101184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.setRequestProperty("Cache-Control", "no-cache");
101284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("B", readAscii(connection));
101384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
101484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
101584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testRequestPragmaNoCache() throws Exception {
101684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse()
101784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
101884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
101984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Cache-Control: max-age=60")
102084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .setBody("A"));
102184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
102284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
102384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
102484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URL url = server.getUrl("/");
102584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
102684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URLConnection connection = url.openConnection();
102784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.setRequestProperty("Pragma", "no-cache");
102884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("B", readAscii(connection));
102984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
103084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
103184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testClientSuppliedIfModifiedSinceWithCachedResult() throws Exception {
103284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        MockResponse response = new MockResponse()
103384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("ETag: v3")
103484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Cache-Control: max-age=0");
103584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        String ifModifiedSinceDate = formatDate(-24, TimeUnit.HOURS);
103684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        RecordedRequest request = assertClientSuppliedCondition(
103784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                response, "If-Modified-Since", ifModifiedSinceDate);
103884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        List<String> headers = request.getHeaders();
103984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + ifModifiedSinceDate));
104084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertFalse(headers.contains("If-None-Match: v3"));
104184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
104284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
104384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testClientSuppliedIfNoneMatchSinceWithCachedResult() throws Exception {
104484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        String lastModifiedDate = formatDate(-3, TimeUnit.MINUTES);
104584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        MockResponse response = new MockResponse()
104684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
104784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
104884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Cache-Control: max-age=0");
104984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        RecordedRequest request = assertClientSuppliedCondition(
105084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                response, "If-None-Match", "v1");
105184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        List<String> headers = request.getHeaders();
105284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertTrue(headers.contains("If-None-Match: v1"));
105384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertFalse(headers.contains("If-Modified-Since: " + lastModifiedDate));
105484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
105584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
105684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    private RecordedRequest assertClientSuppliedCondition(MockResponse seed, String conditionName,
105784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            String conditionValue) throws Exception {
105884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(seed.setBody("A"));
105984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
106084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
106184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
106284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URL url = server.getUrl("/");
106384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
106484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
106584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
106684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.addRequestProperty(conditionName, conditionValue);
106784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, connection.getResponseCode());
106884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("", readAscii(connection));
106984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
107084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.takeRequest(); // seed
107184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        return server.takeRequest();
107284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
107384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
10749531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    public void testSetIfModifiedSince() throws Exception {
10759531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        Date since = new Date();
10769531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        server.enqueue(new MockResponse().setBody("A"));
10779531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        server.play();
10789531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson
10799531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        URL url = server.getUrl("/");
10809531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        URLConnection connection = url.openConnection();
10819531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        connection.setIfModifiedSince(since.getTime());
10829531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        assertEquals("A", readAscii(connection));
10839531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        RecordedRequest request = server.takeRequest();
10849531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        assertTrue(request.getHeaders().contains("If-Modified-Since: " + formatDate(since)));
10859531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    }
10869531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson
108784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testClientSuppliedConditionWithoutCachedResult() throws Exception {
108884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse()
108984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
109084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
109184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
109284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
109384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        String clientIfModifiedSince = formatDate(-24, TimeUnit.HOURS);
109484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.addRequestProperty("If-Modified-Since", clientIfModifiedSince);
109584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, connection.getResponseCode());
109684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("", readAscii(connection));
109784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
1098c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
109921dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testAuthorizationRequestHeaderPreventsCaching() throws Exception {
110021dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse()
110121dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.MINUTES))
110221dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: max-age=60")
110321dddca4064527116af7a1553de502c6d11138daJesse Wilson                .setBody("A"));
110421dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
110521dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.play();
110621dddca4064527116af7a1553de502c6d11138daJesse Wilson
110721dddca4064527116af7a1553de502c6d11138daJesse Wilson        URL url = server.getUrl("/");
1108953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = url.openConnection();
110921dddca4064527116af7a1553de502c6d11138daJesse Wilson        connection.addRequestProperty("Authorization", "password");
111021dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("A", readAscii(connection));
111121dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("B", readAscii(url.openConnection()));
111221dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
111321dddca4064527116af7a1553de502c6d11138daJesse Wilson
111421dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testAuthorizationResponseCachedWithSMaxAge() throws Exception {
111521dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertAuthorizationRequestFullyCached(new MockResponse()
111621dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: s-maxage=60"));
111721dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
111821dddca4064527116af7a1553de502c6d11138daJesse Wilson
111921dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testAuthorizationResponseCachedWithPublic() throws Exception {
112021dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertAuthorizationRequestFullyCached(new MockResponse()
112121dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: public"));
112221dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
112321dddca4064527116af7a1553de502c6d11138daJesse Wilson
112421dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testAuthorizationResponseCachedWithMustRevalidate() throws Exception {
112521dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertAuthorizationRequestFullyCached(new MockResponse()
112621dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: must-revalidate"));
112721dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
112821dddca4064527116af7a1553de502c6d11138daJesse Wilson
112921dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void assertAuthorizationRequestFullyCached(MockResponse response) throws Exception {
113021dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(response
113121dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: max-age=60")
113221dddca4064527116af7a1553de502c6d11138daJesse Wilson                .setBody("A"));
113321dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
113421dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.play();
113521dddca4064527116af7a1553de502c6d11138daJesse Wilson
113621dddca4064527116af7a1553de502c6d11138daJesse Wilson        URL url = server.getUrl("/");
1137953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = url.openConnection();
113821dddca4064527116af7a1553de502c6d11138daJesse Wilson        connection.addRequestProperty("Authorization", "password");
113921dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("A", readAscii(connection));
114021dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("A", readAscii(url.openConnection()));
114121dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
114221dddca4064527116af7a1553de502c6d11138daJesse Wilson
1143953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public void testContentLocationDoesNotPopulateCache() throws Exception {
1144953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse()
1145953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Cache-Control: max-age=60")
1146953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Content-Location: /bar")
1147953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .setBody("A"));
1148953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
1149953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.play();
1150953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
1151953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/foo").openConnection()));
1152953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("B", readAscii(server.getUrl("/bar").openConnection()));
1153953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
1154953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
115519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    public void testUseCachesFalseDoesNotWriteToCache() throws Exception {
115619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.enqueue(new MockResponse()
115719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson                .addHeader("Cache-Control: max-age=60")
115819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson                .setBody("A").setBody("A"));
115919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
116019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.play();
116119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
116219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
116319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        connection.setUseCaches(false);
116419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertEquals("A", readAscii(connection));
116519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertEquals("B", readAscii(server.getUrl("/").openConnection()));
116619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    }
116719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
116819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    public void testUseCachesFalseDoesNotReadFromCache() throws Exception {
116919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.enqueue(new MockResponse()
117019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson                .addHeader("Cache-Control: max-age=60")
117119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson                .setBody("A").setBody("A"));
117219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
117319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.play();
117419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
117519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
117619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
117719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        connection.setUseCaches(false);
117819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertEquals("B", readAscii(connection));
117919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    }
118019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
118119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    public void testDefaultUseCachesSetsInitialValueOnly() throws Exception {
118219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URL url = new URL("http://localhost/");
118319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URLConnection c1 = url.openConnection();
118419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URLConnection c2 = url.openConnection();
118519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertTrue(c1.getDefaultUseCaches());
118619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        c1.setDefaultUseCaches(false);
118719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        try {
118819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            assertTrue(c1.getUseCaches());
118919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            assertTrue(c2.getUseCaches());
119019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            URLConnection c3 = url.openConnection();
119119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            assertFalse(c3.getUseCaches());
119219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        } finally {
119319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            c1.setDefaultUseCaches(true);
119419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        }
119519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    }
119619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
11972f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    public void testConnectionIsReturnedToPoolAfterConditionalSuccess() throws Exception {
11982f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse()
11992f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
12002f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .addHeader("Cache-Control: max-age=0")
12012f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .setBody("A"));
12022f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
12032f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
12042f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.play();
12052f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
12062f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/a").openConnection()));
12072f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/a").openConnection()));
12082f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("B", readAscii(server.getUrl("/b").openConnection()));
12092f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
12102f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals(0, server.takeRequest().getSequenceNumber());
12112f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals(1, server.takeRequest().getSequenceNumber());
12122f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals(2, server.takeRequest().getSequenceNumber());
12132f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    }
12142f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
1215757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    public void testStatisticsConditionalCacheMiss() throws Exception {
1216757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse()
1217757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1218757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Cache-Control: max-age=0")
1219757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .setBody("A"));
1220757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
1221757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse().setBody("C"));
1222757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.play();
1223757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1224757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1225ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getRequestCount());
1226ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getNetworkCount());
1227ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getHitCount());
1228757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("B", readAscii(server.getUrl("/").openConnection()));
1229757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("C", readAscii(server.getUrl("/").openConnection()));
1230ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(3, cache.getRequestCount());
1231ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(3, cache.getNetworkCount());
1232ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getHitCount());
1233757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    }
1234757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1235757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    public void testStatisticsConditionalCacheHit() throws Exception {
1236757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse()
1237757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1238757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Cache-Control: max-age=0")
1239757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .setBody("A"));
1240757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1241757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1242757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.play();
1243757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1244757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1245ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getRequestCount());
1246ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getNetworkCount());
1247ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getHitCount());
1248757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1249757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1250ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(3, cache.getRequestCount());
1251ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(3, cache.getNetworkCount());
1252ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getHitCount());
1253757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    }
1254757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1255757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    public void testStatisticsFullCacheHit() throws Exception {
1256757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse()
1257757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Cache-Control: max-age=60")
1258757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .setBody("A"));
1259757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.play();
1260757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1261757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1262ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getRequestCount());
1263ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getNetworkCount());
1264ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getHitCount());
1265757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1266757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1267ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(3, cache.getRequestCount());
1268ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getNetworkCount());
1269ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getHitCount());
1270757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    }
1271757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
12726f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryMatchesChangedRequestHeaderField() throws Exception {
12736f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
12746f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
12756f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Accept-Language")
12766f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
12776f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
12786f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
12796f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
12806f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URL url = server.getUrl("/");
12816f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        HttpURLConnection frConnection = (HttpURLConnection) url.openConnection();
12826f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        frConnection.addRequestProperty("Accept-Language", "fr-CA");
12836f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(frConnection));
12846f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
12856f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        HttpURLConnection enConnection = (HttpURLConnection) url.openConnection();
12866f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        enConnection.addRequestProperty("Accept-Language", "en-US");
12876f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("B", readAscii(enConnection));
12886f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
12896f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
12906f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryMatchesUnchangedRequestHeaderField() throws Exception {
12916f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
12926f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
12936f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Accept-Language")
12946f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
12956f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
12966f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
12976f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
12986f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URL url = server.getUrl("/");
12996f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection1 = url.openConnection();
13006f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Language", "fr-CA");
13016f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection1));
13026f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection2 = url.openConnection();
13036f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("Accept-Language", "fr-CA");
13046f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection2));
13056f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
13066f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13076f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryMatchesAbsentRequestHeaderField() throws Exception {
13086f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
13096f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
13106f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Foo")
13116f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
13126f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
13136f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
13146f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13156f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
13166f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
13176f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
13186f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13196f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryMatchesAddedRequestHeaderField() throws Exception {
13206f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
13216f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
13226f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Foo")
13236f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
13246f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
13256f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
13266f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13276f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
13286f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection fooConnection = server.getUrl("/").openConnection();
13296f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        fooConnection.addRequestProperty("Foo", "bar");
13306f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("B", readAscii(fooConnection));
13316f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
13326f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13336f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryMatchesRemovedRequestHeaderField() throws Exception {
13346f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
13356f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
13366f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Foo")
13376f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
13386f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
13396f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
13406f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13416f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection fooConnection = server.getUrl("/").openConnection();
13426f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        fooConnection.addRequestProperty("Foo", "bar");
13436f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(fooConnection));
13446f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("B", readAscii(server.getUrl("/").openConnection()));
13456f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
13466f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13476f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryFieldsAreCaseInsensitive() throws Exception {
13486f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
13496f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
13506f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: ACCEPT-LANGUAGE")
13516f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
13526f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
13536f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
13546f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13556f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URL url = server.getUrl("/");
13566f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection1 = url.openConnection();
13576f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Language", "fr-CA");
13586f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection1));
13596f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection2 = url.openConnection();
13606f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("accept-language", "fr-CA");
13616f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection2));
13626f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
13636f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13646f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryMultipleFieldsWithMatch() throws Exception {
13656f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
13666f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
13676f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Accept-Language, Accept-Charset")
13686f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Accept-Encoding")
13696f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
13706f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
13716f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
13726f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13736f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URL url = server.getUrl("/");
13746f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection1 = url.openConnection();
13756f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Language", "fr-CA");
13766f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Charset", "UTF-8");
13776f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Encoding", "identity");
13786f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection1));
13796f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection2 = url.openConnection();
13806f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("Accept-Language", "fr-CA");
13816f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("Accept-Charset", "UTF-8");
13826f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("Accept-Encoding", "identity");
13836f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection2));
13846f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
13856f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13866f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryMultipleFieldsWithNoMatch() throws Exception {
13876f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
13886f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
13896f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Accept-Language, Accept-Charset")
13906f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Accept-Encoding")
13916f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
13926f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
13936f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
13946f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13956f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URL url = server.getUrl("/");
13966f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection frConnection = url.openConnection();
13976f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        frConnection.addRequestProperty("Accept-Language", "fr-CA");
13986f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        frConnection.addRequestProperty("Accept-Charset", "UTF-8");
13996f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        frConnection.addRequestProperty("Accept-Encoding", "identity");
14006f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(frConnection));
14016f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection enConnection = url.openConnection();
14026f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        enConnection.addRequestProperty("Accept-Language", "en-CA");
14036f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        enConnection.addRequestProperty("Accept-Charset", "UTF-8");
14046f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        enConnection.addRequestProperty("Accept-Encoding", "identity");
14056f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("B", readAscii(enConnection));
14066f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
14076f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14086f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryMultipleFieldValuesWithMatch() throws Exception {
14096f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
14106f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
14116f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Accept-Language")
14126f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
14136f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
14146f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
14156f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14166f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URL url = server.getUrl("/");
14176f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection1 = url.openConnection();
14186f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Language", "fr-CA, fr-FR");
14196f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Language", "en-US");
14206f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection1));
14216f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14226f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection2 = url.openConnection();
14236f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("Accept-Language", "fr-CA, fr-FR");
14246f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("Accept-Language", "en-US");
14256f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection2));
14266f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
14276f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14286f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryMultipleFieldValuesWithNoMatch() throws Exception {
14296f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
14306f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
14316f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Accept-Language")
14326f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
14336f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
14346f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
14356f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14366f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URL url = server.getUrl("/");
14376f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection1 = url.openConnection();
14386f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Language", "fr-CA, fr-FR");
14396f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Language", "en-US");
14406f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection1));
14416f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14426f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection2 = url.openConnection();
14436f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("Accept-Language", "fr-CA");
14446f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("Accept-Language", "en-US");
14456f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("B", readAscii(connection2));
14466f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
14476f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14486f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryAsterisk() throws Exception {
14496f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
14506f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
14516f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: *")
14526f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
14536f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
14546f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
14556f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14566f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
14576f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("B", readAscii(server.getUrl("/").openConnection()));
14586f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
14596f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14606f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryAndHttps() throws Exception {
14616f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
14626f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
14636f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
14646f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
14656f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Accept-Language")
14666f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
14676f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
14686f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
14696f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14706f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URL url = server.getUrl("/");
14716f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        HttpsURLConnection connection1 = (HttpsURLConnection) url.openConnection();
14726f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
14736f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Language", "en-US");
14746f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection1));
14756f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14766f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        HttpsURLConnection connection2 = (HttpsURLConnection) url.openConnection();
14776f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
14786f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("Accept-Language", "en-US");
14796f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection2));
14806f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
14816f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
1482e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson    public void testDiskWriteFailureCacheDegradation() throws Exception {
1483e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        Deque<InvocationHandler> writeHandlers = mockOs.getHandlers("write");
1484e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        int i = 0;
1485e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        boolean hasMoreScenarios = true;
1486e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        while (hasMoreScenarios) {
1487e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            mockOs.enqueueNormal("write", i++);
1488e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            mockOs.enqueueFault("write");
1489e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            exercisePossiblyFaultyCache(false);
1490e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            hasMoreScenarios = writeHandlers.isEmpty();
1491e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            writeHandlers.clear();
1492e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        }
1493e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        System.out.println("Exercising the cache performs " + (i - 1) + " writes.");
1494e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson    }
1495e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson
1496e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson    public void testDiskReadFailureCacheDegradation() throws Exception {
1497e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        Deque<InvocationHandler> readHandlers = mockOs.getHandlers("read");
1498e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        int i = 0;
1499e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        boolean hasMoreScenarios = true;
1500e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        while (hasMoreScenarios) {
1501e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            mockOs.enqueueNormal("read", i++);
1502e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            mockOs.enqueueFault("read");
1503e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            exercisePossiblyFaultyCache(true);
1504e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            hasMoreScenarios = readHandlers.isEmpty();
1505e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            readHandlers.clear();
1506e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        }
1507e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        System.out.println("Exercising the cache performs " + (i - 1) + " reads.");
1508e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson    }
1509e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson
1510cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson    public void testCachePlusCookies() throws Exception {
1511cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        server.enqueue(new MockResponse()
15125d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson                .addHeader("Set-Cookie: a=FIRST; domain=" + server.getCookieDomain() + ";")
1513cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1514cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .addHeader("Cache-Control: max-age=0")
1515cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .setBody("A"));
1516cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        server.enqueue(new MockResponse()
15175d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson                .addHeader("Set-Cookie: a=SECOND; domain=" + server.getCookieDomain() + ";")
1518cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1519cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        server.play();
1520cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson
1521cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        URL url = server.getUrl("/");
1522cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
1523cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertCookies(url, "a=FIRST");
1524cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
1525cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertCookies(url, "a=SECOND");
1526cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson    }
1527cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson
1528cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson    public void testGetHeadersReturnsNetworkEndToEndHeaders() throws Exception {
1529cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        server.enqueue(new MockResponse()
1530cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .addHeader("Allow: GET, HEAD")
1531cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1532cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .addHeader("Cache-Control: max-age=0")
1533cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .setBody("A"));
1534cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        server.enqueue(new MockResponse()
1535cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .addHeader("Allow: GET, HEAD, PUT")
1536cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1537cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        server.play();
1538cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson
1539cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        URLConnection connection1 = server.getUrl("/").openConnection();
1540cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertEquals("A", readAscii(connection1));
1541cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertEquals("GET, HEAD", connection1.getHeaderField("Allow"));
1542cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson
1543cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        URLConnection connection2 = server.getUrl("/").openConnection();
1544cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertEquals("A", readAscii(connection2));
1545cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertEquals("GET, HEAD, PUT", connection2.getHeaderField("Allow"));
1546cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson    }
1547cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson
1548cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson    public void testGetHeadersReturnsCachedHopByHopHeaders() throws Exception {
1549cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        server.enqueue(new MockResponse()
1550cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .addHeader("Transfer-Encoding: identity")
1551cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1552cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .addHeader("Cache-Control: max-age=0")
1553cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .setBody("A"));
1554cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        server.enqueue(new MockResponse()
1555cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .addHeader("Transfer-Encoding: none")
1556cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1557cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        server.play();
1558cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson
1559cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        URLConnection connection1 = server.getUrl("/").openConnection();
1560cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertEquals("A", readAscii(connection1));
1561cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertEquals("identity", connection1.getHeaderField("Transfer-Encoding"));
1562cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson
1563cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        URLConnection connection2 = server.getUrl("/").openConnection();
1564cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertEquals("A", readAscii(connection2));
1565cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertEquals("identity", connection2.getHeaderField("Transfer-Encoding"));
1566cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson    }
1567cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson
1568cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson    public void testGetHeadersDeletesCached100LevelWarnings() throws Exception {
1569cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        server.enqueue(new MockResponse()
1570cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .addHeader("Warning: 199 test danger")
1571cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1572cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .addHeader("Cache-Control: max-age=0")
1573cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .setBody("A"));
1574cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        server.enqueue(new MockResponse()
1575cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1576cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        server.play();
1577cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson
1578cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        URLConnection connection1 = server.getUrl("/").openConnection();
1579cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertEquals("A", readAscii(connection1));
1580cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertEquals("199 test danger", connection1.getHeaderField("Warning"));
1581cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson
1582cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        URLConnection connection2 = server.getUrl("/").openConnection();
1583cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertEquals("A", readAscii(connection2));
1584cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertEquals(null, connection2.getHeaderField("Warning"));
1585cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson    }
1586cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson
1587cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson    public void testGetHeadersRetainsCached200LevelWarnings() throws Exception {
1588cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        server.enqueue(new MockResponse()
1589cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .addHeader("Warning: 299 test danger")
1590cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1591cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .addHeader("Cache-Control: max-age=0")
1592cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .setBody("A"));
1593cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        server.enqueue(new MockResponse()
1594cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1595cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        server.play();
1596cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson
1597cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        URLConnection connection1 = server.getUrl("/").openConnection();
1598cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertEquals("A", readAscii(connection1));
1599cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertEquals("299 test danger", connection1.getHeaderField("Warning"));
1600cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson
1601cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        URLConnection connection2 = server.getUrl("/").openConnection();
1602cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertEquals("A", readAscii(connection2));
1603cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertEquals("299 test danger", connection2.getHeaderField("Warning"));
1604cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson    }
1605cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson
1606cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson    public void assertCookies(URL url, String... expectedCookies) throws Exception {
1607cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        List<String> actualCookies = new ArrayList<String>();
1608cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        for (HttpCookie cookie : cookieManager.getCookieStore().get(url.toURI())) {
1609cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson            actualCookies.add(cookie.toString());
1610cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        }
1611cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertEquals(Arrays.asList(expectedCookies), actualCookies);
1612cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson    }
1613cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson
1614cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson    public void testCachePlusRange() throws Exception {
1615cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson        assertNotCached(new MockResponse()
1616cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_PARTIAL)
1617cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
1618cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .addHeader("Content-Range: bytes 100-100/200")
1619cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
1620cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson    }
1621cd8c1dd724036dcd1b7c27542cc4d745391b8d04Jesse Wilson
16225d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson    public void testConditionalHitUpdatesCache() throws Exception {
16235d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        server.enqueue(new MockResponse()
16245d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson                .addHeader("Last-Modified: " + formatDate(0, TimeUnit.SECONDS))
16255d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson                .addHeader("Cache-Control: max-age=0")
16265d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson                .setBody("A"));
16275d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        server.enqueue(new MockResponse()
16285d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson                .addHeader("Cache-Control: max-age=30")
16295d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson                .addHeader("Allow: GET, HEAD")
16305d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson                .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
16315d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
16325d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        server.play();
16335d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson
16345d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        // cache miss; seed the cache
16355d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        HttpURLConnection connection1 = (HttpURLConnection) server.getUrl("/a").openConnection();
16365d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals("A", readAscii(connection1));
16375d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals(null, connection1.getHeaderField("Allow"));
16385d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson
16395d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        // conditional cache hit; update the cache
16405d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        HttpURLConnection connection2 = (HttpURLConnection) server.getUrl("/a").openConnection();
16415d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals(HttpURLConnection.HTTP_OK, connection2.getResponseCode());
16425d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals("A", readAscii(connection2));
16435d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals("GET, HEAD", connection2.getHeaderField("Allow"));
16445d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson
16455d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        // full cache hit
16465d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        HttpURLConnection connection3 = (HttpURLConnection) server.getUrl("/a").openConnection();
16475d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals("A", readAscii(connection3));
16485d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals("GET, HEAD", connection3.getHeaderField("Allow"));
16495d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson
16505d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals(2, server.getRequestCount());
16515d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson    }
16525d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson
16530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
16540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * @param delta the offset from the current date to use. Negative
16550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     *     values yield dates in the past; positive values yield dates in the
16560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     *     future.
16570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
1658953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private String formatDate(long delta, TimeUnit timeUnit) {
16599531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        return formatDate(new Date(System.currentTimeMillis() + timeUnit.toMillis(delta)));
16609531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    }
16619531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson
16629531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    private String formatDate(Date date) {
1663953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        DateFormat rfc1123 = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
1664953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        rfc1123.setTimeZone(TimeZone.getTimeZone("UTC"));
1665953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        return rfc1123.format(date);
16660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
16670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
16680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void addRequestBodyIfNecessary(String requestMethod, HttpURLConnection invalidate)
16690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            throws IOException {
16700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        if (requestMethod.equals("POST") || requestMethod.equals("PUT")) {
16710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            invalidate.setDoOutput(true);
16720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            OutputStream requestBody = invalidate.getOutputStream();
16730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            requestBody.write('x');
16740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            requestBody.close();
16750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
16760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
16770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
16780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void assertNotCached(MockResponse response) throws Exception {
16790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response.setBody("A"));
16800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
16810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
16820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
16830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
16840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
16850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("B", readAscii(url.openConnection()));
16860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
16870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1688e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson    private void exercisePossiblyFaultyCache(boolean permitReadBodyFailures) throws Exception {
1689e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        server.shutdown();
1690e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        server = new MockWebServer();
1691e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        server.enqueue(new MockResponse()
1692e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson                .addHeader("Cache-Control: max-age=60")
1693e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson                .setBody("A"));
1694e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
1695e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        server.play();
1696e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson
1697e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        URL url = server.getUrl("/" + UUID.randomUUID());
1698e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
1699e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson
1700e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        URLConnection connection = url.openConnection();
1701e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        InputStream in = connection.getInputStream();
1702e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        try {
1703e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            int bodyChar = in.read();
1704e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            assertTrue(bodyChar == 'A' || bodyChar == 'B');
1705e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            assertEquals(-1, in.read());
1706e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        } catch (IOException e) {
1707e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            if (!permitReadBodyFailures) {
1708e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson                throw e;
1709e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            }
1710e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        }
1711e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson    }
1712e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson
17130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
17140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * @return the request with the conditional get headers.
17150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
17160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private RecordedRequest assertConditionallyCached(MockResponse response) throws Exception {
17170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // scenario 1: condition succeeds
17185d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        server.enqueue(response.setBody("A").setStatus("HTTP/1.1 200 A-OK"));
17190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
17200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
17210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // scenario 2: condition fails
17225d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        server.enqueue(response.setBody("B").setStatus("HTTP/1.1 200 B-OK"));
17235d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        server.enqueue(new MockResponse().setStatus("HTTP/1.1 200 C-OK").setBody("C"));
17240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
17250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
17260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
17270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL valid = server.getUrl("/valid");
17285d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        HttpURLConnection connection1 = (HttpURLConnection) valid.openConnection();
17295d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals("A", readAscii(connection1));
17305d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals(HttpURLConnection.HTTP_OK, connection1.getResponseCode());
17315d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals("A-OK", connection1.getResponseMessage());
17325d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        HttpURLConnection connection2 = (HttpURLConnection) valid.openConnection();
17335d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals("A", readAscii(connection2));
17345d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals(HttpURLConnection.HTTP_OK, connection2.getResponseCode());
17355d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals("A-OK", connection2.getResponseMessage());
17360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
17370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL invalid = server.getUrl("/invalid");
17385d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        HttpURLConnection connection3 = (HttpURLConnection) invalid.openConnection();
17395d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals("B", readAscii(connection3));
17405d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals(HttpURLConnection.HTTP_OK, connection3.getResponseCode());
17415d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals("B-OK", connection3.getResponseMessage());
17425d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        HttpURLConnection connection4 = (HttpURLConnection) invalid.openConnection();
17435d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals("C", readAscii(connection4));
17445d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals(HttpURLConnection.HTTP_OK, connection4.getResponseCode());
17455d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        assertEquals("C-OK", connection4.getResponseMessage());
17460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
17470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.takeRequest(); // regular get
17480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return server.takeRequest(); // conditional get
17490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
17500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
17510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void assertFullyCached(MockResponse response) throws Exception {
17520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response.setBody("A"));
17530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response.setBody("B"));
17540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
17550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
17560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
17570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
17580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
17590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
17600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
17610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
17620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Shortens the body of {@code response} but not the corresponding headers.
17630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Only useful to test how clients respond to the premature conclusion of
17640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * the HTTP body.
17650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
17660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private MockResponse truncateViolently(MockResponse response, int numBytesToKeep) {
17670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        response.setSocketPolicy(DISCONNECT_AT_END);
17680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = new ArrayList<String>(response.getHeaders());
17690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        response.setBody(Arrays.copyOfRange(response.getBody(), 0, numBytesToKeep));
17700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        response.getHeaders().clear();
17710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        response.getHeaders().addAll(headers);
17720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return response;
17730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
17740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
17750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
17760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Reads {@code count} characters from the stream. If the stream is
17770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * exhausted before {@code count} characters can be read, the remaining
17780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * characters are returned and the stream is closed.
17790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
17800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private String readAscii(URLConnection connection, int count) throws IOException {
178184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpURLConnection httpConnection = (HttpURLConnection) connection;
178284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        InputStream in = httpConnection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST
178384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                ? connection.getInputStream()
178484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                : httpConnection.getErrorStream();
17850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        StringBuilder result = new StringBuilder();
17860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        for (int i = 0; i < count; i++) {
17870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            int value = in.read();
17880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            if (value == -1) {
17890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                in.close();
17900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                break;
17910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
17920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            result.append((char) value);
17930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
17940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return result.toString();
17950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
17960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
17970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private String readAscii(URLConnection connection) throws IOException {
17980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return readAscii(connection, Integer.MAX_VALUE);
17990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
18000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
18010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void reliableSkip(InputStream in, int length) throws IOException {
18020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        while (length > 0) {
18030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            length -= in.skip(length);
18040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
18050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
18060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1807fddea0213028dd6d467f316584fac0f6e0745ce9Elliott Hughes    private void assertGatewayTimeout(HttpURLConnection connection) throws IOException {
1808c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        try {
1809c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson            connection.getInputStream();
1810c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson            fail();
1811c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        } catch (FileNotFoundException expected) {
1812c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        }
1813fddea0213028dd6d467f316584fac0f6e0745ce9Elliott Hughes        assertEquals(504, connection.getResponseCode());
1814953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals(-1, connection.getErrorStream().read());
1815c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
1816c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
18170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    enum TransferKind {
18180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        CHUNKED() {
18190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override void setBody(MockResponse response, byte[] content, int chunkSize)
18200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    throws IOException {
18210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                response.setChunkedBody(content, chunkSize);
18220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
18230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        },
18240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        FIXED_LENGTH() {
18250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override void setBody(MockResponse response, byte[] content, int chunkSize) {
18260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                response.setBody(content);
18270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
18280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        },
18290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        END_OF_STREAM() {
18300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override void setBody(MockResponse response, byte[] content, int chunkSize) {
18310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                response.setBody(content);
18320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                response.setSocketPolicy(DISCONNECT_AT_END);
18330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                for (Iterator<String> h = response.getHeaders().iterator(); h.hasNext(); ) {
18340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    if (h.next().startsWith("Content-Length:")) {
18350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                        h.remove();
18360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                        break;
18370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    }
18380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                }
18390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
18400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        };
18410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
18420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        abstract void setBody(MockResponse response, byte[] content, int chunkSize)
18430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                throws IOException;
18440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
18450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        void setBody(MockResponse response, String content, int chunkSize) throws IOException {
18460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            setBody(response, content.getBytes("UTF-8"), chunkSize);
18470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
18480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
18490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
18500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private <T> List<T> toListOrNull(T[] arrayOrNull) {
18510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return arrayOrNull != null ? Arrays.asList(arrayOrNull) : null;
18520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
18530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
18540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
18550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Returns a gzipped copy of {@code bytes}.
18560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
18570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public byte[] gzip(byte[] bytes) throws IOException {
18580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
18590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        OutputStream gzippedOut = new GZIPOutputStream(bytesOut);
18600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        gzippedOut.write(bytes);
18610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        gzippedOut.close();
18620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return bytesOut.toByteArray();
18630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
18640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1865433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson    private class InsecureResponseCache extends ResponseCache {
18660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        @Override public CacheRequest put(URI uri, URLConnection connection) throws IOException {
1867433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            return cache.put(uri, connection);
18680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
18690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
18700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        @Override public CacheResponse get(URI uri, String requestMethod,
18710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                Map<String, List<String>> requestHeaders) throws IOException {
1872433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            final CacheResponse response = cache.get(uri, requestMethod, requestHeaders);
18730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            if (response instanceof SecureCacheResponse) {
18740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                return new CacheResponse() {
18750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    @Override public InputStream getBody() throws IOException {
18760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                        return response.getBody();
18770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    }
18780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    @Override public Map<String, List<String>> getHeaders() throws IOException {
18790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                        return response.getHeaders();
18800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    }
18810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                };
18820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
18830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            return response;
18840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
18850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
18860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson}
1887