1e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller/*
2e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Copyright (C) 2011 The Android Open Source Project
3e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller *
4e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License");
5e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * you may not use this file except in compliance with the License.
6e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * You may obtain a copy of the License at
7e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller *
8e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller *      http://www.apache.org/licenses/LICENSE-2.0
9e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller *
10e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Unless required by applicable law or agreed to in writing, software
11e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS,
12e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * See the License for the specific language governing permissions and
14e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * limitations under the License.
15e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller */
16e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
17e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerpackage com.squareup.okhttp;
18e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
19e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport com.squareup.okhttp.internal.Internal;
20e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport com.squareup.okhttp.internal.SslContextBuilder;
21e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport com.squareup.okhttp.internal.Util;
2271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport com.squareup.okhttp.internal.io.InMemoryFileSystem;
23e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport com.squareup.okhttp.mockwebserver.MockResponse;
24e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport com.squareup.okhttp.mockwebserver.MockWebServer;
25e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport com.squareup.okhttp.mockwebserver.RecordedRequest;
26e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.io.File;
27e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.io.IOException;
28e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.net.CookieHandler;
29e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.net.CookieManager;
30e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.net.HttpCookie;
31e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.net.HttpURLConnection;
32e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.net.ResponseCache;
33e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.security.Principal;
34e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.security.cert.Certificate;
35e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.text.DateFormat;
36e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.text.SimpleDateFormat;
37e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.ArrayList;
38e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.Arrays;
39e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.Date;
40e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.Iterator;
41e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.List;
42e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.Locale;
43e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.NoSuchElementException;
44e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.TimeZone;
45e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.concurrent.TimeUnit;
46e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.concurrent.atomic.AtomicReference;
473be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport javax.net.ssl.HostnameVerifier;
483be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport javax.net.ssl.SSLContext;
493be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport javax.net.ssl.SSLSession;
503be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport okio.Buffer;
513be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport okio.BufferedSink;
523be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport okio.BufferedSource;
533be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport okio.GzipSink;
543be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport okio.Okio;
553be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport org.junit.After;
563be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport org.junit.Before;
573be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport org.junit.Rule;
583be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport org.junit.Test;
59e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
60e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.mockwebserver.SocketPolicy.DISCONNECT_AT_END;
61e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertEquals;
62e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertFalse;
63e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertNotNull;
64e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertNull;
65e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertTrue;
66e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.fail;
67e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
68e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller/** Test caching with {@link OkUrlFactory}. */
69e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerpublic final class CacheTest {
70e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private static final HostnameVerifier NULL_HOSTNAME_VERIFIER = new HostnameVerifier() {
71e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    @Override public boolean verify(String s, SSLSession sslSession) {
72e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      return true;
73e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
74e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  };
75e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
7671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Rule public MockWebServer server = new MockWebServer();
7771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Rule public MockWebServer server2 = new MockWebServer();
786c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer  @Rule public InMemoryFileSystem fileSystem = new InMemoryFileSystem();
79e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
8071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  private final SSLContext sslContext = SslContextBuilder.localhost();
81e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private final OkHttpClient client = new OkHttpClient();
82e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private Cache cache;
83e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private final CookieManager cookieManager = new CookieManager();
84e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
85e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Before public void setUp() throws Exception {
86e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.setProtocolNegotiationEnabled(false);
8771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    cache = new Cache(new File("/cache/"), Integer.MAX_VALUE, fileSystem);
88e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.setCache(cache);
89e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    CookieHandler.setDefault(cookieManager);
90e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
91e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
92e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @After public void tearDown() throws Exception {
93e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    ResponseCache.setDefault(null);
94e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    CookieHandler.setDefault(null);
956c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    cache.delete();
96e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
97e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
98e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
99e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Test that response caching is consistent with the RI and the spec.
100e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4
101e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
102e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void responseCachingByResponseCode() throws Exception {
103e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Test each documented HTTP/1.1 code, plus the first unused value in each range.
104e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
105e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
106e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // We can't test 100 because it's not really a response.
107e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // assertCached(false, 100);
108e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 101);
109e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 102);
110e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(true,  200);
111e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 201);
112e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 202);
113e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(true,  203);
114e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(true,  204);
115e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 205);
116e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 206); //Electing to not cache partial responses
117e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 207);
118e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(true,  300);
119e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(true,  301);
120e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(true,  302);
121e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 303);
122e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 304);
123e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 305);
124e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 306);
125e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(true,  307);
126e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(true,  308);
127e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 400);
128e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 401);
129e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 402);
130e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 403);
131e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(true,  404);
132e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(true,  405);
133e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 406);
134e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 408);
135e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 409);
136e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // the HTTP spec permits caching 410s, but the RI doesn't.
137e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(true,  410);
138e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 411);
139e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 412);
140e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 413);
141e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(true,  414);
142e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 415);
143e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 416);
144e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 417);
145e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 418);
146e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
147e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 500);
148e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(true,  501);
149e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 502);
150e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 503);
151e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 504);
152e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 505);
153e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCached(false, 506);
154e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
155e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
156e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void assertCached(boolean shouldPut, int responseCode) throws Exception {
157e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server = new MockWebServer();
158e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    MockResponse mockResponse = new MockResponse()
159e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
160e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
161e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(responseCode)
162e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("ABCDE")
163e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("WWW-Authenticate: challenge");
164e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    if (responseCode == HttpURLConnection.HTTP_PROXY_AUTH) {
165e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      mockResponse.addHeader("Proxy-Authenticate: Basic realm=\"protected area\"");
166e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } else if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
167e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      mockResponse.addHeader("WWW-Authenticate: Basic realm=\"protected area\"");
16871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    } else if (responseCode == HttpURLConnection.HTTP_NO_CONTENT
16971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        || responseCode == HttpURLConnection.HTTP_RESET) {
17071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      mockResponse.setBody(""); // We forbid bodies for 204 and 205.
171e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
172e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(mockResponse);
173e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.start();
174e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
175e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
17671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
177e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
178e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
179e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(responseCode, response.code());
180e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
181e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Exhaust the content stream.
182e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    response.body().string();
183e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
184e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response cached = cache.get(request);
185e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    if (shouldPut) {
186e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertNotNull(Integer.toString(responseCode), cached);
187e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      cached.body().close();
188e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } else {
189e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertNull(Integer.toString(responseCode), cached);
190e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
191e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.shutdown(); // tearDown() isn't sufficient; this test starts multiple servers
192e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
193e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
194e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void responseCachingAndInputStreamSkipWithFixedLength() throws IOException {
195e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testResponseCaching(TransferKind.FIXED_LENGTH);
196e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
197e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
198e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void responseCachingAndInputStreamSkipWithChunkedEncoding() throws IOException {
199e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testResponseCaching(TransferKind.CHUNKED);
200e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
201e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
202e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void responseCachingAndInputStreamSkipWithNoLengthHeaders() throws IOException {
203e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testResponseCaching(TransferKind.END_OF_STREAM);
204e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
205e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
206e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
207e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Skipping bytes in the input stream caused ResponseCache corruption.
208e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * http://code.google.com/p/android/issues/detail?id=8175
209e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
210e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void testResponseCaching(TransferKind transferKind) throws IOException {
211e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    MockResponse mockResponse = new MockResponse()
212e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
213e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
214e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setStatus("HTTP/1.1 200 Fantastic");
215e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    transferKind.setBody(mockResponse, "I love puppies but hate spiders", 1);
216e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(mockResponse);
217e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
218e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Make sure that calling skip() doesn't omit bytes from the cache.
21971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Request request = new Request.Builder().url(server.url("/")).build();
220e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request).execute();
221e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
222e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    BufferedSource in1 = response1.body().source();
223e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("I love ", in1.readUtf8("I love ".length()));
224e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    in1.skip("puppies but hate ".length());
225e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("spiders", in1.readUtf8("spiders".length()));
226e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(in1.exhausted());
227e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    in1.close();
228e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getWriteSuccessCount());
229e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getWriteAbortCount());
230e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
231e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = client.newCall(request).execute();
232e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    BufferedSource in2 = response2.body().source();
233e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("I love puppies but hate spiders",
234e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        in2.readUtf8("I love puppies but hate spiders".length()));
235e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(200, response2.code());
236e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("Fantastic", response2.message());
237e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
238e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(in2.exhausted());
239e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    in2.close();
240e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getWriteSuccessCount());
241e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getWriteAbortCount());
242e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, cache.getRequestCount());
243e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getHitCount());
244e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
245e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
246e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void secureResponseCaching() throws IOException {
247e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.useHttps(sslContext.getSocketFactory(), false);
248e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
249e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
250e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
251e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("ABC"));
252e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
253e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.setSslSocketFactory(sslContext.getSocketFactory());
254e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
255e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
25671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Request request = new Request.Builder().url(server.url("/")).build();
257e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request).execute();
258e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    BufferedSource in = response1.body().source();
259e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("ABC", in.readUtf8());
260e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
261e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // OpenJDK 6 fails on this line, complaining that the connection isn't open yet
262e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String suite = response1.handshake().cipherSuite();
263e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    List<Certificate> localCerts = response1.handshake().localCertificates();
264e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    List<Certificate> serverCerts = response1.handshake().peerCertificates();
265e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Principal peerPrincipal = response1.handshake().peerPrincipal();
266e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Principal localPrincipal = response1.handshake().localPrincipal();
267e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
268e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = client.newCall(request).execute(); // Cached!
2696c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    assertEquals("ABC", response2.body().string());
270e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
271e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, cache.getRequestCount());
272e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getNetworkCount());
273e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getHitCount());
274e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
275e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(suite, response2.handshake().cipherSuite());
276e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(localCerts, response2.handshake().localCertificates());
277e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(serverCerts, response2.handshake().peerCertificates());
278e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(peerPrincipal, response2.handshake().peerPrincipal());
279e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(localPrincipal, response2.handshake().localPrincipal());
280e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
281e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
282e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void responseCachingAndRedirects() throws Exception {
283e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
284e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
285e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
286e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
287e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Location: /foo"));
288e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
289e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
290e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
291e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("ABC"));
292e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
293e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("DEF"));
294e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
29571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Request request = new Request.Builder().url(server.url("/")).build();
296e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request).execute();
297e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("ABC", response1.body().string());
298e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
299e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = client.newCall(request).execute(); // Cached!
300e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("ABC", response2.body().string());
301e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
302e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(4, cache.getRequestCount()); // 2 requests + 2 redirects
303e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, cache.getNetworkCount());
304e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, cache.getHitCount());
305e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
306e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
307e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void redirectToCachedResult() throws Exception {
308e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
309e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
310e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("ABC"));
311e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
312e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
313e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Location: /foo"));
314e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
315e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("DEF"));
316e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
31771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Request request1 = new Request.Builder().url(server.url("/foo")).build();
318e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request1).execute();
319e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("ABC", response1.body().string());
320e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest recordedRequest1 = server.takeRequest();
321e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("GET /foo HTTP/1.1", recordedRequest1.getRequestLine());
322e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, recordedRequest1.getSequenceNumber());
323e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
32471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Request request2 = new Request.Builder().url(server.url("/bar")).build();
325e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = client.newCall(request2).execute();
326e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("ABC", response2.body().string());
327e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest recordedRequest2 = server.takeRequest();
328e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("GET /bar HTTP/1.1", recordedRequest2.getRequestLine());
329e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, recordedRequest2.getSequenceNumber());
330e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
331e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // an unrelated request should reuse the pooled connection
33271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Request request3 = new Request.Builder().url(server.url("/baz")).build();
333e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response3 = client.newCall(request3).execute();
334e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("DEF", response3.body().string());
335e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest recordedRequest3 = server.takeRequest();
336e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("GET /baz HTTP/1.1", recordedRequest3.getRequestLine());
337e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, recordedRequest3.getSequenceNumber());
338e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
339e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
340e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void secureResponseCachingAndRedirects() throws IOException {
341e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.useHttps(sslContext.getSocketFactory(), false);
342e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
343e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
344e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
345e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
346e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Location: /foo"));
347e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
348e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
349e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
350e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("ABC"));
351e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
352e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("DEF"));
353e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
354e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.setSslSocketFactory(sslContext.getSocketFactory());
355e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
356e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
35771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response1 = get(server.url("/"));
358e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("ABC", response1.body().string());
359e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNotNull(response1.handshake().cipherSuite());
360e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
361e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Cached!
36271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response2 = get(server.url("/"));
363e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("ABC", response2.body().string());
364e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNotNull(response2.handshake().cipherSuite());
365e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
366e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(4, cache.getRequestCount()); // 2 direct + 2 redirect = 4
367e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, cache.getHitCount());
368e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(response1.handshake().cipherSuite(), response2.handshake().cipherSuite());
369e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
370e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
371e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
372e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * We've had bugs where caching and cross-protocol redirects yield class
373e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * cast exceptions internal to the cache because we incorrectly assumed that
374e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * HttpsURLConnection was always HTTPS and HttpURLConnection was always HTTP;
375e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * in practice redirects mean that each can do either.
376e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   *
377e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * https://github.com/square/okhttp/issues/214
378e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
379e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void secureResponseCachingAndProtocolRedirects() throws IOException {
380e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server2.useHttps(sslContext.getSocketFactory(), false);
381e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server2.enqueue(new MockResponse()
382e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
383e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
384e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("ABC"));
385e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server2.enqueue(new MockResponse()
386e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("DEF"));
387e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
388e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
389e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
390e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
391e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
39271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .addHeader("Location: " + server2.url("/")));
393e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
394e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.setSslSocketFactory(sslContext.getSocketFactory());
395e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
396e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
39771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response1 = get(server.url("/"));
398e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("ABC", response1.body().string());
399e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
400e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Cached!
40171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response2 = get(server.url("/"));
402e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("ABC", response2.body().string());
403e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
404e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(4, cache.getRequestCount()); // 2 direct + 2 redirect = 4
405e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, cache.getHitCount());
406e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
407e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
408e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void foundCachedWithExpiresHeader() throws Exception {
409e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    temporaryRedirectCachedWithCachingHeader(302, "Expires", formatDate(1, TimeUnit.HOURS));
410e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
411e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
412e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void foundCachedWithCacheControlHeader() throws Exception {
413e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    temporaryRedirectCachedWithCachingHeader(302, "Cache-Control", "max-age=60");
414e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
415e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
416e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void temporaryRedirectCachedWithExpiresHeader() throws Exception {
417e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    temporaryRedirectCachedWithCachingHeader(307, "Expires", formatDate(1, TimeUnit.HOURS));
418e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
419e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
420e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void temporaryRedirectCachedWithCacheControlHeader() throws Exception {
421e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    temporaryRedirectCachedWithCachingHeader(307, "Cache-Control", "max-age=60");
422e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
423e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
424e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void foundNotCachedWithoutCacheHeader() throws Exception {
425e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    temporaryRedirectNotCachedWithoutCachingHeader(302);
426e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
427e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
428e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void temporaryRedirectNotCachedWithoutCacheHeader() throws Exception {
429e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    temporaryRedirectNotCachedWithoutCachingHeader(307);
430e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
431e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
432e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void temporaryRedirectCachedWithCachingHeader(
433e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      int responseCode, String headerName, String headerValue) throws Exception {
434e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
435e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(responseCode)
436e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader(headerName, headerValue)
437e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Location", "/a"));
438e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
439e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader(headerName, headerValue)
440e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("a"));
441e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
442e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("b"));
443e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
444e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("c"));
445e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
44671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
447e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("a", get(url).body().string());
448e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("a", get(url).body().string());
449e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
450e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
451e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void temporaryRedirectNotCachedWithoutCachingHeader(int responseCode) throws Exception {
452e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
453e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(responseCode)
454e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Location", "/a"));
455e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
456e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("a"));
457e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
458e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("b"));
459e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
46071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
461e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("a", get(url).body().string());
462e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("b", get(url).body().string());
463e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
464e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
4656c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer  /** https://github.com/square/okhttp/issues/2198 */
4666c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer  @Test public void cachedRedirect() throws IOException {
4676c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    server.enqueue(new MockResponse()
4686c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer        .setResponseCode(301)
4696c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer        .addHeader("Cache-Control: max-age=60")
4706c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer        .addHeader("Location: /bar"));
4716c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    server.enqueue(new MockResponse()
4726c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer        .setBody("ABC"));
4736c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    server.enqueue(new MockResponse()
4746c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer        .setBody("ABC"));
4756c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer
4766c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    Request request1 = new Request.Builder().url(server.url("/")).build();
4776c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    Response response1 = client.newCall(request1).execute();
4786c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    assertEquals("ABC", response1.body().string());
4796c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer
4806c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    Request request2 = new Request.Builder().url(server.url("/")).build();
4816c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    Response response2 = client.newCall(request2).execute();
4826c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    assertEquals("ABC", response2.body().string());
4836c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer  }
4846c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer
485e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void serverDisconnectsPrematurelyWithContentLengthHeader() throws IOException {
486e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testServerPrematureDisconnect(TransferKind.FIXED_LENGTH);
487e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
488e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
489e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void serverDisconnectsPrematurelyWithChunkedEncoding() throws IOException {
490e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testServerPrematureDisconnect(TransferKind.CHUNKED);
491e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
492e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
493e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void serverDisconnectsPrematurelyWithNoLengthHeaders() throws IOException {
494e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Intentionally empty. This case doesn't make sense because there's no
495e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // such thing as a premature disconnect when the disconnect itself
496e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // indicates the end of the data stream.
497e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
498e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
499e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void testServerPrematureDisconnect(TransferKind transferKind) throws IOException {
500e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    MockResponse mockResponse = new MockResponse();
501e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    transferKind.setBody(mockResponse, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 16);
502e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(truncateViolently(mockResponse, 16));
503e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
504e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("Request #2"));
505e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
50671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    BufferedSource bodySource = get(server.url("/")).body().source();
507e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("ABCDE", bodySource.readUtf8Line());
508e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
509e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      bodySource.readUtf8Line();
510e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail("This implementation silently ignored a truncated HTTP body.");
511e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IOException expected) {
512e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } finally {
513e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      bodySource.close();
514e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
515e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
516e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getWriteAbortCount());
517e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getWriteSuccessCount());
51871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response = get(server.url("/"));
519e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("Request #2", response.body().string());
520e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getWriteAbortCount());
521e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getWriteSuccessCount());
522e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
523e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
524e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void clientPrematureDisconnectWithContentLengthHeader() throws IOException {
525e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testClientPrematureDisconnect(TransferKind.FIXED_LENGTH);
526e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
527e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
528e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void clientPrematureDisconnectWithChunkedEncoding() throws IOException {
529e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testClientPrematureDisconnect(TransferKind.CHUNKED);
530e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
531e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
532e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void clientPrematureDisconnectWithNoLengthHeaders() throws IOException {
533e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testClientPrematureDisconnect(TransferKind.END_OF_STREAM);
534e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
535e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
536e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void testClientPrematureDisconnect(TransferKind transferKind) throws IOException {
537e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Setting a low transfer speed ensures that stream discarding will time out.
538e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    MockResponse mockResponse = new MockResponse()
539e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .throttleBody(6, 1, TimeUnit.SECONDS);
540e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    transferKind.setBody(mockResponse, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 1024);
541e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(mockResponse);
542e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
543e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("Request #2"));
544e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
54571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response1 = get(server.url("/"));
546e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    BufferedSource in = response1.body().source();
547e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("ABCDE", in.readUtf8(5));
548e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    in.close();
549e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
550e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      in.readByte();
551e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail("Expected an IllegalStateException because the source is closed.");
552e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalStateException expected) {
553e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
554e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
555e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getWriteAbortCount());
556e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getWriteSuccessCount());
55771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response2 = get(server.url("/"));
558e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("Request #2", response2.body().string());
559e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getWriteAbortCount());
560e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getWriteSuccessCount());
561e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
562e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
563e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void defaultExpirationDateFullyCachedForLessThan24Hours() throws Exception {
564e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //      last modified: 105 seconds ago
565e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //             served:   5 seconds ago
566e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //   default lifetime: (105 - 5) / 10 = 10 seconds
567e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //            expires:  10 seconds from served date = 5 seconds from now
568e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
569e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-105, TimeUnit.SECONDS))
570e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-5, TimeUnit.SECONDS))
571e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
572e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
57371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
574e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = get(url);
575e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
576e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
577e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = get(url);
578e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
579e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(response2.header("Warning"));
580e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
581e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
582e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void defaultExpirationDateConditionallyCached() throws Exception {
583e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //      last modified: 115 seconds ago
584e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //             served:  15 seconds ago
585e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //   default lifetime: (115 - 15) / 10 = 10 seconds
586e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //            expires:  10 seconds from served date = 5 seconds ago
587e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String lastModifiedDate = formatDate(-115, TimeUnit.SECONDS);
588e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
589e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + lastModifiedDate)
590e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-15, TimeUnit.SECONDS)));
591e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(lastModifiedDate, conditionalRequest.getHeader("If-Modified-Since"));
592e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
593e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
594e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void defaultExpirationDateFullyCachedForMoreThan24Hours() throws Exception {
595e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //      last modified: 105 days ago
596e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //             served:   5 days ago
597e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //   default lifetime: (105 - 5) / 10 = 10 days
598e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //            expires:  10 days from served date = 5 days from now
599e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
600e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-105, TimeUnit.DAYS))
601e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-5, TimeUnit.DAYS))
602e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
603e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
60471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
60571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response = get(server.url("/"));
606e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response.body().string());
607e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("113 HttpURLConnection \"Heuristic expiration\"", response.header("Warning"));
608e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
609e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
610e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void noDefaultExpirationForUrlsWithQueryString() throws Exception {
611e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
612e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-105, TimeUnit.SECONDS))
613e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-5, TimeUnit.SECONDS))
614e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
615e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
616e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
617e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
61871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/").newBuilder().addQueryParameter("foo", "bar").build();
619e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
620e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", get(url).body().string());
621e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
622e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
623e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void expirationDateInThePastWithLastModifiedHeader() throws Exception {
624e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
625e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
626e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + lastModifiedDate)
627e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
628e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(lastModifiedDate, conditionalRequest.getHeader("If-Modified-Since"));
629e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
630e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
631e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void expirationDateInThePastWithNoLastModifiedHeader() throws Exception {
632e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNotCached(new MockResponse()
633e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
634e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
635e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
636e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void expirationDateInTheFuture() throws Exception {
637e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFullyCached(new MockResponse()
638e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
639e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
640e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
641e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void maxAgePreferredWithMaxAgeAndExpires() throws Exception {
642e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFullyCached(new MockResponse()
643e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
644e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS))
645e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60"));
646e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
647e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
648e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void maxAgeInThePastWithDateAndLastModifiedHeaders() throws Exception {
649e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
650e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
651e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-120, TimeUnit.SECONDS))
652e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + lastModifiedDate)
653e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60"));
654e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(lastModifiedDate, conditionalRequest.getHeader("If-Modified-Since"));
655e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
656e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
657e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void maxAgeInThePastWithDateHeaderButNoLastModifiedHeader() throws Exception {
658e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Chrome interprets max-age relative to the local clock. Both our cache
659e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // and Firefox both use the earlier of the local and server's clock.
660e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNotCached(new MockResponse()
661e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-120, TimeUnit.SECONDS))
662e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60"));
663e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
664e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
665e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void maxAgeInTheFutureWithDateHeader() throws Exception {
666e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFullyCached(new MockResponse()
667e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
668e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60"));
669e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
670e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
671e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void maxAgeInTheFutureWithNoDateHeader() throws Exception {
672e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFullyCached(new MockResponse()
673e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60"));
674e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
675e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
676e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void maxAgeWithLastModifiedButNoServedDate() throws Exception {
677e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFullyCached(new MockResponse()
678e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
679e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60"));
680e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
681e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
682e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void maxAgeInTheFutureWithDateAndLastModifiedHeaders() throws Exception {
683e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFullyCached(new MockResponse()
684e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
685e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
686e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60"));
687e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
688e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
689e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void maxAgePreferredOverLowerSharedMaxAge() throws Exception {
690e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFullyCached(new MockResponse()
691e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
692e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: s-maxage=60")
693e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=180"));
694e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
695e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
696e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void maxAgePreferredOverHigherMaxAge() throws Exception {
697e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNotCached(new MockResponse()
698e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
699e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: s-maxage=180")
700e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60"));
701e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
702e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
703e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMethodOptionsIsNotCached() throws Exception {
704e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRequestMethod("OPTIONS", false);
705e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
706e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
707e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMethodGetIsCached() throws Exception {
708e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRequestMethod("GET", true);
709e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
710e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
711e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMethodHeadIsNotCached() throws Exception {
712e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // We could support this but choose not to for implementation simplicity
713e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRequestMethod("HEAD", false);
714e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
715e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
716e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMethodPostIsNotCached() throws Exception {
717e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // We could support this but choose not to for implementation simplicity
718e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRequestMethod("POST", false);
719e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
720e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
721e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMethodPutIsNotCached() throws Exception {
722e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRequestMethod("PUT", false);
723e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
724e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
725e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMethodDeleteIsNotCached() throws Exception {
726e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRequestMethod("DELETE", false);
727e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
728e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
729e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMethodTraceIsNotCached() throws Exception {
730e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRequestMethod("TRACE", false);
731e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
732e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
733e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void testRequestMethod(String requestMethod, boolean expectCached) throws Exception {
734e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 1. seed the cache (potentially)
735e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 2. expect a cache hit or miss
736e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
737e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
738e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("X-Response-ID: 1"));
739e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
740e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("X-Response-ID: 2"));
741e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
74271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
743e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
744e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
745e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
746e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .method(requestMethod, requestBodyOrNull(requestMethod))
747e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
748e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request).execute();
749e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    response1.body().close();
750e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("1", response1.header("X-Response-ID"));
751e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
752e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = get(url);
753e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    response2.body().close();
754e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    if (expectCached) {
755e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("1", response2.header("X-Response-ID"));
756e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } else {
757e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("2", response2.header("X-Response-ID"));
758e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
759e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
760e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
761e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private RequestBody requestBodyOrNull(String requestMethod) {
762e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return (requestMethod.equals("POST") || requestMethod.equals("PUT"))
763e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          ? RequestBody.create(MediaType.parse("text/plain"), "foo")
764e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          : null;
765e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
766e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
767e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void postInvalidatesCache() throws Exception {
768e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testMethodInvalidates("POST");
769e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
770e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
771e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void putInvalidatesCache() throws Exception {
772e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testMethodInvalidates("PUT");
773e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
774e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
775e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void deleteMethodInvalidatesCache() throws Exception {
776e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testMethodInvalidates("DELETE");
777e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
778e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
779e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void testMethodInvalidates(String requestMethod) throws Exception {
780e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 1. seed the cache
781e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 2. invalidate it
782e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 3. expect a cache miss
783e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
784e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
785e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
786e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
787e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
788e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
789e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("C"));
790e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
79171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
792e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
793e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
794e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
795e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
796e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
797e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .method(requestMethod, requestBodyOrNull(requestMethod))
798e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
799e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response invalidate = client.newCall(request).execute();
800e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", invalidate.body().string());
801e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
802e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("C", get(url).body().string());
803e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
804e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
805e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void postInvalidatesCacheWithUncacheableResponse() throws Exception {
806e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 1. seed the cache
807e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 2. invalidate it with uncacheable response
808e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 3. expect a cache miss
809e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
810e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
811e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
812e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
813e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B")
814e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(500));
815e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
816e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("C"));
817e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
81871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
819e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
820e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
821e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
822e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
823e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
824e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .method("POST", requestBodyOrNull("POST"))
825e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
826e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response invalidate = client.newCall(request).execute();
827e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", invalidate.body().string());
828e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
829e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("C", get(url).body().string());
830e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
831e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
832e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void etag() throws Exception {
833e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
834e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("ETag: v1"));
835e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("v1", conditionalRequest.getHeader("If-None-Match"));
836e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
837e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
8383be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fuller  /** If both If-Modified-Since and If-None-Match conditions apply, send only If-None-Match. */
839e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void etagAndExpirationDateInThePast() throws Exception {
840e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
841e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
842e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("ETag: v1")
843e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + lastModifiedDate)
844e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
845e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("v1", conditionalRequest.getHeader("If-None-Match"));
8463be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fuller    assertNull(conditionalRequest.getHeader("If-Modified-Since"));
847e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
848e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
849e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void etagAndExpirationDateInTheFuture() throws Exception {
850e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFullyCached(new MockResponse()
851e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("ETag: v1")
852e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
853e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
854e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
855e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
856e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void cacheControlNoCache() throws Exception {
857e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNotCached(new MockResponse()
858e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: no-cache"));
859e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
860e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
861e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void cacheControlNoCacheAndExpirationDateInTheFuture() throws Exception {
862e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
863e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
864e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + lastModifiedDate)
865e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
866e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: no-cache"));
867e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(lastModifiedDate, conditionalRequest.getHeader("If-Modified-Since"));
868e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
869e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
870e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void pragmaNoCache() throws Exception {
871e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNotCached(new MockResponse()
872e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Pragma: no-cache"));
873e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
874e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
875e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void pragmaNoCacheAndExpirationDateInTheFuture() throws Exception {
876e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
877e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
878e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + lastModifiedDate)
879e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
880e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Pragma: no-cache"));
881e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(lastModifiedDate, conditionalRequest.getHeader("If-Modified-Since"));
882e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
883e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
884e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void cacheControlNoStore() throws Exception {
885e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNotCached(new MockResponse()
886e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: no-store"));
887e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
888e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
889e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void cacheControlNoStoreAndExpirationDateInTheFuture() throws Exception {
890e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNotCached(new MockResponse()
891e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
892e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
893e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: no-store"));
894e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
895e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
896e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void partialRangeResponsesDoNotCorruptCache() throws Exception {
897e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 1. request a range
898e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 2. request a full document, expecting a cache miss
899e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
900e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("AA")
901e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_PARTIAL)
902e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
903e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Content-Range: bytes 1000-1001/2000"));
904e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
905e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("BB"));
906e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
90771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
908e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
909e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
910e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
911e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Range", "bytes=1000-1001")
912e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
913e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response range = client.newCall(request).execute();
914e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("AA", range.body().string());
915e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
916e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("BB", get(url).body().string());
917e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
918e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
919e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void serverReturnsDocumentOlderThanCache() throws Exception {
920e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
921e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
922e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
923e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
924e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
925e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B")
926e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-4, TimeUnit.HOURS)));
927e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
92871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
929e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
930e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
931e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
932e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
933e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
934e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void clientSideNoStore() throws Exception {
935e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
936e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
937e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
938e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
939e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
940e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
941e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
942e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request1 = new Request.Builder()
94371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
944e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .cacheControl(new CacheControl.Builder().noStore().build())
945e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
946e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request1).execute();
947e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
948e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
949e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request2 = new Request.Builder()
95071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
951e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
952e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = client.newCall(request2).execute();
953e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response2.body().string());
954e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
955e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
956e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void nonIdentityEncodingAndConditionalCache() throws Exception {
957e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNonIdentityEncodingCached(new MockResponse()
958e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
959e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
960e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
961e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
962e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void nonIdentityEncodingAndFullCache() throws Exception {
963e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNonIdentityEncodingCached(new MockResponse()
964e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
965e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
966e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
967e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
968e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void assertNonIdentityEncodingCached(MockResponse response) throws Exception {
969e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(response
970e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody(gzip("ABCABCABC"))
971e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Content-Encoding: gzip"));
972e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
973e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
974e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
975e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
976e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
977e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // At least three request/response pairs are required because after the first request is cached
978e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // a different execution path might be taken. Thus modifications to the cache applied during
979e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // the second request might not be visible until another request is performed.
98071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("ABCABCABC", get(server.url("/")).body().string());
98171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("ABCABCABC", get(server.url("/")).body().string());
98271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("ABCABCABC", get(server.url("/")).body().string());
983e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
984e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
985e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void notModifiedSpecifiesEncoding() throws Exception {
986e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
987e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody(gzip("ABCABCABC"))
988e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Content-Encoding: gzip")
989e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
990e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
991e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
992e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED)
993e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Content-Encoding: gzip"));
994e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
995e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("DEFDEFDEF"));
996e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
99771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("ABCABCABC", get(server.url("/")).body().string());
99871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("ABCABCABC", get(server.url("/")).body().string());
99971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("DEFDEFDEF", get(server.url("/")).body().string());
1000e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1001e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1002e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** https://github.com/square/okhttp/issues/947 */
1003e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void gzipAndVaryOnAcceptEncoding() throws Exception {
1004e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1005e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody(gzip("ABCABCABC"))
1006e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Content-Encoding: gzip")
1007e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Encoding")
1008e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60"));
1009e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1010e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("FAIL"));
1011e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
101271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("ABCABCABC", get(server.url("/")).body().string());
101371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("ABCABCABC", get(server.url("/")).body().string());
1014e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1015e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1016e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void conditionalCacheHitIsNotDoublePooled() throws Exception {
1017e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1018e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("ETag: v1")
1019e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1020e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1021e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .clearHeaders()
1022e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1023e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1024e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    ConnectionPool pool = ConnectionPool.getDefault();
1025e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    pool.evictAll();
1026e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.setConnectionPool(pool);
1027e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
102871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
102971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
10306c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    assertEquals(1, client.getConnectionPool().getIdleConnectionCount());
1031e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1032e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1033e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void expiresDateBeforeModifiedDate() throws Exception {
1034e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertConditionallyCached(new MockResponse()
1035e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1036e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(-2, TimeUnit.HOURS)));
1037e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1038e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1039e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMaxAge() throws IOException {
1040e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1041e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
1042e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
1043e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-1, TimeUnit.MINUTES))
1044e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
1045e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1046e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1047e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
104871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1049e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1050e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
105171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1052e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "max-age=30")
1053e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1054e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1055e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response.body().string());
1056e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1057e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1058e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMinFresh() throws IOException {
1059e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1060e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
1061e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1062e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
1063e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1064e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1065e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
106671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1067e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1068e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
106971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1070e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "min-fresh=120")
1071e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1072e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1073e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response.body().string());
1074e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1075e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1076e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMaxStale() throws IOException {
1077e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1078e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
1079e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=120")
1080e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-4, TimeUnit.MINUTES)));
1081e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1082e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1083e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
108471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1085e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1086e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
108771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1088e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "max-stale=180")
1089e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1090e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1091e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response.body().string());
1092e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("110 HttpURLConnection \"Response is stale\"", response.header("Warning"));
1093e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1094e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1095e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMaxStaleDirectiveWithNoValue() throws IOException {
1096e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Add a stale response to the cache.
1097e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1098e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
1099e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=120")
1100e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-4, TimeUnit.MINUTES)));
1101e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1102e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1103e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
110471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1105e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1106e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // With max-stale, we'll return that stale response.
1107e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
110871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1109e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "max-stale")
1110e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1111e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1112e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response.body().string());
1113e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("110 HttpURLConnection \"Response is stale\"", response.header("Warning"));
1114e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1115e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1116e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMaxStaleNotHonoredWithMustRevalidate() throws IOException {
1117e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1118e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
1119e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=120, must-revalidate")
1120e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-4, TimeUnit.MINUTES)));
1121e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1122e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1123e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
112471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1125e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1126e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
112771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1128e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "max-stale=180")
1129e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1130e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1131e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response.body().string());
1132e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1133e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1134e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestOnlyIfCachedWithNoResponseCached() throws IOException {
1135e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // (no responses enqueued)
1136e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1137e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
113871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1139e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "only-if-cached")
1140e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1141e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1142e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(response.body().source().exhausted());
1143e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(504, response.code());
1144e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getRequestCount());
1145e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getNetworkCount());
1146e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getHitCount());
1147e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1148e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1149e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestOnlyIfCachedWithFullResponseCached() throws IOException {
1150e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1151e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
1152e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=30")
1153e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
1154e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
115571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1156e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
115771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1158e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "only-if-cached")
1159e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1160e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1161e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response.body().string());
1162e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, cache.getRequestCount());
1163e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getNetworkCount());
1164e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getHitCount());
1165e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1166e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1167e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestOnlyIfCachedWithConditionalResponseCached() throws IOException {
1168e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1169e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
1170e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=30")
1171e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-1, TimeUnit.MINUTES)));
1172e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
117371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1174e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
117571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1176e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "only-if-cached")
1177e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1178e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1179e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(response.body().source().exhausted());
1180e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(504, response.code());
1181e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, cache.getRequestCount());
1182e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getNetworkCount());
1183e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getHitCount());
1184e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1185e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1186e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestOnlyIfCachedWithUnhelpfulResponseCached() throws IOException {
1187e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1188e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1189e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
119071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1191e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
119271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1193e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "only-if-cached")
1194e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1195e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1196e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(response.body().source().exhausted());
1197e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(504, response.code());
1198e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, cache.getRequestCount());
1199e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getNetworkCount());
1200e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getHitCount());
1201e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1202e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1203e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestCacheControlNoCache() throws Exception {
1204e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1205e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
1206e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
1207e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1208e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1209e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1210e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1211e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
121271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1213e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1214e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
1215e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1216e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "no-cache")
1217e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1218e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1219e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response.body().string());
1220e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1221e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1222e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestPragmaNoCache() throws Exception {
1223e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1224e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
1225e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
1226e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1227e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1228e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1229e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1230e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
123171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1232e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1233e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
1234e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1235e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Pragma", "no-cache")
1236e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1237e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1238e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response.body().string());
1239e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1240e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1241e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void clientSuppliedIfModifiedSinceWithCachedResult() throws Exception {
1242e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    MockResponse response = new MockResponse()
1243e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("ETag: v3")
1244e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0");
1245e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String ifModifiedSinceDate = formatDate(-24, TimeUnit.HOURS);
1246e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest request =
1247e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertClientSuppliedCondition(response, "If-Modified-Since", ifModifiedSinceDate);
1248e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(ifModifiedSinceDate, request.getHeader("If-Modified-Since"));
1249e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(request.getHeader("If-None-Match"));
1250e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1251e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1252e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void clientSuppliedIfNoneMatchSinceWithCachedResult() throws Exception {
1253e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String lastModifiedDate = formatDate(-3, TimeUnit.MINUTES);
1254e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    MockResponse response = new MockResponse()
1255e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + lastModifiedDate)
1256e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
1257e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0");
1258e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest request = assertClientSuppliedCondition(response, "If-None-Match", "v1");
1259e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("v1", request.getHeader("If-None-Match"));
1260e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(request.getHeader("If-Modified-Since"));
1261e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1262e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1263e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private RecordedRequest assertClientSuppliedCondition(MockResponse seed, String conditionName,
1264e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      String conditionValue) throws Exception {
1265e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(seed.setBody("A"));
1266e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1267e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1268e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
126971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1270e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1271e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1272e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
1273e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1274e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header(conditionName, conditionValue)
1275e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1276e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1277e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, response.code());
1278e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("", response.body().string());
1279e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1280e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.takeRequest(); // seed
1281e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return server.takeRequest();
1282e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1283e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1284e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
1285e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * For Last-Modified and Date headers, we should echo the date back in the
1286e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * exact format we were served.
1287e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
1288e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void retainServedDateFormat() throws Exception {
1289e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Serve a response with a non-standard date format that OkHttp supports.
1290e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Date lastModifiedDate = new Date(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(-1));
1291e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Date servedDate = new Date(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(-2));
1292e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DateFormat dateFormat = new SimpleDateFormat("EEE dd-MMM-yyyy HH:mm:ss z", Locale.US);
1293e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    dateFormat.setTimeZone(TimeZone.getTimeZone("EDT"));
1294e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String lastModifiedString = dateFormat.format(lastModifiedDate);
1295e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String servedString = dateFormat.format(servedDate);
1296e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1297e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // This response should be conditionally cached.
1298e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1299e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + lastModifiedString)
1300e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + servedString)
1301e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1302e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1303e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1304e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
130571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
130671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1307e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1308e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // The first request has no conditions.
1309e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest request1 = server.takeRequest();
1310e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(request1.getHeader("If-Modified-Since"));
1311e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1312e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // The 2nd request uses the server's date format.
1313e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest request2 = server.takeRequest();
1314e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(lastModifiedString, request2.getHeader("If-Modified-Since"));
1315e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1316e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1317e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void clientSuppliedConditionWithoutCachedResult() throws Exception {
1318e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1319e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1320e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1321e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
132271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1323e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("If-Modified-Since", formatDate(-24, TimeUnit.HOURS))
1324e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1325e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1326e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, response.code());
1327e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("", response.body().string());
1328e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1329e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1330e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void authorizationRequestFullyCached() throws Exception {
1331e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1332e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1333e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1334e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1335e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1336e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
133771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1338e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
1339e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1340e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Authorization", "password")
1341e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1342e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1343e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response.body().string());
1344e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1345e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1346e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1347e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void contentLocationDoesNotPopulateCache() throws Exception {
1348e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1349e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1350e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Content-Location: /bar")
1351e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1352e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1353e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1354e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
135571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/foo")).body().string());
135671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("B", get(server.url("/bar")).body().string());
1357e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1358e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1359e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void connectionIsReturnedToPoolAfterConditionalSuccess() throws Exception {
1360e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1361e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1362e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1363e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1364e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1365e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1366e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1367e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1368e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
136971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/a")).body().string());
137071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/a")).body().string());
137171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("B", get(server.url("/b")).body().string());
1372e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1373e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, server.takeRequest().getSequenceNumber());
1374e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, server.takeRequest().getSequenceNumber());
1375e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, server.takeRequest().getSequenceNumber());
1376e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1377e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1378e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void statisticsConditionalCacheMiss() throws Exception {
1379e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1380e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1381e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1382e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1383e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1384e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1385e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1386e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("C"));
1387e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
138871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1389e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getRequestCount());
1390e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getNetworkCount());
1391e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getHitCount());
139271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("B", get(server.url("/")).body().string());
139371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("C", get(server.url("/")).body().string());
1394e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(3, cache.getRequestCount());
1395e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(3, cache.getNetworkCount());
1396e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getHitCount());
1397e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1398e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1399e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void statisticsConditionalCacheHit() throws Exception {
1400e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1401e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1402e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1403e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1404e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1405e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1406e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1407e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1408e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
140971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1410e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getRequestCount());
1411e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getNetworkCount());
1412e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getHitCount());
141371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
141471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1415e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(3, cache.getRequestCount());
1416e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(3, cache.getNetworkCount());
1417e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, cache.getHitCount());
1418e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1419e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1420e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void statisticsFullCacheHit() throws Exception {
1421e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1422e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1423e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1424e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
142571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1426e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getRequestCount());
1427e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getNetworkCount());
1428e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getHitCount());
142971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
143071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1431e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(3, cache.getRequestCount());
1432e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getNetworkCount());
1433e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, cache.getHitCount());
1434e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1435e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1436e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyMatchesChangedRequestHeaderField() throws Exception {
1437e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1438e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1439e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Language")
1440e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1441e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1442e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1443e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
144471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1445e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request frRequest = new Request.Builder()
1446e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1447e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "fr-CA")
1448e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1449e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response frResponse = client.newCall(frRequest).execute();
1450e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", frResponse.body().string());
1451e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1452e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request enRequest = new Request.Builder()
1453e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1454e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "en-US")
1455e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1456e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response enResponse = client.newCall(enRequest).execute();
1457e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", enResponse.body().string());
1458e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1459e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1460e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyMatchesUnchangedRequestHeaderField() throws Exception {
1461e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1462e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1463e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Language")
1464e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1465e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1466e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1467e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
146871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1469e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
1470e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1471e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "fr-CA")
1472e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1473e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request).execute();
1474e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1475e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request1 = new Request.Builder()
1476e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1477e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "fr-CA")
1478e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1479e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = client.newCall(request1).execute();
1480e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1481e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1482e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1483e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyMatchesAbsentRequestHeaderField() throws Exception {
1484e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1485e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1486e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Foo")
1487e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1488e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1489e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1490e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
149171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
149271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1493e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1494e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1495e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyMatchesAddedRequestHeaderField() throws Exception {
1496e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1497e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1498e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Foo")
1499e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1500e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1501e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1502e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
150371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1504e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
150571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/")).header("Foo", "bar")
1506e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1507e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1508e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response.body().string());
1509e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1510e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1511e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyMatchesRemovedRequestHeaderField() throws Exception {
1512e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1513e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1514e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Foo")
1515e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1516e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1517e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1518e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1519e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
152071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/")).header("Foo", "bar")
1521e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1522e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response fooresponse = client.newCall(request).execute();
1523e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", fooresponse.body().string());
152471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("B", get(server.url("/")).body().string());
1525e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1526e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1527e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyFieldsAreCaseInsensitive() throws Exception {
1528e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1529e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1530e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: ACCEPT-LANGUAGE")
1531e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1532e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1533e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1534e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
153571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1536e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
1537e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1538e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "fr-CA")
1539e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1540e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request).execute();
1541e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1542e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request1 = new Request.Builder()
1543e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1544e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("accept-language", "fr-CA")
1545e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1546e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = client.newCall(request1).execute();
1547e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1548e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1549e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1550e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyMultipleFieldsWithMatch() throws Exception {
1551e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1552e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1553e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Language, Accept-Charset")
1554e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Encoding")
1555e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1556e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1557e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1558e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
155971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1560e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
1561e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1562e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "fr-CA")
1563e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Charset", "UTF-8")
1564e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Encoding", "identity")
1565e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1566e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request).execute();
1567e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1568e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request1 = new Request.Builder()
1569e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1570e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "fr-CA")
1571e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Charset", "UTF-8")
1572e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Encoding", "identity")
1573e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1574e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = client.newCall(request1).execute();
1575e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1576e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1577e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1578e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyMultipleFieldsWithNoMatch() throws Exception {
1579e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1580e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1581e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Language, Accept-Charset")
1582e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Encoding")
1583e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1584e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1585e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1586e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
158771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1588e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request frRequest = new Request.Builder()
1589e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1590e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "fr-CA")
1591e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Charset", "UTF-8")
1592e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Encoding", "identity")
1593e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1594e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response frResponse = client.newCall(frRequest).execute();
1595e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", frResponse.body().string());
1596e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request enRequest = new Request.Builder()
1597e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1598e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "en-CA")
1599e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Charset", "UTF-8")
1600e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Encoding", "identity")
1601e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1602e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response enResponse = client.newCall(enRequest).execute();
1603e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", enResponse.body().string());
1604e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1605e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1606e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyMultipleFieldValuesWithMatch() throws Exception {
1607e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1608e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1609e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Language")
1610e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1611e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1612e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1613e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
161471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1615e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request1 = new Request.Builder()
1616e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1617e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Accept-Language", "fr-CA, fr-FR")
1618e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Accept-Language", "en-US")
1619e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1620e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request1).execute();
1621e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1622e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1623e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request2 = new Request.Builder()
1624e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1625e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Accept-Language", "fr-CA, fr-FR")
1626e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Accept-Language", "en-US")
1627e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1628e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = client.newCall(request2).execute();
1629e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1630e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1631e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1632e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyMultipleFieldValuesWithNoMatch() throws Exception {
1633e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1634e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1635e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Language")
1636e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1637e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1638e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1639e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
164071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1641e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request1 = new Request.Builder()
1642e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1643e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Accept-Language", "fr-CA, fr-FR")
1644e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Accept-Language", "en-US")
1645e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1646e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request1).execute();
1647e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1648e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1649e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request2 = new Request.Builder()
1650e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1651e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Accept-Language", "fr-CA")
1652e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Accept-Language", "en-US")
1653e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1654e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = client.newCall(request2).execute();
1655e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response2.body().string());
1656e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1657e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1658e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyAsterisk() throws Exception {
165971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.enqueue(new MockResponse()
1660e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1661e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: *")
1662e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1663e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1664e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1665e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
166671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
166771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("B", get(server.url("/")).body().string());
1668e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1669e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1670e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyAndHttps() throws Exception {
1671e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.useHttps(sslContext.getSocketFactory(), false);
1672e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1673e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1674e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Language")
1675e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1676e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1677e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1678e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1679e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.setSslSocketFactory(sslContext.getSocketFactory());
1680e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
1681e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
168271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1683e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request1 = new Request.Builder()
1684e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1685e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "en-US")
1686e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1687e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request1).execute();
1688e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1689e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1690e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request2 = new Request.Builder()
1691e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1692e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "en-US")
1693e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1694e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = client.newCall(request2).execute();
1695e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1696e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1697e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1698e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void cachePlusCookies() throws Exception {
1699e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1700e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Set-Cookie: a=FIRST; domain=" + server.getCookieDomain() + ";")
1701e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1702e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1703e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1704e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1705e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Set-Cookie: a=SECOND; domain=" + server.getCookieDomain() + ";")
1706e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1707e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
170871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1709e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1710e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCookies(url, "a=FIRST");
1711e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1712e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCookies(url, "a=SECOND");
1713e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1714e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1715e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void getHeadersReturnsNetworkEndToEndHeaders() throws Exception {
1716e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1717e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Allow: GET, HEAD")
1718e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1719e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1720e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1721e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1722e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Allow: GET, HEAD, PUT")
1723e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1724e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
172571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response1 = get(server.url("/"));
1726e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1727e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("GET, HEAD", response1.header("Allow"));
1728e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
172971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response2 = get(server.url("/"));
1730e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1731e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("GET, HEAD, PUT", response2.header("Allow"));
1732e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1733e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1734e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void getHeadersReturnsCachedHopByHopHeaders() throws Exception {
1735e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1736e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Transfer-Encoding: identity")
1737e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1738e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1739e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1740e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1741e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Transfer-Encoding: none")
1742e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1743e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
174471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response1 = get(server.url("/"));
1745e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1746e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("identity", response1.header("Transfer-Encoding"));
1747e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
174871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response2 = get(server.url("/"));
1749e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1750e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("identity", response2.header("Transfer-Encoding"));
1751e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1752e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1753e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void getHeadersDeletesCached100LevelWarnings() throws Exception {
1754e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1755e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Warning: 199 test danger")
1756e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1757e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1758e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1759e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1760e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1761e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
176271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response1 = get(server.url("/"));
1763e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1764e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("199 test danger", response1.header("Warning"));
1765e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
176671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response2 = get(server.url("/"));
1767e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1768e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(null, response2.header("Warning"));
1769e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1770e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1771e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void getHeadersRetainsCached200LevelWarnings() throws Exception {
1772e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1773e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Warning: 299 test danger")
1774e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1775e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1776e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1777e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1778e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1779e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
178071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response1 = get(server.url("/"));
1781e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1782e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("299 test danger", response1.header("Warning"));
1783e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
178471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response2 = get(server.url("/"));
1785e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1786e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("299 test danger", response2.header("Warning"));
1787e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1788e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
178971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  public void assertCookies(HttpUrl url, String... expectedCookies) throws Exception {
1790e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    List<String> actualCookies = new ArrayList<>();
179171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    for (HttpCookie cookie : cookieManager.getCookieStore().get(url.uri())) {
1792e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      actualCookies.add(cookie.toString());
1793e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
1794e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(Arrays.asList(expectedCookies), actualCookies);
1795e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1796e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1797e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void doNotCachePartialResponse() throws Exception  {
1798e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNotCached(new MockResponse()
179971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_PARTIAL)
180071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
180171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .addHeader("Content-Range: bytes 100-100/200")
180271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .addHeader("Cache-Control: max-age=60"));
1803e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1804e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1805e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void conditionalHitUpdatesCache() throws Exception {
1806e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1807e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(0, TimeUnit.SECONDS))
1808e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1809e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1810e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1811e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=30")
1812e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Allow: GET, HEAD")
1813e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1814e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1815e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1816e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1817e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // cache miss; seed the cache
181871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response1 = get(server.url("/a"));
1819e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1820e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(null, response1.header("Allow"));
1821e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1822e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // conditional cache hit; update the cache
182371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response2 = get(server.url("/a"));
1824e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(HttpURLConnection.HTTP_OK, response2.code());
1825e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1826e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("GET, HEAD", response2.header("Allow"));
1827e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1828e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // full cache hit
182971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response3 = get(server.url("/a"));
1830e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response3.body().string());
1831e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("GET, HEAD", response3.header("Allow"));
1832e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1833e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, server.getRequestCount());
1834e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1835e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1836e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void responseSourceHeaderCached() throws IOException {
1837e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1838e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
1839e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=30")
1840e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
1841e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
184271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1843e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
184471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/")).header("Cache-Control", "only-if-cached")
1845e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1846e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1847e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response.body().string());
1848e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1849e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1850e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void responseSourceHeaderConditionalCacheFetched() throws IOException {
1851e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1852e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
1853e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=30")
1854e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-31, TimeUnit.MINUTES)));
1855e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1856e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B")
1857e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=30")
1858e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
1859e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
186071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
186171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response = get(server.url("/"));
1862e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response.body().string());
1863e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1864e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1865e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void responseSourceHeaderConditionalCacheNotFetched() throws IOException {
1866e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1867e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
1868e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1869e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
1870e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1871e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(304));
1872e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
187371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
187471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response = get(server.url("/"));
1875e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response.body().string());
1876e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1877e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1878e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void responseSourceHeaderFetched() throws IOException {
1879e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1880e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1881e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
188271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response = get(server.url("/"));
1883e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response.body().string());
1884e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1885e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1886e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void emptyResponseHeaderNameFromCacheIsLenient() throws Exception {
1887e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Headers.Builder headers = new Headers.Builder()
1888e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .add("Cache-Control: max-age=120");
1889e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Internal.instance.addLenient(headers, ": A");
1890e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1891e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setHeaders(headers.build())
1892e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("body"));
1893e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
189471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response = get(server.url("/"));
1895e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response.header(""));
18966c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    assertEquals("body", response.body().string());
1897e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1898e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1899e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
1900e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Old implementations of OkHttp's response cache wrote header fields like
1901e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * ":status: 200 OK". This broke our cached response parser because it split
1902e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * on the first colon. This regression test exists to help us read these old
1903e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * bad cache entries.
1904e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   *
1905e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * https://github.com/square/okhttp/issues/227
1906e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
1907e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void testGoldenCacheResponse() throws Exception {
1908e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
1909e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1910e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .clearHeaders()
1911e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1912e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
191371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1914e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String urlKey = Util.md5Hex(url.toString());
1915e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String entryMetadata = ""
1916e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "" + url + "\n"
1917e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "GET\n"
1918e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "0\n"
1919e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "HTTP/1.1 200 OK\n"
1920e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "7\n"
1921e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + ":status: 200 OK\n"
1922e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + ":version: HTTP/1.1\n"
1923e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "etag: foo\n"
1924e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "content-length: 3\n"
1925e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "OkHttp-Received-Millis: " + System.currentTimeMillis() + "\n"
1926e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "X-Android-Response-Source: NETWORK 200\n"
1927e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "OkHttp-Sent-Millis: " + System.currentTimeMillis() + "\n"
1928e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "\n"
1929e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA\n"
1930e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "1\n"
1931e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "MIIBpDCCAQ2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDEw1qd2lsc29uLmxvY2FsMB4XDTEzMDgy"
1932e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "OTA1MDE1OVoXDTEzMDgzMDA1MDE1OVowGDEWMBQGA1UEAxMNandpbHNvbi5sb2NhbDCBnzANBgkqhkiG9w0BAQEF"
1933e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "AAOBjQAwgYkCgYEAlFW+rGo/YikCcRghOyKkJanmVmJSce/p2/jH1QvNIFKizZdh8AKNwojt3ywRWaDULA/RlCUc"
1934e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "ltF3HGNsCyjQI/+Lf40x7JpxXF8oim1E6EtDoYtGWAseelawus3IQ13nmo6nWzfyCA55KhAWf4VipelEy8DjcuFK"
1935e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "v6L0xwXnI0ECAwEAATANBgkqhkiG9w0BAQsFAAOBgQAuluNyPo1HksU3+Mr/PyRQIQS4BI7pRXN8mcejXmqyscdP"
1936e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "7S6J21FBFeRR8/XNjVOp4HT9uSc2hrRtTEHEZCmpyoxixbnM706ikTmC7SN/GgM+SmcoJ1ipJcNcl8N0X6zym4dm"
1937e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "yFfXKHu2PkTo7QFdpOJFvP3lIigcSZXozfmEDg==\n"
1938e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "-1\n";
1939e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String entryBody = "abc";
1940e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String journalBody = ""
1941e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "libcore.io.DiskLruCache\n"
1942e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "1\n"
1943e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "201105\n"
1944e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "2\n"
1945e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "\n"
1946e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "CLEAN " + urlKey + " " + entryMetadata.length() + " " + entryBody.length() + "\n";
1947e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeFile(cache.getDirectory(), urlKey + ".0", entryMetadata);
1948e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeFile(cache.getDirectory(), urlKey + ".1", entryBody);
1949e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeFile(cache.getDirectory(), "journal", journalBody);
195071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    cache = new Cache(cache.getDirectory(), Integer.MAX_VALUE, fileSystem);
1951e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.setCache(cache);
1952e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1953e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = get(url);
1954e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(entryBody, response.body().string());
1955e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("3", response.header("Content-Length"));
1956e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("foo", response.header("etag"));
1957e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1958e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1959e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void evictAll() throws Exception {
1960e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1961e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1962e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1963e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1964e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1965e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
196671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1967e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1968e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.getCache().evictAll();
1969e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, client.getCache().getSize());
1970e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", get(url).body().string());
1971e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1972e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1973e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void networkInterceptorInvokedForConditionalGet() throws Exception {
1974e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1975e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("ETag: v1")
1976e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1977e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1978e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1979e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1980e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Seed the cache.
198171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1982e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1983e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1984e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    final AtomicReference<String> ifNoneMatch = new AtomicReference<>();
1985e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.networkInterceptors().add(new Interceptor() {
1986e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public Response intercept(Chain chain) throws IOException {
1987e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        ifNoneMatch.compareAndSet(null, chain.request().header("If-None-Match"));
1988e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        return chain.proceed(chain.request());
1989e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
1990e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    });
1991e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1992e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Confirm the value is cached and intercepted.
1993e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1994e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("v1", ifNoneMatch.get());
1995e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1996e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1997e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void networkInterceptorNotInvokedForFullyCached() throws Exception {
1998e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1999e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
2000e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
2001e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2002e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Seed the cache.
200371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
2004e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
2005e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2006e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Confirm the interceptor isn't exercised.
2007e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.networkInterceptors().add(new Interceptor() {
2008e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public Response intercept(Chain chain) throws IOException {
2009e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        throw new AssertionError();
2010e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
2011e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    });
2012e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
2013e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2014e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2015e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void iterateCache() throws Exception {
2016e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Put some responses in the cache.
2017e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2018e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("a"));
201971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl urlA = server.url("/a");
2020e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("a", get(urlA).body().string());
2021e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2022e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2023e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("b"));
202471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl urlB = server.url("/b");
2025e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("b", get(urlB).body().string());
2026e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2027e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2028e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("c"));
202971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl urlC = server.url("/c");
2030e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("c", get(urlC).body().string());
2031e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2032e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Confirm the iterator returns those responses...
2033e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<String> i = cache.urls();
2034e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(i.hasNext());
2035e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(urlA.toString(), i.next());
2036e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(i.hasNext());
2037e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(urlB.toString(), i.next());
2038e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(i.hasNext());
2039e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(urlC.toString(), i.next());
2040e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2041e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // ... and nothing else.
2042e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFalse(i.hasNext());
2043e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
2044e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      i.next();
2045e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
2046e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (NoSuchElementException expected) {
2047e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
2048e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2049e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2050e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void iteratorRemoveFromCache() throws Exception {
2051e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Put a response in the cache.
2052e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2053e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
2054e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("a"));
205571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/a");
2056e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("a", get(url).body().string());
2057e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2058e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Remove it with iteration.
2059e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<String> i = cache.urls();
2060e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(url.toString(), i.next());
2061e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    i.remove();
2062e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2063e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Confirm that subsequent requests suffer a cache miss.
2064e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2065e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("b"));
2066e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("b", get(url).body().string());
2067e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2068e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2069e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void iteratorRemoveWithoutNextThrows() throws Exception {
2070e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Put a response in the cache.
2071e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2072e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("a"));
207371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/a");
2074e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("a", get(url).body().string());
2075e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2076e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<String> i = cache.urls();
2077e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(i.hasNext());
2078e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
2079e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      i.remove();
2080e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
2081e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalStateException expected) {
2082e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
2083e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2084e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2085e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void iteratorRemoveOncePerCallToNext() throws Exception {
2086e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Put a response in the cache.
2087e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2088e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("a"));
208971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/a");
2090e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("a", get(url).body().string());
2091e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2092e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<String> i = cache.urls();
2093e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(url.toString(), i.next());
2094e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    i.remove();
2095e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2096e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Too many calls to remove().
2097e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
2098e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      i.remove();
2099e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
2100e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalStateException expected) {
2101e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
2102e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2103e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2104e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void elementEvictedBetweenHasNextAndNext() throws Exception {
2105e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Put a response in the cache.
2106e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2107e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("a"));
210871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/a");
2109e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("a", get(url).body().string());
2110e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2111e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // The URL will remain available if hasNext() returned true...
2112e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<String> i = cache.urls();
2113e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(i.hasNext());
2114e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2115e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // ...so even when we evict the element, we still get something back.
2116e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.evictAll();
2117e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(url.toString(), i.next());
2118e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2119e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Remove does nothing. But most importantly, it doesn't throw!
2120e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    i.remove();
2121e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2122e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2123e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void elementEvictedBeforeHasNextIsOmitted() throws Exception {
2124e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Put a response in the cache.
2125e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2126e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("a"));
212771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/a");
2128e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("a", get(url).body().string());
2129e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2130e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<String> i = cache.urls();
2131e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.evictAll();
2132e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2133e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // The URL was evicted before hasNext() made any promises.
2134e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFalse(i.hasNext());
2135e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
2136e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      i.next();
2137e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
2138e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (NoSuchElementException expected) {
2139e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
2140e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2141e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
214271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  /** Test https://github.com/square/okhttp/issues/1712. */
214371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Test public void conditionalMissUpdatesCache() throws Exception {
214471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.enqueue(new MockResponse()
214571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .addHeader("ETag: v1")
214671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .setBody("A"));
214771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.enqueue(new MockResponse()
214871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
214971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.enqueue(new MockResponse()
215071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .addHeader("ETag: v2")
215171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .setBody("B"));
215271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.enqueue(new MockResponse()
215371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
215471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
215571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
215671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(url).body().string());
215771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(url).body().string());
215871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("B", get(url).body().string());
215971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("B", get(url).body().string());
216071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
216171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals(null, server.takeRequest().getHeader("If-None-Match"));
216271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("v1", server.takeRequest().getHeader("If-None-Match"));
216371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("v1", server.takeRequest().getHeader("If-None-Match"));
216471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("v2", server.takeRequest().getHeader("If-None-Match"));
216571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  }
216671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
216771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  private Response get(HttpUrl url) throws IOException {
2168e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
2169e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
2170e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
2171e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return client.newCall(request).execute();
2172e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2173e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2174e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2175e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void writeFile(File directory, String file, String content) throws IOException {
217671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    BufferedSink sink = Okio.buffer(fileSystem.sink(new File(directory, file)));
2177e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    sink.writeUtf8(content);
2178e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    sink.close();
2179e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2180e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2181e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
2182e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * @param delta the offset from the current date to use. Negative
2183e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * values yield dates in the past; positive values yield dates in the
2184e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * future.
2185e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
2186e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private String formatDate(long delta, TimeUnit timeUnit) {
2187e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return formatDate(new Date(System.currentTimeMillis() + timeUnit.toMillis(delta)));
2188e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2189e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2190e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private String formatDate(Date date) {
2191e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DateFormat rfc1123 = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
2192e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    rfc1123.setTimeZone(TimeZone.getTimeZone("GMT"));
2193e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return rfc1123.format(date);
2194e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2195e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2196e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void assertNotCached(MockResponse response) throws Exception {
2197e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(response.setBody("A"));
2198e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2199e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
2200e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
220171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
2202e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
2203e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", get(url).body().string());
2204e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2205e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2206e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** @return the request with the conditional get headers. */
2207e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private RecordedRequest assertConditionallyCached(MockResponse response) throws Exception {
2208e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // scenario 1: condition succeeds
2209e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(response.setBody("A").setStatus("HTTP/1.1 200 A-OK"));
2210e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2211e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
2212e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2213e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // scenario 2: condition fails
2214e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(response.setBody("B")
2215e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setStatus("HTTP/1.1 200 B-OK"));
2216e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2217e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setStatus("HTTP/1.1 200 C-OK")
2218e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("C"));
2219e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
222071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl valid = server.url("/valid");
2221e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = get(valid);
2222e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
2223e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(HttpURLConnection.HTTP_OK, response1.code());
2224e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A-OK", response1.message());
2225e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = get(valid);
2226e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
2227e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(HttpURLConnection.HTTP_OK, response2.code());
2228e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A-OK", response2.message());
2229e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
223071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl invalid = server.url("/invalid");
2231e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response3 = get(invalid);
2232e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response3.body().string());
2233e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(HttpURLConnection.HTTP_OK, response3.code());
2234e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B-OK", response3.message());
2235e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response4 = get(invalid);
2236e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("C", response4.body().string());
2237e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(HttpURLConnection.HTTP_OK, response4.code());
2238e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("C-OK", response4.message());
2239e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2240e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.takeRequest(); // regular get
2241e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return server.takeRequest(); // conditional get
2242e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2243e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2244e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void assertFullyCached(MockResponse response) throws Exception {
2245e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(response.setBody("A"));
2246e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(response.setBody("B"));
2247e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
224871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
2249e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
2250e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
2251e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2252e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2253e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
2254e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Shortens the body of {@code response} but not the corresponding headers.
2255e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Only useful to test how clients respond to the premature conclusion of
2256e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * the HTTP body.
2257e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
2258e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private MockResponse truncateViolently(MockResponse response, int numBytesToKeep) {
2259e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    response.setSocketPolicy(DISCONNECT_AT_END);
2260e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Headers headers = response.getHeaders();
2261e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer truncatedBody = new Buffer();
2262e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    truncatedBody.write(response.getBody(), numBytesToKeep);
2263e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    response.setBody(truncatedBody);
2264e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    response.setHeaders(headers);
2265e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return response;
2266e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2267e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2268e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  enum TransferKind {
2269e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    CHUNKED() {
2270e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override void setBody(MockResponse response, Buffer content, int chunkSize)
2271e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          throws IOException {
2272e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        response.setChunkedBody(content, chunkSize);
2273e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
2274e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    },
2275e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    FIXED_LENGTH() {
2276e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override void setBody(MockResponse response, Buffer content, int chunkSize) {
2277e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        response.setBody(content);
2278e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
2279e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    },
2280e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    END_OF_STREAM() {
2281e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override void setBody(MockResponse response, Buffer content, int chunkSize) {
2282e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        response.setBody(content);
2283e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        response.setSocketPolicy(DISCONNECT_AT_END);
2284e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        response.removeHeader("Content-Length");
2285e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
2286e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    };
2287e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2288e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    abstract void setBody(MockResponse response, Buffer content, int chunkSize) throws IOException;
2289e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2290e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    void setBody(MockResponse response, String content, int chunkSize) throws IOException {
2291e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      setBody(response, new Buffer().writeUtf8(content), chunkSize);
2292e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
2293e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2294e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2295e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** Returns a gzipped copy of {@code bytes}. */
2296e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  public Buffer gzip(String data) throws IOException {
2297e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer result = new Buffer();
2298e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    BufferedSink sink = Okio.buffer(new GzipSink(result));
2299e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    sink.writeUtf8(data);
2300e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    sink.close();
2301e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return result;
2302e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2303e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller}
2304