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.FileSystem;
2371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport com.squareup.okhttp.internal.io.InMemoryFileSystem;
24e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport com.squareup.okhttp.mockwebserver.MockResponse;
25e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport com.squareup.okhttp.mockwebserver.MockWebServer;
26e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport com.squareup.okhttp.mockwebserver.RecordedRequest;
27e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.io.File;
28e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.io.IOException;
29e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.net.CookieHandler;
30e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.net.CookieManager;
31e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.net.HttpCookie;
32e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.net.HttpURLConnection;
33e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.net.ResponseCache;
34e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.security.Principal;
35e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.security.cert.Certificate;
36e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.text.DateFormat;
37e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.text.SimpleDateFormat;
38e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.ArrayList;
39e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.Arrays;
40e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.Date;
41e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.Iterator;
42e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.List;
43e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.Locale;
44e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.NoSuchElementException;
45e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.TimeZone;
46e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.concurrent.TimeUnit;
47e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.concurrent.atomic.AtomicReference;
483be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport javax.net.ssl.HostnameVerifier;
493be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport javax.net.ssl.SSLContext;
503be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport javax.net.ssl.SSLSession;
513be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport okio.Buffer;
523be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport okio.BufferedSink;
533be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport okio.BufferedSource;
543be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport okio.GzipSink;
553be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport okio.Okio;
563be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport org.junit.After;
573be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport org.junit.Before;
583be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport org.junit.Rule;
593be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fullerimport org.junit.Test;
60e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
61e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.mockwebserver.SocketPolicy.DISCONNECT_AT_END;
62e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertEquals;
63e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertFalse;
64e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertNotNull;
65e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertNull;
66e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertTrue;
67e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.fail;
68e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
69e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller/** Test caching with {@link OkUrlFactory}. */
70e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerpublic final class CacheTest {
71e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private static final HostnameVerifier NULL_HOSTNAME_VERIFIER = new HostnameVerifier() {
72e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    @Override public boolean verify(String s, SSLSession sslSession) {
73e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      return true;
74e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
75e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  };
76e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
7771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Rule public MockWebServer server = new MockWebServer();
7871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Rule public MockWebServer server2 = new MockWebServer();
79e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
8071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  private final SSLContext sslContext = SslContextBuilder.localhost();
8171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  private final FileSystem fileSystem = new InMemoryFileSystem();
82e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private final OkHttpClient client = new OkHttpClient();
83e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private Cache cache;
84e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private final CookieManager cookieManager = new CookieManager();
85e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
86e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Before public void setUp() throws Exception {
87e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.setProtocolNegotiationEnabled(false);
8871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    cache = new Cache(new File("/cache/"), Integer.MAX_VALUE, fileSystem);
89e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.setCache(cache);
90e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    CookieHandler.setDefault(cookieManager);
91e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
92e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
93e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @After public void tearDown() throws Exception {
94e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    ResponseCache.setDefault(null);
95e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    CookieHandler.setDefault(null);
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!
269e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("ABC", response2.body().source().readUtf8());
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
465e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void serverDisconnectsPrematurelyWithContentLengthHeader() throws IOException {
466e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testServerPrematureDisconnect(TransferKind.FIXED_LENGTH);
467e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
468e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
469e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void serverDisconnectsPrematurelyWithChunkedEncoding() throws IOException {
470e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testServerPrematureDisconnect(TransferKind.CHUNKED);
471e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
472e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
473e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void serverDisconnectsPrematurelyWithNoLengthHeaders() throws IOException {
474e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Intentionally empty. This case doesn't make sense because there's no
475e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // such thing as a premature disconnect when the disconnect itself
476e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // indicates the end of the data stream.
477e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
478e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
479e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void testServerPrematureDisconnect(TransferKind transferKind) throws IOException {
480e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    MockResponse mockResponse = new MockResponse();
481e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    transferKind.setBody(mockResponse, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 16);
482e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(truncateViolently(mockResponse, 16));
483e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
484e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("Request #2"));
485e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
48671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    BufferedSource bodySource = get(server.url("/")).body().source();
487e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("ABCDE", bodySource.readUtf8Line());
488e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
489e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      bodySource.readUtf8Line();
490e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail("This implementation silently ignored a truncated HTTP body.");
491e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IOException expected) {
492e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } finally {
493e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      bodySource.close();
494e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
495e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
496e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getWriteAbortCount());
497e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getWriteSuccessCount());
49871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response = get(server.url("/"));
499e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("Request #2", response.body().string());
500e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getWriteAbortCount());
501e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getWriteSuccessCount());
502e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
503e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
504e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void clientPrematureDisconnectWithContentLengthHeader() throws IOException {
505e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testClientPrematureDisconnect(TransferKind.FIXED_LENGTH);
506e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
507e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
508e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void clientPrematureDisconnectWithChunkedEncoding() throws IOException {
509e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testClientPrematureDisconnect(TransferKind.CHUNKED);
510e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
511e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
512e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void clientPrematureDisconnectWithNoLengthHeaders() throws IOException {
513e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testClientPrematureDisconnect(TransferKind.END_OF_STREAM);
514e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
515e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
516e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void testClientPrematureDisconnect(TransferKind transferKind) throws IOException {
517e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Setting a low transfer speed ensures that stream discarding will time out.
518e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    MockResponse mockResponse = new MockResponse()
519e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .throttleBody(6, 1, TimeUnit.SECONDS);
520e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    transferKind.setBody(mockResponse, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 1024);
521e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(mockResponse);
522e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
523e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("Request #2"));
524e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
52571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response1 = get(server.url("/"));
526e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    BufferedSource in = response1.body().source();
527e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("ABCDE", in.readUtf8(5));
528e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    in.close();
529e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
530e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      in.readByte();
531e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail("Expected an IllegalStateException because the source is closed.");
532e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalStateException expected) {
533e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
534e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
535e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getWriteAbortCount());
536e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getWriteSuccessCount());
53771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response2 = get(server.url("/"));
538e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("Request #2", response2.body().string());
539e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getWriteAbortCount());
540e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getWriteSuccessCount());
541e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
542e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
543e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void defaultExpirationDateFullyCachedForLessThan24Hours() throws Exception {
544e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //      last modified: 105 seconds ago
545e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //             served:   5 seconds ago
546e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //   default lifetime: (105 - 5) / 10 = 10 seconds
547e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //            expires:  10 seconds from served date = 5 seconds from now
548e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
549e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-105, TimeUnit.SECONDS))
550e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-5, TimeUnit.SECONDS))
551e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
552e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
55371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
554e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = get(url);
555e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
556e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
557e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = get(url);
558e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
559e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(response2.header("Warning"));
560e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
561e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
562e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void defaultExpirationDateConditionallyCached() throws Exception {
563e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //      last modified: 115 seconds ago
564e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //             served:  15 seconds ago
565e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //   default lifetime: (115 - 15) / 10 = 10 seconds
566e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //            expires:  10 seconds from served date = 5 seconds ago
567e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String lastModifiedDate = formatDate(-115, TimeUnit.SECONDS);
568e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
569e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + lastModifiedDate)
570e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-15, TimeUnit.SECONDS)));
571e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(lastModifiedDate, conditionalRequest.getHeader("If-Modified-Since"));
572e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
573e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
574e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void defaultExpirationDateFullyCachedForMoreThan24Hours() throws Exception {
575e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //      last modified: 105 days ago
576e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //             served:   5 days ago
577e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //   default lifetime: (105 - 5) / 10 = 10 days
578e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    //            expires:  10 days from served date = 5 days from now
579e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
580e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-105, TimeUnit.DAYS))
581e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-5, TimeUnit.DAYS))
582e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
583e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
58471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
58571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response = get(server.url("/"));
586e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response.body().string());
587e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("113 HttpURLConnection \"Heuristic expiration\"", response.header("Warning"));
588e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
589e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
590e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void noDefaultExpirationForUrlsWithQueryString() throws Exception {
591e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
592e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-105, TimeUnit.SECONDS))
593e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-5, TimeUnit.SECONDS))
594e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
595e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
596e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
597e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
59871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/").newBuilder().addQueryParameter("foo", "bar").build();
599e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
600e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", get(url).body().string());
601e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
602e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
603e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void expirationDateInThePastWithLastModifiedHeader() throws Exception {
604e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
605e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
606e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + lastModifiedDate)
607e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
608e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(lastModifiedDate, conditionalRequest.getHeader("If-Modified-Since"));
609e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
610e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
611e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void expirationDateInThePastWithNoLastModifiedHeader() throws Exception {
612e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNotCached(new MockResponse()
613e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
614e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
615e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
616e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void expirationDateInTheFuture() throws Exception {
617e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFullyCached(new MockResponse()
618e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
619e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
620e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
621e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void maxAgePreferredWithMaxAgeAndExpires() throws Exception {
622e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFullyCached(new MockResponse()
623e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
624e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS))
625e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60"));
626e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
627e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
628e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void maxAgeInThePastWithDateAndLastModifiedHeaders() throws Exception {
629e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
630e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
631e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-120, TimeUnit.SECONDS))
632e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + lastModifiedDate)
633e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60"));
634e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(lastModifiedDate, conditionalRequest.getHeader("If-Modified-Since"));
635e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
636e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
637e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void maxAgeInThePastWithDateHeaderButNoLastModifiedHeader() throws Exception {
638e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Chrome interprets max-age relative to the local clock. Both our cache
639e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // and Firefox both use the earlier of the local and server's clock.
640e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNotCached(new MockResponse()
641e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-120, TimeUnit.SECONDS))
642e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60"));
643e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
644e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
645e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void maxAgeInTheFutureWithDateHeader() throws Exception {
646e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFullyCached(new MockResponse()
647e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
648e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60"));
649e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
650e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
651e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void maxAgeInTheFutureWithNoDateHeader() throws Exception {
652e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFullyCached(new MockResponse()
653e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60"));
654e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
655e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
656e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void maxAgeWithLastModifiedButNoServedDate() throws Exception {
657e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFullyCached(new MockResponse()
658e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
659e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60"));
660e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
661e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
662e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void maxAgeInTheFutureWithDateAndLastModifiedHeaders() throws Exception {
663e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFullyCached(new MockResponse()
664e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
665e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
666e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60"));
667e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
668e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
669e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void maxAgePreferredOverLowerSharedMaxAge() throws Exception {
670e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFullyCached(new MockResponse()
671e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
672e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: s-maxage=60")
673e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=180"));
674e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
675e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
676e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void maxAgePreferredOverHigherMaxAge() throws Exception {
677e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNotCached(new MockResponse()
678e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
679e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: s-maxage=180")
680e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60"));
681e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
682e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
683e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMethodOptionsIsNotCached() throws Exception {
684e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRequestMethod("OPTIONS", false);
685e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
686e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
687e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMethodGetIsCached() throws Exception {
688e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRequestMethod("GET", true);
689e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
690e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
691e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMethodHeadIsNotCached() throws Exception {
692e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // We could support this but choose not to for implementation simplicity
693e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRequestMethod("HEAD", false);
694e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
695e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
696e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMethodPostIsNotCached() throws Exception {
697e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // We could support this but choose not to for implementation simplicity
698e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRequestMethod("POST", false);
699e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
700e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
701e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMethodPutIsNotCached() throws Exception {
702e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRequestMethod("PUT", false);
703e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
704e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
705e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMethodDeleteIsNotCached() throws Exception {
706e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRequestMethod("DELETE", false);
707e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
708e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
709e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMethodTraceIsNotCached() throws Exception {
710e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRequestMethod("TRACE", false);
711e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
712e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
713e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void testRequestMethod(String requestMethod, boolean expectCached) throws Exception {
714e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 1. seed the cache (potentially)
715e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 2. expect a cache hit or miss
716e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
717e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
718e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("X-Response-ID: 1"));
719e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
720e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("X-Response-ID: 2"));
721e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
72271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
723e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
724e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
725e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
726e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .method(requestMethod, requestBodyOrNull(requestMethod))
727e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
728e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request).execute();
729e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    response1.body().close();
730e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("1", response1.header("X-Response-ID"));
731e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
732e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = get(url);
733e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    response2.body().close();
734e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    if (expectCached) {
735e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("1", response2.header("X-Response-ID"));
736e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } else {
737e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("2", response2.header("X-Response-ID"));
738e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
739e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
740e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
741e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private RequestBody requestBodyOrNull(String requestMethod) {
742e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return (requestMethod.equals("POST") || requestMethod.equals("PUT"))
743e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          ? RequestBody.create(MediaType.parse("text/plain"), "foo")
744e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          : null;
745e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
746e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
747e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void postInvalidatesCache() throws Exception {
748e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testMethodInvalidates("POST");
749e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
750e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
751e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void putInvalidatesCache() throws Exception {
752e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testMethodInvalidates("PUT");
753e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
754e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
755e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void deleteMethodInvalidatesCache() throws Exception {
756e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testMethodInvalidates("DELETE");
757e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
758e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
759e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void testMethodInvalidates(String requestMethod) throws Exception {
760e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 1. seed the cache
761e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 2. invalidate it
762e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 3. expect a cache miss
763e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
764e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
765e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
766e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
767e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
768e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
769e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("C"));
770e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
77171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
772e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
773e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
774e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
775e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
776e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
777e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .method(requestMethod, requestBodyOrNull(requestMethod))
778e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
779e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response invalidate = client.newCall(request).execute();
780e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", invalidate.body().string());
781e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
782e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("C", get(url).body().string());
783e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
784e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
785e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void postInvalidatesCacheWithUncacheableResponse() throws Exception {
786e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 1. seed the cache
787e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 2. invalidate it with uncacheable response
788e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 3. expect a cache miss
789e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
790e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
791e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
792e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
793e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B")
794e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(500));
795e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
796e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("C"));
797e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
79871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
799e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
800e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
801e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
802e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
803e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
804e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .method("POST", requestBodyOrNull("POST"))
805e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
806e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response invalidate = client.newCall(request).execute();
807e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", invalidate.body().string());
808e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
809e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("C", get(url).body().string());
810e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
811e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
812e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void etag() throws Exception {
813e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
814e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("ETag: v1"));
815e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("v1", conditionalRequest.getHeader("If-None-Match"));
816e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
817e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
8183be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fuller  /** If both If-Modified-Since and If-None-Match conditions apply, send only If-None-Match. */
819e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void etagAndExpirationDateInThePast() throws Exception {
820e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
821e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
822e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("ETag: v1")
823e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + lastModifiedDate)
824e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
825e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("v1", conditionalRequest.getHeader("If-None-Match"));
8263be78b8b0ca13d9e05e2327acb8d8654f719a3f6Neil Fuller    assertNull(conditionalRequest.getHeader("If-Modified-Since"));
827e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
828e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
829e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void etagAndExpirationDateInTheFuture() throws Exception {
830e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFullyCached(new MockResponse()
831e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("ETag: v1")
832e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
833e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
834e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
835e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
836e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void cacheControlNoCache() throws Exception {
837e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNotCached(new MockResponse()
838e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: no-cache"));
839e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
840e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
841e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void cacheControlNoCacheAndExpirationDateInTheFuture() throws Exception {
842e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
843e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
844e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + lastModifiedDate)
845e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
846e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: no-cache"));
847e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(lastModifiedDate, conditionalRequest.getHeader("If-Modified-Since"));
848e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
849e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
850e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void pragmaNoCache() throws Exception {
851e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNotCached(new MockResponse()
852e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Pragma: no-cache"));
853e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
854e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
855e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void pragmaNoCacheAndExpirationDateInTheFuture() throws Exception {
856e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
857e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest conditionalRequest = assertConditionallyCached(new MockResponse()
858e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + lastModifiedDate)
859e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
860e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Pragma: no-cache"));
861e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(lastModifiedDate, conditionalRequest.getHeader("If-Modified-Since"));
862e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
863e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
864e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void cacheControlNoStore() throws Exception {
865e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNotCached(new MockResponse()
866e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: no-store"));
867e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
868e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
869e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void cacheControlNoStoreAndExpirationDateInTheFuture() throws Exception {
870e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNotCached(new MockResponse()
871e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
872e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
873e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: no-store"));
874e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
875e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
876e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void partialRangeResponsesDoNotCorruptCache() throws Exception {
877e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 1. request a range
878e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // 2. request a full document, expecting a cache miss
879e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
880e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("AA")
881e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_PARTIAL)
882e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
883e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Content-Range: bytes 1000-1001/2000"));
884e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
885e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("BB"));
886e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
88771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
888e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
889e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
890e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
891e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Range", "bytes=1000-1001")
892e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
893e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response range = client.newCall(request).execute();
894e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("AA", range.body().string());
895e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
896e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("BB", get(url).body().string());
897e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
898e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
899e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void serverReturnsDocumentOlderThanCache() throws Exception {
900e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
901e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
902e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
903e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
904e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
905e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B")
906e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-4, TimeUnit.HOURS)));
907e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
90871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
909e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
910e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
911e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
912e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
913e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
914e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void clientSideNoStore() throws Exception {
915e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
916e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
917e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
918e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
919e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
920e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
921e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
922e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request1 = new Request.Builder()
92371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
924e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .cacheControl(new CacheControl.Builder().noStore().build())
925e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
926e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request1).execute();
927e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
928e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
929e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request2 = new Request.Builder()
93071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
931e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
932e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = client.newCall(request2).execute();
933e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response2.body().string());
934e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
935e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
936e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void nonIdentityEncodingAndConditionalCache() throws Exception {
937e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNonIdentityEncodingCached(new MockResponse()
938e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
939e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
940e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
941e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
942e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void nonIdentityEncodingAndFullCache() throws Exception {
943e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNonIdentityEncodingCached(new MockResponse()
944e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
945e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
946e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
947e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
948e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void assertNonIdentityEncodingCached(MockResponse response) throws Exception {
949e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(response
950e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody(gzip("ABCABCABC"))
951e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Content-Encoding: gzip"));
952e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
953e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
954e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
955e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
956e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
957e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // At least three request/response pairs are required because after the first request is cached
958e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // a different execution path might be taken. Thus modifications to the cache applied during
959e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // the second request might not be visible until another request is performed.
96071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("ABCABCABC", get(server.url("/")).body().string());
96171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("ABCABCABC", get(server.url("/")).body().string());
96271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("ABCABCABC", get(server.url("/")).body().string());
963e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
964e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
965e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void notModifiedSpecifiesEncoding() throws Exception {
966e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
967e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody(gzip("ABCABCABC"))
968e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Content-Encoding: gzip")
969e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
970e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
971e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
972e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED)
973e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Content-Encoding: gzip"));
974e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
975e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("DEFDEFDEF"));
976e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
97771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("ABCABCABC", get(server.url("/")).body().string());
97871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("ABCABCABC", get(server.url("/")).body().string());
97971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("DEFDEFDEF", get(server.url("/")).body().string());
980e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
981e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
982e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** https://github.com/square/okhttp/issues/947 */
983e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void gzipAndVaryOnAcceptEncoding() throws Exception {
984e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
985e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody(gzip("ABCABCABC"))
986e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Content-Encoding: gzip")
987e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Encoding")
988e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60"));
989e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
990e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("FAIL"));
991e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
99271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("ABCABCABC", get(server.url("/")).body().string());
99371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("ABCABCABC", get(server.url("/")).body().string());
994e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
995e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
996e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void conditionalCacheHitIsNotDoublePooled() throws Exception {
997e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
998e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("ETag: v1")
999e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1000e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1001e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .clearHeaders()
1002e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1003e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1004e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    ConnectionPool pool = ConnectionPool.getDefault();
1005e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    pool.evictAll();
1006e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.setConnectionPool(pool);
1007e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
100871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
100971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1010e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, client.getConnectionPool().getConnectionCount());
1011e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1012e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1013e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void expiresDateBeforeModifiedDate() throws Exception {
1014e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertConditionallyCached(new MockResponse()
1015e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1016e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(-2, TimeUnit.HOURS)));
1017e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1018e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1019e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMaxAge() throws IOException {
1020e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1021e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
1022e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
1023e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-1, TimeUnit.MINUTES))
1024e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
1025e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1026e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1027e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
102871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1029e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1030e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
103171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1032e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "max-age=30")
1033e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1034e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1035e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response.body().string());
1036e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1037e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1038e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMinFresh() throws IOException {
1039e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1040e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
1041e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1042e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
1043e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1044e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1045e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
104671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1047e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1048e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
104971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1050e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "min-fresh=120")
1051e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1052e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1053e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response.body().string());
1054e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1055e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1056e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMaxStale() throws IOException {
1057e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1058e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
1059e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=120")
1060e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-4, TimeUnit.MINUTES)));
1061e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1062e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1063e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
106471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1065e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1066e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
106771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1068e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "max-stale=180")
1069e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1070e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1071e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response.body().string());
1072e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("110 HttpURLConnection \"Response is stale\"", response.header("Warning"));
1073e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1074e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1075e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMaxStaleDirectiveWithNoValue() throws IOException {
1076e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Add a stale response to the cache.
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    // With max-stale, we'll return that stale response.
1087e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
108871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1089e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "max-stale")
1090e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1091e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1092e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response.body().string());
1093e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("110 HttpURLConnection \"Response is stale\"", response.header("Warning"));
1094e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1095e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1096e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestMaxStaleNotHonoredWithMustRevalidate() throws IOException {
1097e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1098e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
1099e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=120, must-revalidate")
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    Request request = new Request.Builder()
110771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1108e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "max-stale=180")
1109e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1110e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1111e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response.body().string());
1112e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1113e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1114e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestOnlyIfCachedWithNoResponseCached() throws IOException {
1115e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // (no responses enqueued)
1116e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1117e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
111871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1119e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "only-if-cached")
1120e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1121e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1122e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(response.body().source().exhausted());
1123e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(504, response.code());
1124e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getRequestCount());
1125e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getNetworkCount());
1126e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getHitCount());
1127e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1128e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1129e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestOnlyIfCachedWithFullResponseCached() throws IOException {
1130e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1131e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
1132e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=30")
1133e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
1134e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
113571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1136e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
113771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1138e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "only-if-cached")
1139e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1140e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1141e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response.body().string());
1142e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, cache.getRequestCount());
1143e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getNetworkCount());
1144e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getHitCount());
1145e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1146e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1147e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestOnlyIfCachedWithConditionalResponseCached() throws IOException {
1148e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1149e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
1150e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=30")
1151e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-1, TimeUnit.MINUTES)));
1152e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
115371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1154e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
115571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1156e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "only-if-cached")
1157e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1158e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1159e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(response.body().source().exhausted());
1160e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(504, response.code());
1161e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, cache.getRequestCount());
1162e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getNetworkCount());
1163e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getHitCount());
1164e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1165e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1166e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestOnlyIfCachedWithUnhelpfulResponseCached() throws IOException {
1167e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1168e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1169e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
117071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1171e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
117271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1173e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "only-if-cached")
1174e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1175e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1176e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(response.body().source().exhausted());
1177e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(504, response.code());
1178e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, cache.getRequestCount());
1179e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getNetworkCount());
1180e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getHitCount());
1181e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1182e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1183e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestCacheControlNoCache() throws Exception {
1184e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1185e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
1186e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
1187e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1188e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1189e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1190e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1191e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
119271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1193e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1194e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
1195e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1196e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Cache-Control", "no-cache")
1197e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1198e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1199e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response.body().string());
1200e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1201e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1202e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void requestPragmaNoCache() throws Exception {
1203e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1204e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
1205e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
1206e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1207e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1208e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1209e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1210e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
121171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1212e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1213e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
1214e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1215e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Pragma", "no-cache")
1216e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1217e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1218e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response.body().string());
1219e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1220e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1221e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void clientSuppliedIfModifiedSinceWithCachedResult() throws Exception {
1222e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    MockResponse response = new MockResponse()
1223e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("ETag: v3")
1224e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0");
1225e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String ifModifiedSinceDate = formatDate(-24, TimeUnit.HOURS);
1226e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest request =
1227e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertClientSuppliedCondition(response, "If-Modified-Since", ifModifiedSinceDate);
1228e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(ifModifiedSinceDate, request.getHeader("If-Modified-Since"));
1229e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(request.getHeader("If-None-Match"));
1230e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1231e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1232e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void clientSuppliedIfNoneMatchSinceWithCachedResult() throws Exception {
1233e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String lastModifiedDate = formatDate(-3, TimeUnit.MINUTES);
1234e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    MockResponse response = new MockResponse()
1235e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + lastModifiedDate)
1236e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
1237e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0");
1238e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest request = assertClientSuppliedCondition(response, "If-None-Match", "v1");
1239e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("v1", request.getHeader("If-None-Match"));
1240e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(request.getHeader("If-Modified-Since"));
1241e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1242e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1243e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private RecordedRequest assertClientSuppliedCondition(MockResponse seed, String conditionName,
1244e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      String conditionValue) throws Exception {
1245e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(seed.setBody("A"));
1246e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1247e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1248e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
124971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1250e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1251e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1252e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
1253e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1254e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header(conditionName, conditionValue)
1255e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1256e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1257e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, response.code());
1258e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("", response.body().string());
1259e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1260e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.takeRequest(); // seed
1261e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return server.takeRequest();
1262e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1263e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1264e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
1265e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * For Last-Modified and Date headers, we should echo the date back in the
1266e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * exact format we were served.
1267e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
1268e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void retainServedDateFormat() throws Exception {
1269e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Serve a response with a non-standard date format that OkHttp supports.
1270e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Date lastModifiedDate = new Date(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(-1));
1271e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Date servedDate = new Date(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(-2));
1272e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DateFormat dateFormat = new SimpleDateFormat("EEE dd-MMM-yyyy HH:mm:ss z", Locale.US);
1273e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    dateFormat.setTimeZone(TimeZone.getTimeZone("EDT"));
1274e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String lastModifiedString = dateFormat.format(lastModifiedDate);
1275e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String servedString = dateFormat.format(servedDate);
1276e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1277e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // This response should be conditionally cached.
1278e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1279e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + lastModifiedString)
1280e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Expires: " + servedString)
1281e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1282e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1283e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1284e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
128571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
128671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1287e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1288e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // The first request has no conditions.
1289e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest request1 = server.takeRequest();
1290e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(request1.getHeader("If-Modified-Since"));
1291e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1292e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // The 2nd request uses the server's date format.
1293e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest request2 = server.takeRequest();
1294e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(lastModifiedString, request2.getHeader("If-Modified-Since"));
1295e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1296e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1297e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void clientSuppliedConditionWithoutCachedResult() throws Exception {
1298e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1299e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1300e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1301e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
130271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/"))
1303e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("If-Modified-Since", formatDate(-24, TimeUnit.HOURS))
1304e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1305e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1306e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, response.code());
1307e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("", response.body().string());
1308e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1309e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1310e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void authorizationRequestFullyCached() throws Exception {
1311e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1312e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1313e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1314e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1315e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1316e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
131771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1318e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
1319e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1320e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Authorization", "password")
1321e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1322e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1323e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response.body().string());
1324e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1325e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1326e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1327e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void contentLocationDoesNotPopulateCache() throws Exception {
1328e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1329e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1330e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Content-Location: /bar")
1331e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1332e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1333e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1334e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
133571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/foo")).body().string());
133671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("B", get(server.url("/bar")).body().string());
1337e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1338e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1339e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void connectionIsReturnedToPoolAfterConditionalSuccess() throws Exception {
1340e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1341e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1342e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1343e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1344e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1345e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1346e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1347e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1348e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
134971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/a")).body().string());
135071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/a")).body().string());
135171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("B", get(server.url("/b")).body().string());
1352e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1353e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, server.takeRequest().getSequenceNumber());
1354e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, server.takeRequest().getSequenceNumber());
1355e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, server.takeRequest().getSequenceNumber());
1356e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1357e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1358e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void statisticsConditionalCacheMiss() throws Exception {
1359e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1360e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1361e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1362e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1363e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1364e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1365e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1366e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("C"));
1367e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
136871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1369e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getRequestCount());
1370e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getNetworkCount());
1371e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getHitCount());
137271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("B", get(server.url("/")).body().string());
137371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("C", get(server.url("/")).body().string());
1374e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(3, cache.getRequestCount());
1375e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(3, cache.getNetworkCount());
1376e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getHitCount());
1377e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1378e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1379e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void statisticsConditionalCacheHit() throws Exception {
1380e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1381e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1382e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1383e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1384e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1385e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1386e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1387e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1388e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
138971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1390e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getRequestCount());
1391e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getNetworkCount());
1392e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getHitCount());
139371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
139471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1395e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(3, cache.getRequestCount());
1396e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(3, cache.getNetworkCount());
1397e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, cache.getHitCount());
1398e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1399e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1400e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void statisticsFullCacheHit() throws Exception {
1401e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1402e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1403e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1404e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
140571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1406e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getRequestCount());
1407e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getNetworkCount());
1408e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.getHitCount());
140971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
141071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1411e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(3, cache.getRequestCount());
1412e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, cache.getNetworkCount());
1413e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, cache.getHitCount());
1414e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1415e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1416e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyMatchesChangedRequestHeaderField() throws Exception {
1417e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1418e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1419e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Language")
1420e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1421e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1422e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1423e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
142471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1425e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request frRequest = new Request.Builder()
1426e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1427e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "fr-CA")
1428e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1429e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response frResponse = client.newCall(frRequest).execute();
1430e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", frResponse.body().string());
1431e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1432e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request enRequest = new Request.Builder()
1433e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1434e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "en-US")
1435e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1436e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response enResponse = client.newCall(enRequest).execute();
1437e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", enResponse.body().string());
1438e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1439e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1440e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyMatchesUnchangedRequestHeaderField() throws Exception {
1441e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1442e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1443e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Language")
1444e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1445e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1446e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1447e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
144871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1449e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
1450e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1451e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "fr-CA")
1452e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1453e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request).execute();
1454e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1455e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request1 = new Request.Builder()
1456e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1457e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "fr-CA")
1458e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1459e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = client.newCall(request1).execute();
1460e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1461e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1462e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1463e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyMatchesAbsentRequestHeaderField() throws Exception {
1464e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1465e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1466e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Foo")
1467e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1468e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1469e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1470e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
147171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
147271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1473e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1474e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1475e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyMatchesAddedRequestHeaderField() throws Exception {
1476e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1477e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1478e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Foo")
1479e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1480e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1481e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1482e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
148371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1484e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
148571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/")).header("Foo", "bar")
1486e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1487e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1488e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response.body().string());
1489e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1490e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1491e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyMatchesRemovedRequestHeaderField() throws Exception {
1492e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1493e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1494e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Foo")
1495e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1496e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1497e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1498e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1499e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
150071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/")).header("Foo", "bar")
1501e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1502e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response fooresponse = client.newCall(request).execute();
1503e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", fooresponse.body().string());
150471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("B", get(server.url("/")).body().string());
1505e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1506e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1507e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyFieldsAreCaseInsensitive() throws Exception {
1508e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1509e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1510e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: ACCEPT-LANGUAGE")
1511e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1512e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1513e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1514e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
151571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1516e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
1517e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1518e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "fr-CA")
1519e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1520e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request).execute();
1521e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1522e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request1 = new Request.Builder()
1523e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1524e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("accept-language", "fr-CA")
1525e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1526e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = client.newCall(request1).execute();
1527e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1528e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1529e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1530e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyMultipleFieldsWithMatch() throws Exception {
1531e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1532e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1533e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Language, Accept-Charset")
1534e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Encoding")
1535e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1536e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1537e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1538e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
153971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1540e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
1541e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1542e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "fr-CA")
1543e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Charset", "UTF-8")
1544e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Encoding", "identity")
1545e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1546e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request).execute();
1547e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1548e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request1 = new Request.Builder()
1549e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1550e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "fr-CA")
1551e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Charset", "UTF-8")
1552e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Encoding", "identity")
1553e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1554e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = client.newCall(request1).execute();
1555e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1556e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1557e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1558e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyMultipleFieldsWithNoMatch() throws Exception {
1559e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1560e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1561e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Language, Accept-Charset")
1562e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Encoding")
1563e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1564e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1565e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1566e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
156771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1568e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request frRequest = 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 frResponse = client.newCall(frRequest).execute();
1575e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", frResponse.body().string());
1576e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request enRequest = new Request.Builder()
1577e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1578e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "en-CA")
1579e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Charset", "UTF-8")
1580e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Encoding", "identity")
1581e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1582e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response enResponse = client.newCall(enRequest).execute();
1583e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", enResponse.body().string());
1584e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1585e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1586e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyMultipleFieldValuesWithMatch() throws Exception {
1587e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1588e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1589e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Language")
1590e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1591e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1592e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1593e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
159471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1595e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request1 = new Request.Builder()
1596e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1597e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Accept-Language", "fr-CA, fr-FR")
1598e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Accept-Language", "en-US")
1599e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1600e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request1).execute();
1601e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1602e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1603e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request2 = new Request.Builder()
1604e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1605e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Accept-Language", "fr-CA, fr-FR")
1606e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Accept-Language", "en-US")
1607e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1608e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = client.newCall(request2).execute();
1609e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1610e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1611e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1612e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyMultipleFieldValuesWithNoMatch() throws Exception {
1613e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1614e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1615e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Language")
1616e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1617e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1618e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1619e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
162071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1621e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request1 = new Request.Builder()
1622e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1623e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Accept-Language", "fr-CA, fr-FR")
1624e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Accept-Language", "en-US")
1625e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1626e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request1).execute();
1627e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1628e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1629e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request2 = new Request.Builder()
1630e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1631e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Accept-Language", "fr-CA")
1632e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Accept-Language", "en-US")
1633e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1634e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = client.newCall(request2).execute();
1635e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response2.body().string());
1636e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1637e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1638e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyAsterisk() throws Exception {
163971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.enqueue(new MockResponse()
1640e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1641e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: *")
1642e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1643e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1644e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1645e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
164671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
164771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("B", get(server.url("/")).body().string());
1648e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1649e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1650e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void varyAndHttps() throws Exception {
1651e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.useHttps(sslContext.getSocketFactory(), false);
1652e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1653e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1654e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Vary: Accept-Language")
1655e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1656e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1657e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1658e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1659e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.setSslSocketFactory(sslContext.getSocketFactory());
1660e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
1661e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
166271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1663e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request1 = new Request.Builder()
1664e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1665e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "en-US")
1666e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1667e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = client.newCall(request1).execute();
1668e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1669e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1670e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request2 = new Request.Builder()
1671e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
1672e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .header("Accept-Language", "en-US")
1673e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1674e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = client.newCall(request2).execute();
1675e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1676e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1677e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1678e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void cachePlusCookies() throws Exception {
1679e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1680e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Set-Cookie: a=FIRST; domain=" + server.getCookieDomain() + ";")
1681e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1682e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1683e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1684e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1685e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Set-Cookie: a=SECOND; domain=" + server.getCookieDomain() + ";")
1686e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1687e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
168871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1689e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1690e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCookies(url, "a=FIRST");
1691e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1692e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertCookies(url, "a=SECOND");
1693e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1694e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1695e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void getHeadersReturnsNetworkEndToEndHeaders() throws Exception {
1696e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1697e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Allow: GET, HEAD")
1698e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1699e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1700e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1701e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1702e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Allow: GET, HEAD, PUT")
1703e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1704e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
170571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response1 = get(server.url("/"));
1706e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1707e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("GET, HEAD", response1.header("Allow"));
1708e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
170971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response2 = get(server.url("/"));
1710e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1711e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("GET, HEAD, PUT", response2.header("Allow"));
1712e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1713e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1714e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void getHeadersReturnsCachedHopByHopHeaders() throws Exception {
1715e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1716e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Transfer-Encoding: identity")
1717e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1718e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1719e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1720e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1721e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Transfer-Encoding: none")
1722e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1723e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
172471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response1 = get(server.url("/"));
1725e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1726e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("identity", response1.header("Transfer-Encoding"));
1727e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
172871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response2 = get(server.url("/"));
1729e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1730e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("identity", response2.header("Transfer-Encoding"));
1731e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1732e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1733e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void getHeadersDeletesCached100LevelWarnings() throws Exception {
1734e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1735e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Warning: 199 test danger")
1736e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1737e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1738e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1739e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1740e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1741e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
174271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response1 = get(server.url("/"));
1743e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1744e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("199 test danger", response1.header("Warning"));
1745e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
174671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response2 = get(server.url("/"));
1747e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1748e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(null, response2.header("Warning"));
1749e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1750e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1751e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void getHeadersRetainsCached200LevelWarnings() throws Exception {
1752e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1753e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Warning: 299 test danger")
1754e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
1755e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1756e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1757e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1758e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1759e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
176071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response1 = get(server.url("/"));
1761e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1762e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("299 test danger", response1.header("Warning"));
1763e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
176471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response2 = get(server.url("/"));
1765e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1766e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("299 test danger", response2.header("Warning"));
1767e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1768e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
176971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  public void assertCookies(HttpUrl url, String... expectedCookies) throws Exception {
1770e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    List<String> actualCookies = new ArrayList<>();
177171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    for (HttpCookie cookie : cookieManager.getCookieStore().get(url.uri())) {
1772e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      actualCookies.add(cookie.toString());
1773e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
1774e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(Arrays.asList(expectedCookies), actualCookies);
1775e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1776e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1777e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void doNotCachePartialResponse() throws Exception  {
1778e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNotCached(new MockResponse()
177971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_PARTIAL)
178071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
178171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .addHeader("Content-Range: bytes 100-100/200")
178271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .addHeader("Cache-Control: max-age=60"));
1783e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1784e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1785e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void conditionalHitUpdatesCache() throws Exception {
1786e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1787e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Last-Modified: " + formatDate(0, TimeUnit.SECONDS))
1788e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1789e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1790e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1791e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=30")
1792e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Allow: GET, HEAD")
1793e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1794e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1795e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1796e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1797e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // cache miss; seed the cache
179871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response1 = get(server.url("/a"));
1799e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
1800e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(null, response1.header("Allow"));
1801e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1802e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // conditional cache hit; update the cache
180371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response2 = get(server.url("/a"));
1804e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(HttpURLConnection.HTTP_OK, response2.code());
1805e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
1806e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("GET, HEAD", response2.header("Allow"));
1807e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1808e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // full cache hit
180971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response3 = get(server.url("/a"));
1810e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response3.body().string());
1811e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("GET, HEAD", response3.header("Allow"));
1812e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1813e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(2, server.getRequestCount());
1814e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1815e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1816e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void responseSourceHeaderCached() throws IOException {
1817e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1818e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
1819e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=30")
1820e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
1821e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
182271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
1823e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
182471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .url(server.url("/")).header("Cache-Control", "only-if-cached")
1825e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
1826e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = client.newCall(request).execute();
1827e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response.body().string());
1828e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1829e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1830e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void responseSourceHeaderConditionalCacheFetched() throws IOException {
1831e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1832e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
1833e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=30")
1834e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(-31, TimeUnit.MINUTES)));
1835e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1836e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B")
1837e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=30")
1838e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
1839e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
184071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
184171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response = get(server.url("/"));
1842e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response.body().string());
1843e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1844e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1845e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void responseSourceHeaderConditionalCacheNotFetched() throws IOException {
1846e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1847e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A")
1848e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=0")
1849e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
1850e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1851e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(304));
1852e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
185371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(server.url("/")).body().string());
185471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response = get(server.url("/"));
1855e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response.body().string());
1856e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1857e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1858e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void responseSourceHeaderFetched() throws IOException {
1859e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1860e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1861e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
186271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response = get(server.url("/"));
1863e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response.body().string());
1864e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1865e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1866e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void emptyResponseHeaderNameFromCacheIsLenient() throws Exception {
1867e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Headers.Builder headers = new Headers.Builder()
1868e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .add("Cache-Control: max-age=120");
1869e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Internal.instance.addLenient(headers, ": A");
1870e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1871e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setHeaders(headers.build())
1872e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("body"));
1873e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
187471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Response response = get(server.url("/"));
1875e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response.header(""));
1876e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1877e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1878e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
1879e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Old implementations of OkHttp's response cache wrote header fields like
1880e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * ":status: 200 OK". This broke our cached response parser because it split
1881e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * on the first colon. This regression test exists to help us read these old
1882e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * bad cache entries.
1883e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   *
1884e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * https://github.com/square/okhttp/issues/227
1885e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
1886e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void testGoldenCacheResponse() throws Exception {
1887e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
1888e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1889e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .clearHeaders()
1890e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1891e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
189271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1893e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String urlKey = Util.md5Hex(url.toString());
1894e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String entryMetadata = ""
1895e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "" + url + "\n"
1896e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "GET\n"
1897e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "0\n"
1898e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "HTTP/1.1 200 OK\n"
1899e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "7\n"
1900e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + ":status: 200 OK\n"
1901e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + ":version: HTTP/1.1\n"
1902e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "etag: foo\n"
1903e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "content-length: 3\n"
1904e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "OkHttp-Received-Millis: " + System.currentTimeMillis() + "\n"
1905e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "X-Android-Response-Source: NETWORK 200\n"
1906e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "OkHttp-Sent-Millis: " + System.currentTimeMillis() + "\n"
1907e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "\n"
1908e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA\n"
1909e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "1\n"
1910e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "MIIBpDCCAQ2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDEw1qd2lsc29uLmxvY2FsMB4XDTEzMDgy"
1911e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "OTA1MDE1OVoXDTEzMDgzMDA1MDE1OVowGDEWMBQGA1UEAxMNandpbHNvbi5sb2NhbDCBnzANBgkqhkiG9w0BAQEF"
1912e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "AAOBjQAwgYkCgYEAlFW+rGo/YikCcRghOyKkJanmVmJSce/p2/jH1QvNIFKizZdh8AKNwojt3ywRWaDULA/RlCUc"
1913e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "ltF3HGNsCyjQI/+Lf40x7JpxXF8oim1E6EtDoYtGWAseelawus3IQ13nmo6nWzfyCA55KhAWf4VipelEy8DjcuFK"
1914e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "v6L0xwXnI0ECAwEAATANBgkqhkiG9w0BAQsFAAOBgQAuluNyPo1HksU3+Mr/PyRQIQS4BI7pRXN8mcejXmqyscdP"
1915e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "7S6J21FBFeRR8/XNjVOp4HT9uSc2hrRtTEHEZCmpyoxixbnM706ikTmC7SN/GgM+SmcoJ1ipJcNcl8N0X6zym4dm"
1916e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "yFfXKHu2PkTo7QFdpOJFvP3lIigcSZXozfmEDg==\n"
1917e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "-1\n";
1918e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String entryBody = "abc";
1919e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String journalBody = ""
1920e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "libcore.io.DiskLruCache\n"
1921e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "1\n"
1922e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "201105\n"
1923e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "2\n"
1924e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "\n"
1925e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "CLEAN " + urlKey + " " + entryMetadata.length() + " " + entryBody.length() + "\n";
1926e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeFile(cache.getDirectory(), urlKey + ".0", entryMetadata);
1927e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeFile(cache.getDirectory(), urlKey + ".1", entryBody);
1928e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeFile(cache.getDirectory(), "journal", journalBody);
192971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    cache = new Cache(cache.getDirectory(), Integer.MAX_VALUE, fileSystem);
1930e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.setCache(cache);
1931e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1932e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = get(url);
1933e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(entryBody, response.body().string());
1934e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("3", response.header("Content-Length"));
1935e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("foo", response.header("etag"));
1936e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1937e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1938e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void evictAll() throws Exception {
1939e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1940e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1941e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1942e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1943e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
1944e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
194571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1946e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1947e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.getCache().evictAll();
1948e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, client.getCache().getSize());
1949e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", get(url).body().string());
1950e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1951e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1952e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void networkInterceptorInvokedForConditionalGet() throws Exception {
1953e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1954e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("ETag: v1")
1955e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1956e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1957e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1958e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1959e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Seed the cache.
196071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1961e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1962e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1963e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    final AtomicReference<String> ifNoneMatch = new AtomicReference<>();
1964e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.networkInterceptors().add(new Interceptor() {
1965e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public Response intercept(Chain chain) throws IOException {
1966e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        ifNoneMatch.compareAndSet(null, chain.request().header("If-None-Match"));
1967e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        return chain.proceed(chain.request());
1968e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
1969e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    });
1970e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1971e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Confirm the value is cached and intercepted.
1972e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1973e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("v1", ifNoneMatch.get());
1974e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1975e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1976e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void networkInterceptorNotInvokedForFullyCached() throws Exception {
1977e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1978e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
1979e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("A"));
1980e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1981e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Seed the cache.
198271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
1983e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1984e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1985e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Confirm the interceptor isn't exercised.
1986e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.networkInterceptors().add(new Interceptor() {
1987e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public Response intercept(Chain chain) throws IOException {
1988e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        throw new AssertionError();
1989e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
1990e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    });
1991e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
1992e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1993e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1994e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void iterateCache() throws Exception {
1995e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Put some responses in the cache.
1996e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1997e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("a"));
199871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl urlA = server.url("/a");
1999e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("a", get(urlA).body().string());
2000e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2001e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2002e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("b"));
200371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl urlB = server.url("/b");
2004e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("b", get(urlB).body().string());
2005e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2006e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2007e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("c"));
200871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl urlC = server.url("/c");
2009e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("c", get(urlC).body().string());
2010e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2011e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Confirm the iterator returns those responses...
2012e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<String> i = cache.urls();
2013e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(i.hasNext());
2014e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(urlA.toString(), i.next());
2015e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(i.hasNext());
2016e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(urlB.toString(), i.next());
2017e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(i.hasNext());
2018e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(urlC.toString(), i.next());
2019e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2020e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // ... and nothing else.
2021e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFalse(i.hasNext());
2022e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
2023e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      i.next();
2024e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
2025e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (NoSuchElementException expected) {
2026e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
2027e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2028e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2029e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void iteratorRemoveFromCache() throws Exception {
2030e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Put a response in the cache.
2031e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2032e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Cache-Control: max-age=60")
2033e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("a"));
203471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/a");
2035e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("a", get(url).body().string());
2036e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2037e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Remove it with iteration.
2038e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<String> i = cache.urls();
2039e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(url.toString(), i.next());
2040e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    i.remove();
2041e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2042e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Confirm that subsequent requests suffer a cache miss.
2043e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2044e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("b"));
2045e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("b", get(url).body().string());
2046e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2047e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2048e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void iteratorRemoveWithoutNextThrows() throws Exception {
2049e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Put a response in the cache.
2050e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2051e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("a"));
205271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/a");
2053e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("a", get(url).body().string());
2054e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2055e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<String> i = cache.urls();
2056e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(i.hasNext());
2057e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
2058e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      i.remove();
2059e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
2060e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalStateException expected) {
2061e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
2062e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2063e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2064e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void iteratorRemoveOncePerCallToNext() throws Exception {
2065e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Put a response in the cache.
2066e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2067e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("a"));
206871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/a");
2069e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("a", get(url).body().string());
2070e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2071e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<String> i = cache.urls();
2072e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(url.toString(), i.next());
2073e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    i.remove();
2074e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2075e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Too many calls to remove().
2076e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
2077e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      i.remove();
2078e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
2079e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalStateException expected) {
2080e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
2081e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2082e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2083e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void elementEvictedBetweenHasNextAndNext() throws Exception {
2084e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Put a response in the cache.
2085e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2086e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("a"));
208771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/a");
2088e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("a", get(url).body().string());
2089e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2090e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // The URL will remain available if hasNext() returned true...
2091e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<String> i = cache.urls();
2092e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(i.hasNext());
2093e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2094e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // ...so even when we evict the element, we still get something back.
2095e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.evictAll();
2096e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(url.toString(), i.next());
2097e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2098e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Remove does nothing. But most importantly, it doesn't throw!
2099e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    i.remove();
2100e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2101e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2102e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void elementEvictedBeforeHasNextIsOmitted() throws Exception {
2103e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Put a response in the cache.
2104e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2105e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("a"));
210671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/a");
2107e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("a", get(url).body().string());
2108e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2109e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<String> i = cache.urls();
2110e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.evictAll();
2111e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2112e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // The URL was evicted before hasNext() made any promises.
2113e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFalse(i.hasNext());
2114e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
2115e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      i.next();
2116e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
2117e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (NoSuchElementException expected) {
2118e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
2119e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2120e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
212171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  /** Test https://github.com/square/okhttp/issues/1712. */
212271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Test public void conditionalMissUpdatesCache() throws Exception {
212371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.enqueue(new MockResponse()
212471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .addHeader("ETag: v1")
212571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .setBody("A"));
212671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.enqueue(new MockResponse()
212771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
212871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.enqueue(new MockResponse()
212971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .addHeader("ETag: v2")
213071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .setBody("B"));
213171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.enqueue(new MockResponse()
213271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
213371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
213471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
213571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(url).body().string());
213671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("A", get(url).body().string());
213771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("B", get(url).body().string());
213871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("B", get(url).body().string());
213971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
214071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals(null, server.takeRequest().getHeader("If-None-Match"));
214171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("v1", server.takeRequest().getHeader("If-None-Match"));
214271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("v1", server.takeRequest().getHeader("If-None-Match"));
214371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("v2", server.takeRequest().getHeader("If-None-Match"));
214471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  }
214571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
214671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  private Response get(HttpUrl url) throws IOException {
2147e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Request request = new Request.Builder()
2148e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .url(url)
2149e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .build();
2150e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return client.newCall(request).execute();
2151e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2152e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2153e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2154e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void writeFile(File directory, String file, String content) throws IOException {
215571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    BufferedSink sink = Okio.buffer(fileSystem.sink(new File(directory, file)));
2156e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    sink.writeUtf8(content);
2157e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    sink.close();
2158e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2159e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2160e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
2161e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * @param delta the offset from the current date to use. Negative
2162e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * values yield dates in the past; positive values yield dates in the
2163e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * future.
2164e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
2165e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private String formatDate(long delta, TimeUnit timeUnit) {
2166e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return formatDate(new Date(System.currentTimeMillis() + timeUnit.toMillis(delta)));
2167e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2168e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2169e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private String formatDate(Date date) {
2170e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DateFormat rfc1123 = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
2171e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    rfc1123.setTimeZone(TimeZone.getTimeZone("GMT"));
2172e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return rfc1123.format(date);
2173e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2174e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2175e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void assertNotCached(MockResponse response) throws Exception {
2176e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(response.setBody("A"));
2177e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2178e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("B"));
2179e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
218071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
2181e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
2182e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", get(url).body().string());
2183e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2184e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2185e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** @return the request with the conditional get headers. */
2186e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private RecordedRequest assertConditionallyCached(MockResponse response) throws Exception {
2187e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // scenario 1: condition succeeds
2188e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(response.setBody("A").setStatus("HTTP/1.1 200 A-OK"));
2189e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2190e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
2191e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2192e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // scenario 2: condition fails
2193e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(response.setBody("B")
2194e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setStatus("HTTP/1.1 200 B-OK"));
2195e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2196e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setStatus("HTTP/1.1 200 C-OK")
2197e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody("C"));
2198e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
219971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl valid = server.url("/valid");
2200e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response1 = get(valid);
2201e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response1.body().string());
2202e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(HttpURLConnection.HTTP_OK, response1.code());
2203e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A-OK", response1.message());
2204e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response2 = get(valid);
2205e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", response2.body().string());
2206e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(HttpURLConnection.HTTP_OK, response2.code());
2207e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A-OK", response2.message());
2208e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
220971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl invalid = server.url("/invalid");
2210e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response3 = get(invalid);
2211e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B", response3.body().string());
2212e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(HttpURLConnection.HTTP_OK, response3.code());
2213e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("B-OK", response3.message());
2214e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response4 = get(invalid);
2215e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("C", response4.body().string());
2216e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(HttpURLConnection.HTTP_OK, response4.code());
2217e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("C-OK", response4.message());
2218e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2219e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.takeRequest(); // regular get
2220e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return server.takeRequest(); // conditional get
2221e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2222e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2223e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void assertFullyCached(MockResponse response) throws Exception {
2224e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(response.setBody("A"));
2225e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(response.setBody("B"));
2226e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
222771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpUrl url = server.url("/");
2228e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
2229e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("A", get(url).body().string());
2230e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2231e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2232e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
2233e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Shortens the body of {@code response} but not the corresponding headers.
2234e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Only useful to test how clients respond to the premature conclusion of
2235e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * the HTTP body.
2236e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
2237e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private MockResponse truncateViolently(MockResponse response, int numBytesToKeep) {
2238e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    response.setSocketPolicy(DISCONNECT_AT_END);
2239e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Headers headers = response.getHeaders();
2240e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer truncatedBody = new Buffer();
2241e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    truncatedBody.write(response.getBody(), numBytesToKeep);
2242e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    response.setBody(truncatedBody);
2243e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    response.setHeaders(headers);
2244e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return response;
2245e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2246e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2247e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  enum TransferKind {
2248e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    CHUNKED() {
2249e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override void setBody(MockResponse response, Buffer content, int chunkSize)
2250e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          throws IOException {
2251e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        response.setChunkedBody(content, chunkSize);
2252e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
2253e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    },
2254e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    FIXED_LENGTH() {
2255e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override void setBody(MockResponse response, Buffer content, int chunkSize) {
2256e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        response.setBody(content);
2257e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
2258e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    },
2259e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    END_OF_STREAM() {
2260e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override void setBody(MockResponse response, Buffer content, int chunkSize) {
2261e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        response.setBody(content);
2262e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        response.setSocketPolicy(DISCONNECT_AT_END);
2263e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        response.removeHeader("Content-Length");
2264e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
2265e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    };
2266e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2267e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    abstract void setBody(MockResponse response, Buffer content, int chunkSize) throws IOException;
2268e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2269e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    void setBody(MockResponse response, String content, int chunkSize) throws IOException {
2270e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      setBody(response, new Buffer().writeUtf8(content), chunkSize);
2271e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
2272e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2273e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2274e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** Returns a gzipped copy of {@code bytes}. */
2275e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  public Buffer gzip(String data) throws IOException {
2276e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer result = new Buffer();
2277e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    BufferedSink sink = Okio.buffer(new GzipSink(result));
2278e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    sink.writeUtf8(data);
2279e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    sink.close();
2280e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return result;
2281e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2282e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller}
2283