HttpResponseCacheTest.java revision e5eb80e6adaab18ff7372adcd09f9e1af3a76871
10c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson/*
20c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * Copyright (C) 2011 The Android Open Source Project
30c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson *
40c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License");
50c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * you may not use this file except in compliance with the License.
60c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * You may obtain a copy of the License at
70c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson *
80c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson *      http://www.apache.org/licenses/LICENSE-2.0
90c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson *
100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * Unless required by applicable law or agreed to in writing, software
110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * See the License for the specific language governing permissions and
140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * limitations under the License.
150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson */
160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
17757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilsonpackage libcore.net.http;
180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.BufferedReader;
200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.ByteArrayOutputStream;
21433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilsonimport java.io.File;
22c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilsonimport java.io.FileNotFoundException;
230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.IOException;
240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.InputStream;
250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.InputStreamReader;
260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.OutputStream;
27e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilsonimport java.lang.reflect.InvocationHandler;
280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.CacheRequest;
290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.CacheResponse;
300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.HttpURLConnection;
310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.ResponseCache;
320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.SecureCacheResponse;
330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.URI;
340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.URISyntaxException;
350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.URL;
360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.URLConnection;
370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.security.Principal;
380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.security.cert.Certificate;
39953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.text.DateFormat;
40953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.text.SimpleDateFormat;
410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.ArrayList;
420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.Arrays;
430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.Collections;
440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.Date;
45e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilsonimport java.util.Deque;
460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.Iterator;
470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.List;
48953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.util.Locale;
490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.Map;
50953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.util.TimeZone;
51433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilsonimport java.util.UUID;
520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.concurrent.TimeUnit;
530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.concurrent.atomic.AtomicInteger;
540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.concurrent.atomic.AtomicReference;
550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.zip.GZIPOutputStream;
560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport javax.net.ssl.HttpsURLConnection;
570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport junit.framework.TestCase;
580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport libcore.javax.net.ssl.TestSSLContext;
590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport tests.http.MockResponse;
600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport tests.http.MockWebServer;
610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport tests.http.RecordedRequest;
620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport static tests.http.SocketPolicy.DISCONNECT_AT_END;
63e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilsonimport tests.io.MockOs;
640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
65396af46f45fd302e6309fc48ac1fd68cbb739edbJesse Wilsonpublic final class HttpResponseCacheTest extends TestCase {
66fb4a6392a04b1f3a1124b3db6bae51d8cbfa53f8Jesse Wilson
67fb4a6392a04b1f3a1124b3db6bae51d8cbfa53f8Jesse Wilson    // TODO: test cache + cookies
68fb4a6392a04b1f3a1124b3db6bae51d8cbfa53f8Jesse Wilson    // TODO: test cache + user-provided Range header
69fb4a6392a04b1f3a1124b3db6bae51d8cbfa53f8Jesse Wilson
700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private MockWebServer server = new MockWebServer();
71396af46f45fd302e6309fc48ac1fd68cbb739edbJesse Wilson    private HttpResponseCache cache;
72e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson    final MockOs mockOs = new MockOs();
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();
820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    @Override protected void tearDown() throws Exception {
85e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        mockOs.uninstall();
860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.shutdown();
87433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        ResponseCache.setDefault(null);
88fb4a6392a04b1f3a1124b3db6bae51d8cbfa53f8Jesse Wilson        cache.getCache().delete();
890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        super.tearDown();
900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Test that response caching is consistent with the RI and the spec.
940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4
950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
9684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testResponseCachingByResponseCode() throws Exception {
970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // Test each documented HTTP/1.1 code, plus the first unused value in each range.
980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // We can't test 100 because it's not really a response.
1010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // assertCached(false, 100);
1020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 101);
1030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 102);
1040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(true,  200);
1050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 201);
1060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 202);
1070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(true,  203);
1080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 204);
1090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 205);
1100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(true,  206);
1110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 207);
11284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertCached(true,  300);
1130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(true,  301);
1140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        for (int i = 302; i <= 308; ++i) {
1150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertCached(false, i);
1160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        for (int i = 400; i <= 406; ++i) {
1180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertCached(false, i);
1190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // (See test_responseCaching_407.)
1210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 408);
1220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertCached(false, 409);
1230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // (See test_responseCaching_410.)
1240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        for (int i = 411; i <= 418; ++i) {
1250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertCached(false, i);
1260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        for (int i = 500; i <= 506; ++i) {
1280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertCached(false, i);
1290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
1310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
1330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Response code 407 should only come from proxy servers. Android's client
1340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * throws if it is sent by an origin server.
1350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
1360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testOriginServerSends407() throws Exception {
1370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(407));
1380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
1390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
1410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
1420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
1430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            conn.getResponseCode();
1440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail();
1450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (IOException expected) {
1460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
1480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void test_responseCaching_410() throws Exception {
1500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // the HTTP spec permits caching 410s, but the RI doesn't.
15184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertCached(true, 410);
1520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
1530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void assertCached(boolean shouldPut, int responseCode) throws Exception {
1550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server = new MockWebServer();
1560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
1570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
1590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setResponseCode(responseCode)
1600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("ABCDE")
1610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("WWW-Authenticate: challenge"));
1620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
1630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
1650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
1660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(responseCode, conn.getResponseCode());
1670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // exhaust the content stream
16984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        readAscii(conn);
1700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
171433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        CacheResponse cached = cache.get(url.toURI(), "GET",
172433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson                Collections.<String, List<String>>emptyMap());
173433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        if (shouldPut) {
174433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            assertNotNull(Integer.toString(responseCode), cached);
175433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            cached.getBody().close();
176433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        } else {
177433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            assertNull(Integer.toString(responseCode), cached);
178433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        }
1790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.shutdown(); // tearDown() isn't sufficient; this test starts multiple servers
1800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
1810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
1830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Test that we can interrogate the response when the cache is being
1840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * populated. http://code.google.com/p/android/issues/detail?id=7787
1850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
1860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponseCacheCallbackApis() throws Exception {
1870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        final String body = "ABCDE";
1880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        final AtomicInteger cacheCount = new AtomicInteger();
1890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
1910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setStatus("HTTP/1.1 200 Fantastic")
1920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("fgh: ijk")
1930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody(body));
1940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
1950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        ResponseCache.setDefault(new ResponseCache() {
1970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override public CacheResponse get(URI uri, String requestMethod,
1980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    Map<String, List<String>> requestHeaders) throws IOException {
1990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                return null;
2000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
2010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override public CacheRequest put(URI uri, URLConnection conn) throws IOException {
2020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                HttpURLConnection httpConnection = (HttpURLConnection) conn;
203953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                try {
204953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                    httpConnection.getRequestProperties();
205953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                    fail();
206953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                } catch (IllegalStateException expected) {
207953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                }
208953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                try {
209953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                    httpConnection.addRequestProperty("K", "V");
210953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                    fail();
211953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                } catch (IllegalStateException expected) {
212953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                }
2130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                assertEquals("HTTP/1.1 200 Fantastic", httpConnection.getHeaderField(null));
2140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                assertEquals(Arrays.asList("HTTP/1.1 200 Fantastic"),
2150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                        httpConnection.getHeaderFields().get(null));
2160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                assertEquals(200, httpConnection.getResponseCode());
2170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                assertEquals("Fantastic", httpConnection.getResponseMessage());
2180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                assertEquals(body.length(), httpConnection.getContentLength());
2190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                assertEquals("ijk", httpConnection.getHeaderField("fgh"));
2200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                try {
2210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    httpConnection.getInputStream(); // the RI doesn't forbid this, but it should
2220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    fail();
2230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                } catch (IOException expected) {
2240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                }
2250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                cacheCount.incrementAndGet();
2260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                return null;
2270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
2280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        });
2290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
231953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = url.openConnection();
2320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(body, readAscii(connection));
2330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(1, cacheCount.get());
2340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponseCachingAndInputStreamSkipWithFixedLength() throws IOException {
2380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testResponseCaching(TransferKind.FIXED_LENGTH);
2390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponseCachingAndInputStreamSkipWithChunkedEncoding() throws IOException {
2420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testResponseCaching(TransferKind.CHUNKED);
2430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponseCachingAndInputStreamSkipWithNoLengthHeaders() throws IOException {
2460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testResponseCaching(TransferKind.END_OF_STREAM);
2470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
2500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * HttpURLConnection.getInputStream().skip(long) causes ResponseCache corruption
2510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * http://code.google.com/p/android/issues/detail?id=8175
2520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
2530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testResponseCaching(TransferKind transferKind) throws IOException {
2540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        MockResponse response = new MockResponse()
2550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
2560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
2570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setStatus("HTTP/1.1 200 Fantastic");
2580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        transferKind.setBody(response, "I love puppies but hate spiders", 1);
2590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
2600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
2610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // Make sure that calling skip() doesn't omit bytes from the cache.
2630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection();
2640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        InputStream in = urlConnection.getInputStream();
2650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("I love ", readAscii(urlConnection, "I love ".length()));
2660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        reliableSkip(in, "puppies but hate ".length());
2670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("spiders", readAscii(urlConnection, "spiders".length()));
2680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(-1, in.read());
2690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        in.close();
270ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteSuccessCount());
271ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getWriteAbortCount());
2720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        urlConnection = (HttpURLConnection) server.getUrl("/").openConnection(); // cached!
2740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        in = urlConnection.getInputStream();
2750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("I love puppies but hate spiders",
2760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                readAscii(urlConnection, "I love puppies but hate spiders".length()));
2770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(200, urlConnection.getResponseCode());
2780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("Fantastic", urlConnection.getResponseMessage());
2790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(-1, in.read());
281433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        in.close();
282ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteSuccessCount());
283ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getWriteAbortCount());
284ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getRequestCount());
285ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getHitCount());
2860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testSecureResponseCaching() throws IOException {
2890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
2900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
2910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
2920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
2930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
2940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("ABC"));
2950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
2960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
2980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
2990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
3000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // OpenJDK 6 fails on this line, complaining that the connection isn't open yet
3020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String suite = connection.getCipherSuite();
3030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<Certificate> localCerts = toListOrNull(connection.getLocalCertificates());
3040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<Certificate> serverCerts = toListOrNull(connection.getServerCertificates());
3050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        Principal peerPrincipal = connection.getPeerPrincipal();
3060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        Principal localPrincipal = connection.getLocalPrincipal();
3070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection = (HttpsURLConnection) server.getUrl("/").openConnection(); // cached!
3090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
3100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
3110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
312ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getRequestCount());
313ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getNetworkCount());
314ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getHitCount());
3150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(suite, connection.getCipherSuite());
3170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(localCerts, toListOrNull(connection.getLocalCertificates()));
3180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(serverCerts, toListOrNull(connection.getServerCertificates()));
3190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(peerPrincipal, connection.getPeerPrincipal());
3200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(localPrincipal, connection.getLocalPrincipal());
3210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
3220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheReturnsInsecureResponseForSecureRequest() throws IOException {
3240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
3250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
3260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("ABC"));
3270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("DEF"));
3280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
3290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        ResponseCache.setDefault(new InsecureResponseCache());
3310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
3330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
3340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
3350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection = (HttpsURLConnection) server.getUrl("/").openConnection(); // not cached!
3370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
3380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("DEF", readAscii(connection));
3390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
3400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
341a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson    public void testResponseCachingAndRedirects() throws Exception {
3420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
3430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
3440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
3450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
3460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Location: /foo"));
3470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
3480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
3490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
3500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("ABC"));
3510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("DEF"));
3520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
3530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
354953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
3550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
3560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
357953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        connection = server.getUrl("/").openConnection(); // cached!
3580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
3590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
360ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(4, cache.getRequestCount()); // 2 requests + 2 redirects
361ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getNetworkCount());
362ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getHitCount());
3630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
3640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
365a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson    public void testRedirectToCachedResult() throws Exception {
366a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        server.enqueue(new MockResponse()
367a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson                .addHeader("Cache-Control: max-age=60")
368a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson                .setBody("ABC"));
369a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        server.enqueue(new MockResponse()
370a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
371a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson                .addHeader("Location: /foo"));
372a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        server.enqueue(new MockResponse().setBody("DEF"));
373a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        server.play();
374a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
375a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("ABC", readAscii(server.getUrl("/foo").openConnection()));
376a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        RecordedRequest request1 = server.takeRequest();
377a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("GET /foo HTTP/1.1", request1.getRequestLine());
378a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals(0, request1.getSequenceNumber());
379a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
380a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("ABC", readAscii(server.getUrl("/bar").openConnection()));
381a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        RecordedRequest request2 = server.takeRequest();
382a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("GET /bar HTTP/1.1", request2.getRequestLine());
383a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals(1, request2.getSequenceNumber());
384a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
385a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        // an unrelated request should reuse the pooled connection
386a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("DEF", readAscii(server.getUrl("/baz").openConnection()));
387a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        RecordedRequest request3 = server.takeRequest();
388a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("GET /baz HTTP/1.1", request3.getRequestLine());
389a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals(2, request3.getSequenceNumber());
390a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson    }
391a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
3920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testSecureResponseCachingAndRedirects() throws IOException {
3930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
3940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
3950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
3960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
3970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
3980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
3990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Location: /foo"));
4000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
4010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
4020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
4030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("ABC"));
4040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("DEF"));
4050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
4060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
4080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
4090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
4100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection = (HttpsURLConnection) server.getUrl("/").openConnection(); // cached!
4120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
4130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", readAscii(connection));
4140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
415ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(4, cache.getRequestCount()); // 2 direct + 2 redirect = 4
416ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getHitCount());
4170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponseCacheRequestHeaders() throws IOException, URISyntaxException {
4200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("ABC"));
4210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
4220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        final AtomicReference<Map<String, List<String>>> requestHeadersRef
4240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                = new AtomicReference<Map<String, List<String>>>();
4250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        ResponseCache.setDefault(new ResponseCache() {
4260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override public CacheResponse get(URI uri, String requestMethod,
4270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    Map<String, List<String>> requestHeaders) throws IOException {
4280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                requestHeadersRef.set(requestHeaders);
4290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                return null;
4300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
4310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override public CacheRequest put(URI uri, URLConnection conn) throws IOException {
4320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                return null;
4330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
4340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        });
4350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
4370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection urlConnection = url.openConnection();
4380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        urlConnection.addRequestProperty("A", "android");
4390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        readAscii(urlConnection);
4400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(Arrays.asList("android"), requestHeadersRef.get().get("A"));
4410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testServerDisconnectsPrematurelyWithContentLengthHeader() throws IOException {
4450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testServerPrematureDisconnect(TransferKind.FIXED_LENGTH);
4460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testServerDisconnectsPrematurelyWithChunkedEncoding() throws IOException {
4490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testServerPrematureDisconnect(TransferKind.CHUNKED);
4500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testServerDisconnectsPrematurelyWithNoLengthHeaders() throws IOException {
4530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        /*
4540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * Intentionally empty. This case doesn't make sense because there's no
4550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * such thing as a premature disconnect when the disconnect itself
4560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * indicates the end of the data stream.
4570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         */
4580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testServerPrematureDisconnect(TransferKind transferKind) throws IOException {
4610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        MockResponse response = new MockResponse();
4620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        transferKind.setBody(response, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 16);
4630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(truncateViolently(response, 16));
4640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("Request #2"));
4650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
4660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        BufferedReader reader = new BufferedReader(new InputStreamReader(
4680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                server.getUrl("/").openConnection().getInputStream()));
4690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCDE", reader.readLine());
4700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
4710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            reader.readLine();
4720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail("This implementation silently ignored a truncated HTTP body.");
4730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (IOException expected) {
474433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        } finally {
475433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            reader.close();
4760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
4770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
478ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteAbortCount());
479ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getWriteSuccessCount());
4800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
4810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("Request #2", readAscii(connection));
482ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteAbortCount());
483ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteSuccessCount());
4840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testClientPrematureDisconnectWithContentLengthHeader() throws IOException {
4870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testClientPrematureDisconnect(TransferKind.FIXED_LENGTH);
4880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testClientPrematureDisconnectWithChunkedEncoding() throws IOException {
4910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testClientPrematureDisconnect(TransferKind.CHUNKED);
4920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testClientPrematureDisconnectWithNoLengthHeaders() throws IOException {
4950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testClientPrematureDisconnect(TransferKind.END_OF_STREAM);
4960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testClientPrematureDisconnect(TransferKind transferKind) throws IOException {
4990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        MockResponse response = new MockResponse();
5000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        transferKind.setBody(response, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 1024);
5010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
5020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("Request #2"));
5030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
5040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
5050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
5060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        InputStream in = connection.getInputStream();
5070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCDE", readAscii(connection, 5));
5080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        in.close();
5090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
5100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            in.read();
5110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail("Expected an IOException because the stream is closed.");
5120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (IOException expected) {
5130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
5140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
515ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteAbortCount());
516ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getWriteSuccessCount());
5170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection = server.getUrl("/").openConnection();
51884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("Request #2", readAscii(connection));
519ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteAbortCount());
520ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getWriteSuccessCount());
5210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
523953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public void testDefaultExpirationDateFullyCachedForLessThan24Hours() throws Exception {
52421dddca4064527116af7a1553de502c6d11138daJesse Wilson        //      last modified: 105 seconds ago
52521dddca4064527116af7a1553de502c6d11138daJesse Wilson        //             served:   5 seconds ago
52621dddca4064527116af7a1553de502c6d11138daJesse Wilson        //   default lifetime: (105 - 5) / 10 = 10 seconds
52721dddca4064527116af7a1553de502c6d11138daJesse Wilson        //            expires:  10 seconds from served date = 5 seconds from now
528953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse()
52921dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Last-Modified: " + formatDate(-105, TimeUnit.SECONDS))
530953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Date: " + formatDate(-5, TimeUnit.SECONDS))
531953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .setBody("A"));
532953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.play();
533953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
534953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URL url = server.getUrl("/");
535953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
536953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = url.openConnection();
537953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(connection));
538953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertNull(connection.getHeaderField("Warning"));
53921dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
54021dddca4064527116af7a1553de502c6d11138daJesse Wilson
54121dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testDefaultExpirationDateConditionallyCached() throws Exception {
54221dddca4064527116af7a1553de502c6d11138daJesse Wilson        //      last modified: 115 seconds ago
54321dddca4064527116af7a1553de502c6d11138daJesse Wilson        //             served:  15 seconds ago
54421dddca4064527116af7a1553de502c6d11138daJesse Wilson        //   default lifetime: (115 - 15) / 10 = 10 seconds
54521dddca4064527116af7a1553de502c6d11138daJesse Wilson        //            expires:  10 seconds from served date = 5 seconds ago
54621dddca4064527116af7a1553de502c6d11138daJesse Wilson        String lastModifiedDate = formatDate(-115, TimeUnit.SECONDS);
5470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
54821dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
54921dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Date: " + formatDate(-15, TimeUnit.SECONDS)));
5500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
5510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
5520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
554953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public void testDefaultExpirationDateFullyCachedForMoreThan24Hours() throws Exception {
555953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        //      last modified: 105 days ago
556953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        //             served:   5 days ago
557953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        //   default lifetime: (105 - 5) / 10 = 10 days
558953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        //            expires:  10 days from served date = 5 days from now
559953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse()
560953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-105, TimeUnit.DAYS))
561953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Date: " + formatDate(-5, TimeUnit.DAYS))
562953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .setBody("A"));
563953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.play();
564953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
565953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
566953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
567953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(connection));
568953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("113 HttpURLConnection \"Heuristic expiration\"",
569953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                connection.getHeaderField("Warning"));
570953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
571953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
57221dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testNoDefaultExpirationForUrlsWithQueryString() throws Exception {
57321dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse()
57421dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Last-Modified: " + formatDate(-105, TimeUnit.SECONDS))
57521dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Date: " + formatDate(-5, TimeUnit.SECONDS))
57621dddca4064527116af7a1553de502c6d11138daJesse Wilson                .setBody("A"));
57721dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
57821dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.play();
57921dddca4064527116af7a1553de502c6d11138daJesse Wilson
58021dddca4064527116af7a1553de502c6d11138daJesse Wilson        URL url = server.getUrl("/?foo=bar");
58121dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("A", readAscii(url.openConnection()));
58221dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("B", readAscii(url.openConnection()));
58321dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
58421dddca4064527116af7a1553de502c6d11138daJesse Wilson
5850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testExpirationDateInThePastWithLastModifiedHeader() throws Exception {
5860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
5870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
5880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
5890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
5900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
5910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
5920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
5940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testExpirationDateInThePastWithNoLastModifiedHeader() throws Exception {
5950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse()
5960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
5970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
5990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testExpirationDateInTheFuture() throws Exception {
6000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
6010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
6020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgePreferredWithMaxAgeAndExpires() throws Exception {
6050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
6060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
6070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS))
6080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
6090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgeInThePastWithDateAndLastModifiedHeaders() throws Exception {
6120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
6130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
6140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Date: " + formatDate(-120, TimeUnit.SECONDS))
6150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
6160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
6170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
6180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
6190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgeInThePastWithDateHeaderButNoLastModifiedHeader() throws Exception {
62284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        /*
62384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson         * Chrome interprets max-age relative to the local clock. Both our cache
62484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson         * and Firefox both use the earlier of the local and server's clock.
62584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson         */
6260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse()
6270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Date: " + formatDate(-120, TimeUnit.SECONDS))
6280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
6290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgeInTheFutureWithDateHeader() throws Exception {
6320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
6330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
6340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
6350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMaxAgeInTheFutureWithNoDateHeader() throws Exception {
6380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
6390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
6400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
642adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    public void testMaxAgeWithLastModifiedButNoServedDate() throws Exception {
643adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertFullyCached(new MockResponse()
644adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
645adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
646adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    }
647adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
648adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    public void testMaxAgeInTheFutureWithDateAndLastModifiedHeaders() throws Exception {
649adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertFullyCached(new MockResponse()
650adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
651adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
652adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Cache-Control: max-age=60"));
653adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    }
654adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
65521dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testMaxAgePreferredOverLowerSharedMaxAge() throws Exception {
65621dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertFullyCached(new MockResponse()
65721dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
65821dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: s-maxage=60")
65921dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: max-age=180"));
66021dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
66121dddca4064527116af7a1553de502c6d11138daJesse Wilson
66221dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testMaxAgePreferredOverHigherMaxAge() throws Exception {
66321dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertNotCached(new MockResponse()
66421dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
66521dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: s-maxage=180")
66621dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: max-age=60"));
66721dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
66821dddca4064527116af7a1553de502c6d11138daJesse Wilson
6690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodOptionsIsNotCached() throws Exception {
6700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("OPTIONS", false);
6710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodGetIsCached() throws Exception {
6740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("GET", true);
6750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodHeadIsNotCached() throws Exception {
6780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // We could support this but choose not to for implementation simplicity
6790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("HEAD", false);
6800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodPostIsNotCached() throws Exception {
6830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // We could support this but choose not to for implementation simplicity
6840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("POST", false);
6850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodPutIsNotCached() throws Exception {
6880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("PUT", false);
6890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodDeleteIsNotCached() throws Exception {
6920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("DELETE", false);
6930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestMethodTraceIsNotCached() throws Exception {
6960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testRequestMethod("TRACE", false);
6970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testRequestMethod(String requestMethod, boolean expectCached) throws Exception {
7000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        /*
7010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 1. seed the cache (potentially)
7020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 2. expect a cache hit or miss
7030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         */
7040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
7050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
7060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("X-Response-ID: 1"));
7070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
7080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("X-Response-ID: 2"));
7090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
7100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
7120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection request1 = (HttpURLConnection) url.openConnection();
7140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        request1.setRequestMethod(requestMethod);
7150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        addRequestBodyIfNecessary(requestMethod, request1);
7160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("1", request1.getHeaderField("X-Response-ID"));
7170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
718953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection request2 = url.openConnection();
7190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        if (expectCached) {
7200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertEquals("1", request1.getHeaderField("X-Response-ID"));
7210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } else {
7220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertEquals("2", request2.getHeaderField("X-Response-ID"));
7230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
7240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPostInvalidatesCache() throws Exception {
7270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testMethodInvalidates("POST");
7280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPutInvalidatesCache() throws Exception {
7310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testMethodInvalidates("PUT");
7320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testDeleteMethodInvalidatesCache() throws Exception {
7350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testMethodInvalidates("DELETE");
7360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testMethodInvalidates(String requestMethod) throws Exception {
7390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        /*
7400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 1. seed the cache
7410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 2. invalidate it
7420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 3. expect a cache miss
7430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         */
7440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
7450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
7460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
7470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("C"));
7480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
7490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
7510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
7530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection invalidate = (HttpURLConnection) url.openConnection();
7550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        invalidate.setRequestMethod(requestMethod);
7560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        addRequestBodyIfNecessary(requestMethod, invalidate);
7570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("B", readAscii(invalidate));
7580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("C", readAscii(url.openConnection()));
7600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testEtag() throws Exception {
7630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
7640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("ETag: v1"));
7650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(conditionalRequest.getHeaders().contains("If-None-Match: v1"));
7660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testEtagAndExpirationDateInThePast() throws Exception {
7690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
7700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
7710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("ETag: v1")
7720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
7730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
7740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
7750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-None-Match: v1"));
7760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
7770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testEtagAndExpirationDateInTheFuture() throws Exception {
7800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFullyCached(new MockResponse()
7810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("ETag: v1")
7820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
7830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
7840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheControlNoCache() throws Exception {
7870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse().addHeader("Cache-Control: no-cache"));
7880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheControlNoCacheAndExpirationDateInTheFuture() throws Exception {
7910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
7920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
7930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
7940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
7950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: no-cache"));
7960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
7970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
7980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPragmaNoCache() throws Exception {
8010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse().addHeader("Pragma: no-cache"));
8020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPragmaNoCacheAndExpirationDateInTheFuture() throws Exception {
8050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
8060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
8070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
8080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
8090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Pragma: no-cache"));
8100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = conditionalRequest.getHeaders();
8110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
8120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheControlNoStore() throws Exception {
8150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse().addHeader("Cache-Control: no-store"));
8160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testCacheControlNoStoreAndExpirationDateInTheFuture() throws Exception {
8190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNotCached(new MockResponse()
8200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
8210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
8220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Cache-Control: no-store"));
8230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testPartialRangeResponsesDoNotCorruptCache() throws Exception {
8260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        /*
8270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 1. request a range
8280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * 2. request a full document, expecting a cache miss
8290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         */
8300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("AA")
8310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_PARTIAL)
8320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
8330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Content-Range: bytes 1000-1001/2000"));
8340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("BB"));
8350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
8360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
8380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection range = url.openConnection();
8400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        range.addRequestProperty("Range", "bytes=1000-1001");
8410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("AA", readAscii(range));
8420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("BB", readAscii(url.openConnection()));
8440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testServerReturnsDocumentOlderThanCache() throws Exception {
8470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
8480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
8490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
8500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("B")
8510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-4, TimeUnit.HOURS)));
8520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
8530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
8550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
8570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
8580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testNonIdentityEncodingAndConditionalCache() throws Exception {
8610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNonIdentityEncodingCached(new MockResponse()
8620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
8630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
8640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testNonIdentityEncodingAndFullCache() throws Exception {
8670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNonIdentityEncodingCached(new MockResponse()
8680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
8690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
8700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void assertNonIdentityEncodingCached(MockResponse response) throws Exception {
8730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response
8740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody(gzip("ABCABCABC".getBytes("UTF-8")))
8750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Content-Encoding: gzip"));
8760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
8770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
8790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCABCABC", readAscii(server.getUrl("/").openConnection()));
8800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCABCABC", readAscii(server.getUrl("/").openConnection()));
8810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testExpiresDateBeforeModifiedDate() throws Exception {
8840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertConditionallyCached(new MockResponse()
8850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
8860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Expires: " + formatDate(-2, TimeUnit.HOURS)));
8870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
889adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    public void testRequestMaxAge() throws IOException {
890adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
891adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
892adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Date: " + formatDate(-1, TimeUnit.MINUTES))
893adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
894adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
895adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
896adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.play();
897adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
898adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
899adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
900adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        connection.addRequestProperty("Cache-Control", "max-age=30");
901adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals("B", readAscii(connection));
902adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    }
903adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
904adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    public void testRequestMinFresh() throws IOException {
905adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
906adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Cache-Control: max-age=60")
907adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
908adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
909adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
910adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.play();
911adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
912adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
913adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
914adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        connection.addRequestProperty("Cache-Control", "min-fresh=120");
915adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals("B", readAscii(connection));
916adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    }
917adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
918c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestMaxStale() throws IOException {
919c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("A")
920c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Cache-Control: max-age=120")
921c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Date: " + formatDate(-4, TimeUnit.MINUTES)));
922c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
923c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
924c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
925c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
926c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
927c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
928c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "max-stale=180");
929c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(connection));
930953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("110 HttpURLConnection \"Response is stale\"",
931953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                connection.getHeaderField("Warning"));
932c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
933c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
9342f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    public void testRequestMaxStaleNotHonoredWithMustRevalidate() throws IOException {
9352f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse().setBody("A")
9362f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .addHeader("Cache-Control: max-age=120, must-revalidate")
9372f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .addHeader("Date: " + formatDate(-4, TimeUnit.MINUTES)));
9382f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
9392f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
9402f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.play();
9412f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
9422f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
9432f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
9442f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        connection.addRequestProperty("Cache-Control", "max-stale=180");
9452f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("B", readAscii(connection));
9462f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    }
9472f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
948c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestOnlyIfCachedWithNoResponseCached() throws IOException {
949c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        // (no responses enqueued)
950c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
951c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
952953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
953c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "only-if-cached");
954c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertBadGateway(connection);
955c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
956c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
957c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestOnlyIfCachedWithFullResponseCached() throws IOException {
958c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("A")
959c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Cache-Control: max-age=30")
960c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
961c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
962c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
963c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
964c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
965c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "only-if-cached");
966c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
967c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
968c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
969c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestOnlyIfCachedWithConditionalResponseCached() throws IOException {
970c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("A")
971c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Cache-Control: max-age=30")
972c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson                .addHeader("Date: " + formatDate(-1, TimeUnit.MINUTES)));
973c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
974c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
975c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
976953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
977c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "only-if-cached");
978c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertBadGateway(connection);
979c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
980c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
981c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testRequestOnlyIfCachedWithUnhelpfulResponseCached() throws IOException {
982c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.enqueue(new MockResponse().setBody("A"));
983c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
984c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
985c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
986953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
987c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.addRequestProperty("Cache-Control", "only-if-cached");
988c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertBadGateway(connection);
989c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
990c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
99184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testRequestCacheControlNoCache() throws Exception {
99284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse()
99384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
99484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
99584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Cache-Control: max-age=60")
99684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .setBody("A"));
99784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
99884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
99984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
100084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URL url = server.getUrl("/");
100184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
100284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URLConnection connection = url.openConnection();
100384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.setRequestProperty("Cache-Control", "no-cache");
100484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("B", readAscii(connection));
100584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
100684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
100784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testRequestPragmaNoCache() throws Exception {
100884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse()
100984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
101084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
101184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Cache-Control: max-age=60")
101284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .setBody("A"));
101384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
101484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
101584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
101684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URL url = server.getUrl("/");
101784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
101884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URLConnection connection = url.openConnection();
101984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.setRequestProperty("Pragma", "no-cache");
102084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("B", readAscii(connection));
102184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
102284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
102384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testClientSuppliedIfModifiedSinceWithCachedResult() throws Exception {
102484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        MockResponse response = new MockResponse()
102584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("ETag: v3")
102684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Cache-Control: max-age=0");
102784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        String ifModifiedSinceDate = formatDate(-24, TimeUnit.HOURS);
102884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        RecordedRequest request = assertClientSuppliedCondition(
102984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                response, "If-Modified-Since", ifModifiedSinceDate);
103084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        List<String> headers = request.getHeaders();
103184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertTrue(headers.contains("If-Modified-Since: " + ifModifiedSinceDate));
103284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertFalse(headers.contains("If-None-Match: v3"));
103384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
103484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
103584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testClientSuppliedIfNoneMatchSinceWithCachedResult() throws Exception {
103684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        String lastModifiedDate = formatDate(-3, TimeUnit.MINUTES);
103784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        MockResponse response = new MockResponse()
103884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Last-Modified: " + lastModifiedDate)
103984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
104084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .addHeader("Cache-Control: max-age=0");
104184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        RecordedRequest request = assertClientSuppliedCondition(
104284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                response, "If-None-Match", "v1");
104384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        List<String> headers = request.getHeaders();
104484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertTrue(headers.contains("If-None-Match: v1"));
104584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertFalse(headers.contains("If-Modified-Since: " + lastModifiedDate));
104684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
104784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
104884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    private RecordedRequest assertClientSuppliedCondition(MockResponse seed, String conditionName,
104984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            String conditionValue) throws Exception {
105084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(seed.setBody("A"));
105184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
105284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
105384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
105484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        URL url = server.getUrl("/");
105584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
105684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
105784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
105884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.addRequestProperty(conditionName, conditionValue);
105984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, connection.getResponseCode());
106084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("", readAscii(connection));
106184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
106284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.takeRequest(); // seed
106384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        return server.takeRequest();
106484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
106584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
10669531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    public void testSetIfModifiedSince() throws Exception {
10679531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        Date since = new Date();
10689531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        server.enqueue(new MockResponse().setBody("A"));
10699531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        server.play();
10709531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson
10719531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        URL url = server.getUrl("/");
10729531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        URLConnection connection = url.openConnection();
10739531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        connection.setIfModifiedSince(since.getTime());
10749531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        assertEquals("A", readAscii(connection));
10759531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        RecordedRequest request = server.takeRequest();
10769531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        assertTrue(request.getHeaders().contains("If-Modified-Since: " + formatDate(since)));
10779531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    }
10789531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson
107984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testClientSuppliedConditionWithoutCachedResult() throws Exception {
108084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse()
108184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
108284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
108384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
108484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
108584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        String clientIfModifiedSince = formatDate(-24, TimeUnit.HOURS);
108684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.addRequestProperty("If-Modified-Since", clientIfModifiedSince);
108784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, connection.getResponseCode());
108884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("", readAscii(connection));
108984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
1090c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
109121dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testAuthorizationRequestHeaderPreventsCaching() throws Exception {
109221dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse()
109321dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.MINUTES))
109421dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: max-age=60")
109521dddca4064527116af7a1553de502c6d11138daJesse Wilson                .setBody("A"));
109621dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
109721dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.play();
109821dddca4064527116af7a1553de502c6d11138daJesse Wilson
109921dddca4064527116af7a1553de502c6d11138daJesse Wilson        URL url = server.getUrl("/");
1100953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = url.openConnection();
110121dddca4064527116af7a1553de502c6d11138daJesse Wilson        connection.addRequestProperty("Authorization", "password");
110221dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("A", readAscii(connection));
110321dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("B", readAscii(url.openConnection()));
110421dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
110521dddca4064527116af7a1553de502c6d11138daJesse Wilson
110621dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testAuthorizationResponseCachedWithSMaxAge() throws Exception {
110721dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertAuthorizationRequestFullyCached(new MockResponse()
110821dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: s-maxage=60"));
110921dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
111021dddca4064527116af7a1553de502c6d11138daJesse Wilson
111121dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testAuthorizationResponseCachedWithPublic() throws Exception {
111221dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertAuthorizationRequestFullyCached(new MockResponse()
111321dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: public"));
111421dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
111521dddca4064527116af7a1553de502c6d11138daJesse Wilson
111621dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testAuthorizationResponseCachedWithMustRevalidate() throws Exception {
111721dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertAuthorizationRequestFullyCached(new MockResponse()
111821dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: must-revalidate"));
111921dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
112021dddca4064527116af7a1553de502c6d11138daJesse Wilson
112121dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void assertAuthorizationRequestFullyCached(MockResponse response) throws Exception {
112221dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(response
112321dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Cache-Control: max-age=60")
112421dddca4064527116af7a1553de502c6d11138daJesse Wilson                .setBody("A"));
112521dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
112621dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.play();
112721dddca4064527116af7a1553de502c6d11138daJesse Wilson
112821dddca4064527116af7a1553de502c6d11138daJesse Wilson        URL url = server.getUrl("/");
1129953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection connection = url.openConnection();
113021dddca4064527116af7a1553de502c6d11138daJesse Wilson        connection.addRequestProperty("Authorization", "password");
113121dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("A", readAscii(connection));
113221dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("A", readAscii(url.openConnection()));
113321dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
113421dddca4064527116af7a1553de502c6d11138daJesse Wilson
1135953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public void testContentLocationDoesNotPopulateCache() throws Exception {
1136953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse()
1137953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Cache-Control: max-age=60")
1138953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Content-Location: /bar")
1139953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .setBody("A"));
1140953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
1141953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.play();
1142953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
1143953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/foo").openConnection()));
1144953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("B", readAscii(server.getUrl("/bar").openConnection()));
1145953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
1146953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
114719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    public void testUseCachesFalseDoesNotWriteToCache() throws Exception {
114819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.enqueue(new MockResponse()
114919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson                .addHeader("Cache-Control: max-age=60")
115019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson                .setBody("A").setBody("A"));
115119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
115219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.play();
115319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
115419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
115519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        connection.setUseCaches(false);
115619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertEquals("A", readAscii(connection));
115719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertEquals("B", readAscii(server.getUrl("/").openConnection()));
115819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    }
115919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
116019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    public void testUseCachesFalseDoesNotReadFromCache() throws Exception {
116119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.enqueue(new MockResponse()
116219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson                .addHeader("Cache-Control: max-age=60")
116319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson                .setBody("A").setBody("A"));
116419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
116519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.play();
116619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
116719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
116819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
116919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        connection.setUseCaches(false);
117019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertEquals("B", readAscii(connection));
117119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    }
117219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
117319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    public void testDefaultUseCachesSetsInitialValueOnly() throws Exception {
117419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URL url = new URL("http://localhost/");
117519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URLConnection c1 = url.openConnection();
117619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URLConnection c2 = url.openConnection();
117719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertTrue(c1.getDefaultUseCaches());
117819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        c1.setDefaultUseCaches(false);
117919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        try {
118019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            assertTrue(c1.getUseCaches());
118119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            assertTrue(c2.getUseCaches());
118219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            URLConnection c3 = url.openConnection();
118319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            assertFalse(c3.getUseCaches());
118419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        } finally {
118519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            c1.setDefaultUseCaches(true);
118619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        }
118719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    }
118819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
11892f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    public void testConnectionIsReturnedToPoolAfterConditionalSuccess() throws Exception {
11902f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse()
11912f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
11922f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .addHeader("Cache-Control: max-age=0")
11932f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .setBody("A"));
11942f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
11952f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
11962f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.play();
11972f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
11982f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/a").openConnection()));
11992f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("A", readAscii(server.getUrl("/a").openConnection()));
12002f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("B", readAscii(server.getUrl("/b").openConnection()));
12012f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
12022f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals(0, server.takeRequest().getSequenceNumber());
12032f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals(1, server.takeRequest().getSequenceNumber());
12042f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals(2, server.takeRequest().getSequenceNumber());
12052f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    }
12062f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
1207757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    public void testStatisticsConditionalCacheMiss() throws Exception {
1208757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse()
1209757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1210757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Cache-Control: max-age=0")
1211757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .setBody("A"));
1212757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
1213757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse().setBody("C"));
1214757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.play();
1215757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1216757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1217ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getRequestCount());
1218ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getNetworkCount());
1219ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getHitCount());
1220757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("B", readAscii(server.getUrl("/").openConnection()));
1221757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("C", readAscii(server.getUrl("/").openConnection()));
1222ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(3, cache.getRequestCount());
1223ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(3, cache.getNetworkCount());
1224ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getHitCount());
1225757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    }
1226757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1227757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    public void testStatisticsConditionalCacheHit() throws Exception {
1228757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse()
1229757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1230757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Cache-Control: max-age=0")
1231757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .setBody("A"));
1232757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1233757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1234757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.play();
1235757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1236757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1237ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getRequestCount());
1238ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getNetworkCount());
1239ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getHitCount());
1240757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1241757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1242ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(3, cache.getRequestCount());
1243ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(3, cache.getNetworkCount());
1244ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getHitCount());
1245757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    }
1246757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1247757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    public void testStatisticsFullCacheHit() throws Exception {
1248757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse()
1249757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Cache-Control: max-age=60")
1250757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .setBody("A"));
1251757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.play();
1252757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1253757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1254ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getRequestCount());
1255ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getNetworkCount());
1256ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(0, cache.getHitCount());
1257757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1258757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
1259ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(3, cache.getRequestCount());
1260ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, cache.getNetworkCount());
1261ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, cache.getHitCount());
1262757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    }
1263757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
12646f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryMatchesChangedRequestHeaderField() throws Exception {
12656f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
12666f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
12676f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Accept-Language")
12686f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
12696f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
12706f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
12716f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
12726f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URL url = server.getUrl("/");
12736f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        HttpURLConnection frConnection = (HttpURLConnection) url.openConnection();
12746f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        frConnection.addRequestProperty("Accept-Language", "fr-CA");
12756f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(frConnection));
12766f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
12776f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        HttpURLConnection enConnection = (HttpURLConnection) url.openConnection();
12786f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        enConnection.addRequestProperty("Accept-Language", "en-US");
12796f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("B", readAscii(enConnection));
12806f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
12816f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
12826f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryMatchesUnchangedRequestHeaderField() throws Exception {
12836f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
12846f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
12856f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Accept-Language")
12866f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
12876f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
12886f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
12896f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
12906f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URL url = server.getUrl("/");
12916f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection1 = url.openConnection();
12926f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Language", "fr-CA");
12936f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection1));
12946f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection2 = url.openConnection();
12956f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("Accept-Language", "fr-CA");
12966f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection2));
12976f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
12986f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
12996f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryMatchesAbsentRequestHeaderField() throws Exception {
13006f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
13016f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
13026f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Foo")
13036f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
13046f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
13056f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
13066f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13076f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
13086f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
13096f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
13106f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13116f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryMatchesAddedRequestHeaderField() throws Exception {
13126f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
13136f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
13146f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Foo")
13156f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
13166f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
13176f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
13186f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13196f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
13206f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection fooConnection = server.getUrl("/").openConnection();
13216f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        fooConnection.addRequestProperty("Foo", "bar");
13226f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("B", readAscii(fooConnection));
13236f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
13246f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13256f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryMatchesRemovedRequestHeaderField() throws Exception {
13266f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
13276f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
13286f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Foo")
13296f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
13306f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
13316f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
13326f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13336f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection fooConnection = server.getUrl("/").openConnection();
13346f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        fooConnection.addRequestProperty("Foo", "bar");
13356f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(fooConnection));
13366f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("B", readAscii(server.getUrl("/").openConnection()));
13376f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
13386f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13396f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryFieldsAreCaseInsensitive() throws Exception {
13406f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
13416f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
13426f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: ACCEPT-LANGUAGE")
13436f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
13446f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
13456f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
13466f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13476f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URL url = server.getUrl("/");
13486f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection1 = url.openConnection();
13496f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Language", "fr-CA");
13506f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection1));
13516f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection2 = url.openConnection();
13526f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("accept-language", "fr-CA");
13536f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection2));
13546f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
13556f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13566f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryMultipleFieldsWithMatch() throws Exception {
13576f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
13586f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
13596f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Accept-Language, Accept-Charset")
13606f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Accept-Encoding")
13616f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
13626f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
13636f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
13646f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13656f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URL url = server.getUrl("/");
13666f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection1 = url.openConnection();
13676f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Language", "fr-CA");
13686f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Charset", "UTF-8");
13696f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Encoding", "identity");
13706f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection1));
13716f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection2 = url.openConnection();
13726f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("Accept-Language", "fr-CA");
13736f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("Accept-Charset", "UTF-8");
13746f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("Accept-Encoding", "identity");
13756f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection2));
13766f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
13776f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13786f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryMultipleFieldsWithNoMatch() throws Exception {
13796f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
13806f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
13816f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Accept-Language, Accept-Charset")
13826f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Accept-Encoding")
13836f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
13846f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
13856f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
13866f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
13876f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URL url = server.getUrl("/");
13886f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection frConnection = url.openConnection();
13896f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        frConnection.addRequestProperty("Accept-Language", "fr-CA");
13906f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        frConnection.addRequestProperty("Accept-Charset", "UTF-8");
13916f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        frConnection.addRequestProperty("Accept-Encoding", "identity");
13926f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(frConnection));
13936f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection enConnection = url.openConnection();
13946f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        enConnection.addRequestProperty("Accept-Language", "en-CA");
13956f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        enConnection.addRequestProperty("Accept-Charset", "UTF-8");
13966f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        enConnection.addRequestProperty("Accept-Encoding", "identity");
13976f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("B", readAscii(enConnection));
13986f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
13996f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14006f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryMultipleFieldValuesWithMatch() throws Exception {
14016f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
14026f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
14036f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Accept-Language")
14046f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
14056f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
14066f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
14076f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14086f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URL url = server.getUrl("/");
14096f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection1 = url.openConnection();
14106f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Language", "fr-CA, fr-FR");
14116f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Language", "en-US");
14126f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection1));
14136f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14146f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection2 = url.openConnection();
14156f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("Accept-Language", "fr-CA, fr-FR");
14166f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("Accept-Language", "en-US");
14176f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection2));
14186f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
14196f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14206f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryMultipleFieldValuesWithNoMatch() throws Exception {
14216f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
14226f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
14236f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Accept-Language")
14246f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
14256f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
14266f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
14276f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14286f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URL url = server.getUrl("/");
14296f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection1 = url.openConnection();
14306f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Language", "fr-CA, fr-FR");
14316f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Language", "en-US");
14326f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection1));
14336f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14346f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URLConnection connection2 = url.openConnection();
14356f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("Accept-Language", "fr-CA");
14366f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("Accept-Language", "en-US");
14376f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("B", readAscii(connection2));
14386f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
14396f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14406f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryAsterisk() throws Exception {
14416f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
14426f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
14436f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: *")
14446f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
14456f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
14466f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
14476f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14486f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(server.getUrl("/").openConnection()));
14496f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("B", readAscii(server.getUrl("/").openConnection()));
14506f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
14516f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14526f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    public void testVaryAndHttps() throws Exception {
14536f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
14546f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
14556f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse()
14566f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Cache-Control: max-age=60")
14576f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .addHeader("Vary: Accept-Language")
14586f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson                .setBody("A"));
14596f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.enqueue(new MockResponse().setBody("B"));
14606f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        server.play();
14616f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14626f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        URL url = server.getUrl("/");
14636f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        HttpsURLConnection connection1 = (HttpsURLConnection) url.openConnection();
14646f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
14656f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection1.addRequestProperty("Accept-Language", "en-US");
14666f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection1));
14676f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
14686f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        HttpsURLConnection connection2 = (HttpsURLConnection) url.openConnection();
14696f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
14706f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        connection2.addRequestProperty("Accept-Language", "en-US");
14716f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson        assertEquals("A", readAscii(connection2));
14726f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson    }
14736f778cc173cc60ec184e8ca54a16ad10cf55f2cfJesse Wilson
1474e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson    public void testDiskWriteFailureCacheDegradation() throws Exception {
1475e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        Deque<InvocationHandler> writeHandlers = mockOs.getHandlers("write");
1476e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        int i = 0;
1477e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        boolean hasMoreScenarios = true;
1478e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        while (hasMoreScenarios) {
1479e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            mockOs.enqueueNormal("write", i++);
1480e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            mockOs.enqueueFault("write");
1481e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            exercisePossiblyFaultyCache(false);
1482e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            hasMoreScenarios = writeHandlers.isEmpty();
1483e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            writeHandlers.clear();
1484e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        }
1485e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        System.out.println("Exercising the cache performs " + (i - 1) + " writes.");
1486e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson    }
1487e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson
1488e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson    public void testDiskReadFailureCacheDegradation() throws Exception {
1489e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        Deque<InvocationHandler> readHandlers = mockOs.getHandlers("read");
1490e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        int i = 0;
1491e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        boolean hasMoreScenarios = true;
1492e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        while (hasMoreScenarios) {
1493e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            mockOs.enqueueNormal("read", i++);
1494e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            mockOs.enqueueFault("read");
1495e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            exercisePossiblyFaultyCache(true);
1496e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            hasMoreScenarios = readHandlers.isEmpty();
1497e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            readHandlers.clear();
1498e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        }
1499e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        System.out.println("Exercising the cache performs " + (i - 1) + " reads.");
1500e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson    }
1501e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson
15020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
15030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * @param delta the offset from the current date to use. Negative
15040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     *     values yield dates in the past; positive values yield dates in the
15050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     *     future.
15060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
1507953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private String formatDate(long delta, TimeUnit timeUnit) {
15089531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        return formatDate(new Date(System.currentTimeMillis() + timeUnit.toMillis(delta)));
15099531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    }
15109531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson
15119531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    private String formatDate(Date date) {
1512953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        DateFormat rfc1123 = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
1513953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        rfc1123.setTimeZone(TimeZone.getTimeZone("UTC"));
1514953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        return rfc1123.format(date);
15150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
15160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
15170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void addRequestBodyIfNecessary(String requestMethod, HttpURLConnection invalidate)
15180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            throws IOException {
15190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        if (requestMethod.equals("POST") || requestMethod.equals("PUT")) {
15200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            invalidate.setDoOutput(true);
15210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            OutputStream requestBody = invalidate.getOutputStream();
15220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            requestBody.write('x');
15230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            requestBody.close();
15240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
15250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
15260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
15270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void assertNotCached(MockResponse response) throws Exception {
15280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response.setBody("A"));
15290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
15300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
15310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
15320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
15330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
15340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("B", readAscii(url.openConnection()));
15350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
15360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1537e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson    private void exercisePossiblyFaultyCache(boolean permitReadBodyFailures) throws Exception {
1538e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        server.shutdown();
1539e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        server = new MockWebServer();
1540e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        server.enqueue(new MockResponse()
1541e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson                .addHeader("Cache-Control: max-age=60")
1542e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson                .setBody("A"));
1543e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        server.enqueue(new MockResponse().setBody("B"));
1544e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        server.play();
1545e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson
1546e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        URL url = server.getUrl("/" + UUID.randomUUID());
1547e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
1548e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson
1549e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        URLConnection connection = url.openConnection();
1550e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        InputStream in = connection.getInputStream();
1551e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        try {
1552e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            int bodyChar = in.read();
1553e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            assertTrue(bodyChar == 'A' || bodyChar == 'B');
1554e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            assertEquals(-1, in.read());
1555e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        } catch (IOException e) {
1556e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            if (!permitReadBodyFailures) {
1557e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson                throw e;
1558e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson            }
1559e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson        }
1560e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson    }
1561e5eb80e6adaab18ff7372adcd09f9e1af3a76871Jesse Wilson
15620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
15630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * @return the request with the conditional get headers.
15640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
15650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private RecordedRequest assertConditionallyCached(MockResponse response) throws Exception {
15660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // scenario 1: condition succeeds
15670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response.setBody("A"));
15680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
15690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
15700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // scenario 2: condition fails
15710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response.setBody("B"));
15720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("C"));
15730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
15740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
15750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
15760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL valid = server.getUrl("/valid");
15770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(valid.openConnection()));
15780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(valid.openConnection()));
15790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
15800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL invalid = server.getUrl("/invalid");
15810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("B", readAscii(invalid.openConnection()));
15820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("C", readAscii(invalid.openConnection()));
15830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
15840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.takeRequest(); // regular get
15850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return server.takeRequest(); // conditional get
15860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
15870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
15880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void assertFullyCached(MockResponse response) throws Exception {
15890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response.setBody("A"));
15900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response.setBody("B"));
15910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
15920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
15930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
15940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
15950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", readAscii(url.openConnection()));
15960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
15970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
15980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
15990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Shortens the body of {@code response} but not the corresponding headers.
16000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Only useful to test how clients respond to the premature conclusion of
16010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * the HTTP body.
16020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
16030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private MockResponse truncateViolently(MockResponse response, int numBytesToKeep) {
16040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        response.setSocketPolicy(DISCONNECT_AT_END);
16050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        List<String> headers = new ArrayList<String>(response.getHeaders());
16060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        response.setBody(Arrays.copyOfRange(response.getBody(), 0, numBytesToKeep));
16070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        response.getHeaders().clear();
16080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        response.getHeaders().addAll(headers);
16090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return response;
16100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
16110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
16120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
16130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Reads {@code count} characters from the stream. If the stream is
16140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * exhausted before {@code count} characters can be read, the remaining
16150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * characters are returned and the stream is closed.
16160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
16170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private String readAscii(URLConnection connection, int count) throws IOException {
161884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpURLConnection httpConnection = (HttpURLConnection) connection;
161984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        InputStream in = httpConnection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST
162084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                ? connection.getInputStream()
162184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson                : httpConnection.getErrorStream();
16220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        StringBuilder result = new StringBuilder();
16230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        for (int i = 0; i < count; i++) {
16240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            int value = in.read();
16250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            if (value == -1) {
16260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                in.close();
16270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                break;
16280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
16290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            result.append((char) value);
16300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
16310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return result.toString();
16320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
16330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
16340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private String readAscii(URLConnection connection) throws IOException {
16350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return readAscii(connection, Integer.MAX_VALUE);
16360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
16370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
16380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void reliableSkip(InputStream in, int length) throws IOException {
16390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        while (length > 0) {
16400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            length -= in.skip(length);
16410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
16420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
16430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1644953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private void assertBadGateway(HttpURLConnection connection) throws IOException {
1645c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        try {
1646c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson            connection.getInputStream();
1647c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson            fail();
1648c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        } catch (FileNotFoundException expected) {
1649c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        }
1650953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals(HttpURLConnection.HTTP_BAD_GATEWAY, connection.getResponseCode());
1651953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals(-1, connection.getErrorStream().read());
1652c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
1653c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
16540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    enum TransferKind {
16550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        CHUNKED() {
16560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override void setBody(MockResponse response, byte[] content, int chunkSize)
16570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    throws IOException {
16580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                response.setChunkedBody(content, chunkSize);
16590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
16600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        },
16610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        FIXED_LENGTH() {
16620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override void setBody(MockResponse response, byte[] content, int chunkSize) {
16630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                response.setBody(content);
16640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
16650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        },
16660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        END_OF_STREAM() {
16670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            @Override void setBody(MockResponse response, byte[] content, int chunkSize) {
16680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                response.setBody(content);
16690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                response.setSocketPolicy(DISCONNECT_AT_END);
16700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                for (Iterator<String> h = response.getHeaders().iterator(); h.hasNext(); ) {
16710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    if (h.next().startsWith("Content-Length:")) {
16720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                        h.remove();
16730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                        break;
16740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    }
16750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                }
16760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
16770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        };
16780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
16790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        abstract void setBody(MockResponse response, byte[] content, int chunkSize)
16800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                throws IOException;
16810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
16820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        void setBody(MockResponse response, String content, int chunkSize) throws IOException {
16830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            setBody(response, content.getBytes("UTF-8"), chunkSize);
16840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
16850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
16860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
16870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private <T> List<T> toListOrNull(T[] arrayOrNull) {
16880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return arrayOrNull != null ? Arrays.asList(arrayOrNull) : null;
16890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
16900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
16910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
16920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Returns a gzipped copy of {@code bytes}.
16930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
16940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public byte[] gzip(byte[] bytes) throws IOException {
16950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
16960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        OutputStream gzippedOut = new GZIPOutputStream(bytesOut);
16970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        gzippedOut.write(bytes);
16980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        gzippedOut.close();
16990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return bytesOut.toByteArray();
17000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
17010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1702433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson    private class InsecureResponseCache extends ResponseCache {
17030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        @Override public CacheRequest put(URI uri, URLConnection connection) throws IOException {
1704433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            return cache.put(uri, connection);
17050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
17060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
17070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        @Override public CacheResponse get(URI uri, String requestMethod,
17080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                Map<String, List<String>> requestHeaders) throws IOException {
1709433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            final CacheResponse response = cache.get(uri, requestMethod, requestHeaders);
17100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            if (response instanceof SecureCacheResponse) {
17110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                return new CacheResponse() {
17120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    @Override public InputStream getBody() throws IOException {
17130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                        return response.getBody();
17140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    }
17150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    @Override public Map<String, List<String>> getHeaders() throws IOException {
17160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                        return response.getHeaders();
17170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    }
17180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                };
17190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
17200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            return response;
17210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
17220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
17230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson}
1724