17899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath/*
27899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * Copyright (C) 2011 The Android Open Source Project
37899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *
47899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * Licensed under the Apache License, Version 2.0 (the "License");
57899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * you may not use this file except in compliance with the License.
67899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * You may obtain a copy of the License at
77899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *
87899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *      http://www.apache.org/licenses/LICENSE-2.0
97899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *
107899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * Unless required by applicable law or agreed to in writing, software
117899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS,
127899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * See the License for the specific language governing permissions and
147899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * limitations under the License.
157899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath */
167899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
172231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonpackage com.squareup.okhttp.internal.http;
187899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
19c6bd683320121544811f481709b3fdbcbe9b3866Neil Fullerimport com.squareup.okhttp.ConnectionPool;
20faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport com.squareup.okhttp.HttpResponseCache;
212231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.OkHttpClient;
223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.OkResponseCache;
233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Request;
243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Response;
25faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport com.squareup.okhttp.ResponseSource;
262231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.internal.SslContextBuilder;
27166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport com.squareup.okhttp.internal.Util;
28166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport com.squareup.okhttp.mockwebserver.MockResponse;
29166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport com.squareup.okhttp.mockwebserver.MockWebServer;
30166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport com.squareup.okhttp.mockwebserver.RecordedRequest;
317899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.BufferedReader;
327899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.ByteArrayOutputStream;
337899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.File;
347899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.FileNotFoundException;
35166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport java.io.FileOutputStream;
367899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.IOException;
377899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.InputStream;
387899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.InputStreamReader;
397899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.OutputStream;
407899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.CacheRequest;
417899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.CookieHandler;
427899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.CookieManager;
437899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.HttpCookie;
447899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.HttpURLConnection;
457899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.ResponseCache;
467899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.URISyntaxException;
477899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.URL;
487899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.URLConnection;
492231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.security.Principal;
502231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.security.cert.Certificate;
517899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.text.DateFormat;
527899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.text.SimpleDateFormat;
537899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.ArrayList;
547899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.Arrays;
557899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.Date;
564944713f5c5b141966ac82973d6a31a634e8e01eNeil Fullerimport java.util.HashMap;
577899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.Iterator;
587899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.List;
597899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.Locale;
607899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.TimeZone;
617899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.UUID;
627899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.concurrent.TimeUnit;
633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.concurrent.atomic.AtomicBoolean;
647899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.concurrent.atomic.AtomicInteger;
657899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.concurrent.atomic.AtomicReference;
667899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.zip.GZIPOutputStream;
672231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport javax.net.ssl.HostnameVerifier;
682231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport javax.net.ssl.HttpsURLConnection;
692231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport javax.net.ssl.SSLContext;
702231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport javax.net.ssl.SSLSession;
712231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport org.junit.After;
7254cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport org.junit.Before;
7354cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport org.junit.Test;
7454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
75166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport static com.squareup.okhttp.mockwebserver.SocketPolicy.DISCONNECT_AT_END;
762231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.assertEquals;
772231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.assertFalse;
782231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.assertNotNull;
792231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.assertNull;
803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.assertSame;
812231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.assertTrue;
822231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.fail;
837899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller/**
853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Android's HttpResponseCacheTest. This tests both the {@link HttpResponseCache} implementation and
863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * the behavior of {@link com.squareup.okhttp.OkResponseCache} classes generally.
873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */
882231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonpublic final class HttpResponseCacheTest {
8954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static final HostnameVerifier NULL_HOSTNAME_VERIFIER = new HostnameVerifier() {
9054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public boolean verify(String s, SSLSession sslSession) {
9154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return true;
9254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
9354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  };
943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private static final SSLContext sslContext = SslContextBuilder.localhost();
963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
9754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private final OkHttpClient client = new OkHttpClient();
9854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private MockWebServer server = new MockWebServer();
9974623587bf099735cf16ec2298005f12fd3fcb07jwilson  private MockWebServer server2 = new MockWebServer();
10054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private HttpResponseCache cache;
10154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private final CookieManager cookieManager = new CookieManager();
10254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
10354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Before public void setUp() throws Exception {
10454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String tmp = System.getProperty("java.io.tmpdir");
10554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    File cacheDir = new File(tmp, "HttpCache-" + UUID.randomUUID());
10654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    cache = new HttpResponseCache(cacheDir, Integer.MAX_VALUE);
10754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    ResponseCache.setDefault(cache);
10854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    CookieHandler.setDefault(cookieManager);
109166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    server.setNpnEnabled(false);
11054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
11154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
11254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @After public void tearDown() throws Exception {
11354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.shutdown();
11474623587bf099735cf16ec2298005f12fd3fcb07jwilson    server2.shutdown();
11554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    ResponseCache.setDefault(null);
116faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    cache.delete();
11754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    CookieHandler.setDefault(null);
11854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
11954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
12054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private HttpURLConnection openConnection(URL url) {
12154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return client.open(url);
12254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
12354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void responseCacheAccessWithOkHttpMember() throws IOException {
1253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    ResponseCache.setDefault(null);
1263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.setResponseCache(cache);
1273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertSame(cache, client.getOkResponseCache());
1283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertNull(client.getResponseCache());
1293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void responseCacheAccessWithGlobalDefault() throws IOException {
1323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    ResponseCache.setDefault(cache);
1333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.setResponseCache(null);
1343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertNull(client.getOkResponseCache());
1353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertNull(client.getResponseCache());
1363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
13854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
13954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Test that response caching is consistent with the RI and the spec.
14054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4
14154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
14254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCachingByResponseCode() throws Exception {
14354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Test each documented HTTP/1.1 code, plus the first unused value in each range.
14454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
14554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
14654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // We can't test 100 because it's not really a response.
14754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // assertCached(false, 100);
14854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 101);
14954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 102);
15054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(true, 200);
15154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 201);
15254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 202);
15354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(true, 203);
15454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 204);
15554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 205);
15654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 206); // we don't cache partial responses
15754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 207);
15854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(true, 300);
15954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(true, 301);
16054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 302; i <= 308; ++i) {
16154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertCached(false, i);
16254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
16354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 400; i <= 406; ++i) {
16454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertCached(false, i);
16554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
16654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // (See test_responseCaching_407.)
16754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 408);
16854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 409);
16954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // (See test_responseCaching_410.)
17054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 411; i <= 418; ++i) {
17154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertCached(false, i);
17254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
17354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 500; i <= 506; ++i) {
17454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertCached(false, i);
17554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
17654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
17754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
17854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
17954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Response code 407 should only come from proxy servers. Android's client
18054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * throws if it is sent by an origin server.
18154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
18254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void originServerSends407() throws Exception {
18354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(407));
18454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
18554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
18654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
18754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection conn = openConnection(url);
18854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
18954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      conn.getResponseCode();
19054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
19154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException expected) {
19254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
19354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
19454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
19554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCaching_410() throws Exception {
19654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // the HTTP spec permits caching 410s, but the RI doesn't.
19754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(true, 410);
19854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
19954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
20054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void assertCached(boolean shouldPut, int responseCode) throws Exception {
20154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server = new MockWebServer();
20254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response =
20354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
20454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
20554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setResponseCode(responseCode)
20654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setBody("ABCDE")
20754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("WWW-Authenticate: challenge");
20854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (responseCode == HttpURLConnection.HTTP_PROXY_AUTH) {
20954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      response.addHeader("Proxy-Authenticate: Basic realm=\"protected area\"");
21054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
21154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      response.addHeader("WWW-Authenticate: Basic realm=\"protected area\"");
21254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
21354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
21454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
21554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
21654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
21754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection conn = openConnection(url);
21854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(responseCode, conn.getResponseCode());
21954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
22054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // exhaust the content stream
22154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    readAscii(conn);
22254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Response cached = cache.get(new Request.Builder().url(url).build());
22454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (shouldPut) {
22554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertNotNull(Integer.toString(responseCode), cached);
2263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      cached.body().close();
22754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
22854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertNull(Integer.toString(responseCode), cached);
22954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
23054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.shutdown(); // tearDown() isn't sufficient; this test starts multiple servers
23154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
23254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
23354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
23454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Test that we can interrogate the response when the cache is being
23554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * populated. http://code.google.com/p/android/issues/detail?id=7787
23654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
23754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCacheCallbackApis() throws Exception {
23854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    final String body = "ABCDE";
23954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    final AtomicInteger cacheCount = new AtomicInteger();
24054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse()
2423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .setStatus("HTTP/1.1 200 Fantastic")
2433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("Content-Type: text/plain")
2443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("fgh: ijk")
2453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .setBody(body));
2463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.play();
2473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.setOkResponseCache(new AbstractOkResponseCache() {
2493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      @Override public CacheRequest put(Response response) throws IOException {
2503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        assertEquals(server.getUrl("/"), response.request().url());
2513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        assertEquals(200, response.code());
2523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        assertNull(response.body());
2533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        assertEquals("5", response.header("Content-Length"));
2543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        assertEquals("text/plain", response.header("Content-Type"));
2553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        assertEquals("ijk", response.header("fgh"));
25654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        cacheCount.incrementAndGet();
25754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return null;
25854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
25954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    });
26054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
26154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
26254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(url);
26354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(body, readAscii(connection));
26454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cacheCount.get());
26554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
26654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** Don't explode if the cache returns a null body. http://b/3373699 */
2683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void responseCacheReturnsNullOutputStream() throws Exception {
2693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    final AtomicBoolean aborted = new AtomicBoolean();
2703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.setOkResponseCache(new AbstractOkResponseCache() {
2713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      @Override public CacheRequest put(Response response) throws IOException {
2723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return new CacheRequest() {
2733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          @Override public void abort() {
2743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            aborted.set(true);
2753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          }
2763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          @Override public OutputStream getBody() throws IOException {
2783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            return null;
2793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          }
2803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        };
2813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
2823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    });
2833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse().setBody("abcdef"));
2853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.play();
2863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpURLConnection connection = client.open(server.getUrl("/"));
2883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("abc", readAscii(connection, 3));
2893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.getInputStream().close();
2903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertFalse(aborted.get()); // The best behavior is ambiguous, but RI 6 doesn't abort here
2913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
2923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
29354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCachingAndInputStreamSkipWithFixedLength() throws IOException {
29454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testResponseCaching(TransferKind.FIXED_LENGTH);
29554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
29654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
29754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCachingAndInputStreamSkipWithChunkedEncoding() throws IOException {
29854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testResponseCaching(TransferKind.CHUNKED);
29954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
30054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
30154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCachingAndInputStreamSkipWithNoLengthHeaders() throws IOException {
30254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testResponseCaching(TransferKind.END_OF_STREAM);
30354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
30454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
30554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
30654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * HttpURLConnection.getInputStream().skip(long) causes ResponseCache corruption
30754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * http://code.google.com/p/android/issues/detail?id=8175
30854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
30954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testResponseCaching(TransferKind transferKind) throws IOException {
31054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response =
31154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
31254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
31354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setStatus("HTTP/1.1 200 Fantastic");
31454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transferKind.setBody(response, "I love puppies but hate spiders", 1);
31554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
31654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
31754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
31854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Make sure that calling skip() doesn't omit bytes from the cache.
31954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection urlConnection = openConnection(server.getUrl("/"));
32054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream in = urlConnection.getInputStream();
32154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("I love ", readAscii(urlConnection, "I love ".length()));
32254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    reliableSkip(in, "puppies but hate ".length());
32354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("spiders", readAscii(urlConnection, "spiders".length()));
32454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(-1, in.read());
32554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    in.close();
32654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getWriteSuccessCount());
32754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, cache.getWriteAbortCount());
32854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
32954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    urlConnection = openConnection(server.getUrl("/")); // cached!
33054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    in = urlConnection.getInputStream();
33154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("I love puppies but hate spiders",
33254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        readAscii(urlConnection, "I love puppies but hate spiders".length()));
33354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(200, urlConnection.getResponseCode());
33454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("Fantastic", urlConnection.getResponseMessage());
33554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
33654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(-1, in.read());
33754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    in.close();
33854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getWriteSuccessCount());
33954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, cache.getWriteAbortCount());
34054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, cache.getRequestCount());
34154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getHitCount());
34254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
34354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
34454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void secureResponseCaching() throws IOException {
34554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.useHttps(sslContext.getSocketFactory(), false);
34654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
34754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
34854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("ABC"));
34954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
35054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
3513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpsURLConnection c1 = (HttpsURLConnection) client.open(server.getUrl("/"));
3523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    c1.setSSLSocketFactory(sslContext.getSocketFactory());
3533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    c1.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
3543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("ABC", readAscii(c1));
35554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
35654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // OpenJDK 6 fails on this line, complaining that the connection isn't open yet
3573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String suite = c1.getCipherSuite();
3583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    List<Certificate> localCerts = toListOrNull(c1.getLocalCertificates());
3593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    List<Certificate> serverCerts = toListOrNull(c1.getServerCertificates());
3603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Principal peerPrincipal = c1.getPeerPrincipal();
3613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Principal localPrincipal = c1.getLocalPrincipal();
3623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
3633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpsURLConnection c2 = (HttpsURLConnection) client.open(server.getUrl("/")); // cached!
3643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    c2.setSSLSocketFactory(sslContext.getSocketFactory());
3653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    c2.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
3663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("ABC", readAscii(c2));
36754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
36854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, cache.getRequestCount());
36954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getNetworkCount());
37054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getHitCount());
37154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
3723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(suite, c2.getCipherSuite());
3733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(localCerts, toListOrNull(c2.getLocalCertificates()));
3743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(serverCerts, toListOrNull(c2.getServerCertificates()));
3753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(peerPrincipal, c2.getPeerPrincipal());
3763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(localPrincipal, c2.getLocalPrincipal());
37754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
37854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
37954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCachingAndRedirects() throws Exception {
38054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
38154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
38254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
38354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Location: /foo"));
38454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
38554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
38654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("ABC"));
38754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("DEF"));
38854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
38954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
39054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(server.getUrl("/"));
39154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(connection));
39254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
39354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection = openConnection(server.getUrl("/")); // cached!
39454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(connection));
39554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
39654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(4, cache.getRequestCount()); // 2 requests + 2 redirects
39754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, cache.getNetworkCount());
39854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, cache.getHitCount());
39954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
40054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
40154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void redirectToCachedResult() throws Exception {
40254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60").setBody("ABC"));
40354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
40454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Location: /foo"));
40554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("DEF"));
40654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
40754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
40854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(openConnection(server.getUrl("/foo"))));
40954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request1 = server.takeRequest();
41054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET /foo HTTP/1.1", request1.getRequestLine());
41154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, request1.getSequenceNumber());
41254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
41354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(openConnection(server.getUrl("/bar"))));
41454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request2 = server.takeRequest();
41554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET /bar HTTP/1.1", request2.getRequestLine());
41654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, request2.getSequenceNumber());
41754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
41854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // an unrelated request should reuse the pooled connection
41954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("DEF", readAscii(openConnection(server.getUrl("/baz"))));
42054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request3 = server.takeRequest();
42154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET /baz HTTP/1.1", request3.getRequestLine());
42254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, request3.getSequenceNumber());
42354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
42454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
42554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void secureResponseCachingAndRedirects() throws IOException {
42654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.useHttps(sslContext.getSocketFactory(), false);
42754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
42854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
42954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
43054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Location: /foo"));
43154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
43254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
43354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("ABC"));
43454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("DEF"));
43554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
43654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
437166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    client.setSslSocketFactory(sslContext.getSocketFactory());
438166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    client.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
439166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
44054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpsURLConnection connection1 = (HttpsURLConnection) client.open(server.getUrl("/"));
44154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(connection1));
4423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertNotNull(connection1.getCipherSuite());
44354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
44454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Cached!
44554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpsURLConnection connection2 = (HttpsURLConnection) client.open(server.getUrl("/"));
44654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(connection2));
4473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertNotNull(connection2.getCipherSuite());
44854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
44954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(4, cache.getRequestCount()); // 2 direct + 2 redirect = 4
45054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, cache.getHitCount());
4513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(connection1.getCipherSuite(), connection2.getCipherSuite());
45274623587bf099735cf16ec2298005f12fd3fcb07jwilson  }
45374623587bf099735cf16ec2298005f12fd3fcb07jwilson
45474623587bf099735cf16ec2298005f12fd3fcb07jwilson  /**
45574623587bf099735cf16ec2298005f12fd3fcb07jwilson   * We've had bugs where caching and cross-protocol redirects yield class
45674623587bf099735cf16ec2298005f12fd3fcb07jwilson   * cast exceptions internal to the cache because we incorrectly assumed that
45774623587bf099735cf16ec2298005f12fd3fcb07jwilson   * HttpsURLConnection was always HTTPS and HttpURLConnection was always HTTP;
45874623587bf099735cf16ec2298005f12fd3fcb07jwilson   * in practice redirects mean that each can do either.
45974623587bf099735cf16ec2298005f12fd3fcb07jwilson   *
46074623587bf099735cf16ec2298005f12fd3fcb07jwilson   * https://github.com/square/okhttp/issues/214
46174623587bf099735cf16ec2298005f12fd3fcb07jwilson   */
46274623587bf099735cf16ec2298005f12fd3fcb07jwilson  @Test public void secureResponseCachingAndProtocolRedirects() throws IOException {
46374623587bf099735cf16ec2298005f12fd3fcb07jwilson    server2.useHttps(sslContext.getSocketFactory(), false);
46474623587bf099735cf16ec2298005f12fd3fcb07jwilson    server2.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
46574623587bf099735cf16ec2298005f12fd3fcb07jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
46674623587bf099735cf16ec2298005f12fd3fcb07jwilson        .setBody("ABC"));
46774623587bf099735cf16ec2298005f12fd3fcb07jwilson    server2.enqueue(new MockResponse().setBody("DEF"));
46874623587bf099735cf16ec2298005f12fd3fcb07jwilson    server2.play();
46974623587bf099735cf16ec2298005f12fd3fcb07jwilson
47074623587bf099735cf16ec2298005f12fd3fcb07jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
47174623587bf099735cf16ec2298005f12fd3fcb07jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
47274623587bf099735cf16ec2298005f12fd3fcb07jwilson        .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
47374623587bf099735cf16ec2298005f12fd3fcb07jwilson        .addHeader("Location: " + server2.getUrl("/")));
47474623587bf099735cf16ec2298005f12fd3fcb07jwilson    server.play();
47574623587bf099735cf16ec2298005f12fd3fcb07jwilson
47674623587bf099735cf16ec2298005f12fd3fcb07jwilson    client.setSslSocketFactory(sslContext.getSocketFactory());
47774623587bf099735cf16ec2298005f12fd3fcb07jwilson    client.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
47874623587bf099735cf16ec2298005f12fd3fcb07jwilson
47974623587bf099735cf16ec2298005f12fd3fcb07jwilson    HttpURLConnection connection1 = client.open(server.getUrl("/"));
48074623587bf099735cf16ec2298005f12fd3fcb07jwilson    assertEquals("ABC", readAscii(connection1));
48174623587bf099735cf16ec2298005f12fd3fcb07jwilson
48274623587bf099735cf16ec2298005f12fd3fcb07jwilson    // Cached!
48374623587bf099735cf16ec2298005f12fd3fcb07jwilson    HttpURLConnection connection2 = client.open(server.getUrl("/"));
48474623587bf099735cf16ec2298005f12fd3fcb07jwilson    assertEquals("ABC", readAscii(connection2));
48574623587bf099735cf16ec2298005f12fd3fcb07jwilson
48674623587bf099735cf16ec2298005f12fd3fcb07jwilson    assertEquals(4, cache.getRequestCount()); // 2 direct + 2 redirect = 4
48774623587bf099735cf16ec2298005f12fd3fcb07jwilson    assertEquals(2, cache.getHitCount());
48854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
48954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
49054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCacheRequestHeaders() throws IOException, URISyntaxException {
49154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("ABC"));
49254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
49354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
4943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    final AtomicReference<Request> requestRef = new AtomicReference<Request>();
4953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.setOkResponseCache(new AbstractOkResponseCache() {
4963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      @Override public Response get(Request request) throws IOException {
4973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        requestRef.set(request);
49854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return null;
49954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
50054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    });
50154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
50254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
50354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection urlConnection = openConnection(url);
50454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    urlConnection.addRequestProperty("A", "android");
50554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    readAscii(urlConnection);
5063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(Arrays.asList("android"), requestRef.get().headers("A"));
50754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
50854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
50954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void serverDisconnectsPrematurelyWithContentLengthHeader() throws IOException {
51054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testServerPrematureDisconnect(TransferKind.FIXED_LENGTH);
51154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
51254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
51354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void serverDisconnectsPrematurelyWithChunkedEncoding() throws IOException {
51454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testServerPrematureDisconnect(TransferKind.CHUNKED);
51554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
51654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
51754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void serverDisconnectsPrematurelyWithNoLengthHeaders() throws IOException {
51854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Intentionally empty. This case doesn't make sense because there's no
51954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // such thing as a premature disconnect when the disconnect itself
52054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // indicates the end of the data stream.
52154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
52254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
52354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testServerPrematureDisconnect(TransferKind transferKind) throws IOException {
52454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response = new MockResponse();
52554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transferKind.setBody(response, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 16);
52654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(truncateViolently(response, 16));
52754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("Request #2"));
52854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
52954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
53054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    BufferedReader reader = new BufferedReader(
53154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new InputStreamReader(openConnection(server.getUrl("/")).getInputStream()));
53254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABCDE", reader.readLine());
53354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
53454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      reader.readLine();
53554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail("This implementation silently ignored a truncated HTTP body.");
53654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException expected) {
53754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } finally {
53854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      reader.close();
53954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
54054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
54154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getWriteAbortCount());
54254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, cache.getWriteSuccessCount());
54354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
54454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("Request #2", readAscii(connection));
54554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getWriteAbortCount());
54654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getWriteSuccessCount());
54754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
54854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
54954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientPrematureDisconnectWithContentLengthHeader() throws IOException {
55054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testClientPrematureDisconnect(TransferKind.FIXED_LENGTH);
55154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
55254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
55354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientPrematureDisconnectWithChunkedEncoding() throws IOException {
55454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testClientPrematureDisconnect(TransferKind.CHUNKED);
55554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
55654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
55754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientPrematureDisconnectWithNoLengthHeaders() throws IOException {
55854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testClientPrematureDisconnect(TransferKind.END_OF_STREAM);
55954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
56054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
56154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testClientPrematureDisconnect(TransferKind transferKind) throws IOException {
56254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Setting a low transfer speed ensures that stream discarding will time out.
5633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    MockResponse response = new MockResponse().throttleBody(6, 1, TimeUnit.SECONDS);
56454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transferKind.setBody(response, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 1024);
56554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
56654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("Request #2"));
56754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
56854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
56954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
57054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream in = connection.getInputStream();
57154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABCDE", readAscii(connection, 5));
57254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    in.close();
57354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
57454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      in.read();
57554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail("Expected an IOException because the stream is closed.");
57654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException expected) {
57754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
57854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
57954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getWriteAbortCount());
58054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, cache.getWriteSuccessCount());
58154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection = openConnection(server.getUrl("/"));
58254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("Request #2", readAscii(connection));
58354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getWriteAbortCount());
58454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getWriteSuccessCount());
58554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
58654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
58754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void defaultExpirationDateFullyCachedForLessThan24Hours() throws Exception {
58854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //      last modified: 105 seconds ago
58954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //             served:   5 seconds ago
59054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //   default lifetime: (105 - 5) / 10 = 10 seconds
59154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //            expires:  10 seconds from served date = 5 seconds from now
59254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
59354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-105, TimeUnit.SECONDS))
59454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Date: " + formatDate(-5, TimeUnit.SECONDS))
59554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setBody("A"));
59654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
59754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
59854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
59954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
60054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(url);
60154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
60254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNull(connection.getHeaderField("Warning"));
60354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
60454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
60554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void defaultExpirationDateConditionallyCached() throws Exception {
60654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //      last modified: 115 seconds ago
60754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //             served:  15 seconds ago
60854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //   default lifetime: (115 - 15) / 10 = 10 seconds
60954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //            expires:  10 seconds from served date = 5 seconds ago
61054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-115, TimeUnit.SECONDS);
61154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest = assertConditionallyCached(
61254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + lastModifiedDate)
61354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Date: " + formatDate(-15, TimeUnit.SECONDS)));
61454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = conditionalRequest.getHeaders();
61554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
61654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
61754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
61854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void defaultExpirationDateFullyCachedForMoreThan24Hours() throws Exception {
61954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //      last modified: 105 days ago
62054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //             served:   5 days ago
62154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //   default lifetime: (105 - 5) / 10 = 10 days
62254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //            expires:  10 days from served date = 5 days from now
62354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-105, TimeUnit.DAYS))
62454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(-5, TimeUnit.DAYS))
62554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
62654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
62754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
62854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
62954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
63054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
63154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("113 HttpURLConnection \"Heuristic expiration\"",
63254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        connection.getHeaderField("Warning"));
63354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
63454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
63554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void noDefaultExpirationForUrlsWithQueryString() throws Exception {
63654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
63754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-105, TimeUnit.SECONDS))
63854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Date: " + formatDate(-5, TimeUnit.SECONDS))
63954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setBody("A"));
64054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
64154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
64254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
64354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/?foo=bar");
64454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
64554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(url)));
64654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
64754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
64854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void expirationDateInThePastWithLastModifiedHeader() throws Exception {
64954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
65054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest = assertConditionallyCached(
65154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + lastModifiedDate)
65254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
65354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = conditionalRequest.getHeaders();
65454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
65554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
65654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
65754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void expirationDateInThePastWithNoLastModifiedHeader() throws Exception {
65854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
65954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
66054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
66154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void expirationDateInTheFuture() throws Exception {
66254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(new MockResponse().addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
66354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
66454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
66554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgePreferredWithMaxAgeAndExpires() throws Exception {
66654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(new MockResponse().addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
66754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS))
66854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=60"));
66954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
67054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
67154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgeInThePastWithDateAndLastModifiedHeaders() throws Exception {
67254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
67354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest = assertConditionallyCached(
67454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Date: " + formatDate(-120, TimeUnit.SECONDS))
67554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Last-Modified: " + lastModifiedDate)
67654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: max-age=60"));
67754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = conditionalRequest.getHeaders();
67854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
67954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
68054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
68154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgeInThePastWithDateHeaderButNoLastModifiedHeader() throws Exception {
68254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Chrome interprets max-age relative to the local clock. Both our cache
68354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // and Firefox both use the earlier of the local and server's clock.
68454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Date: " + formatDate(-120, TimeUnit.SECONDS))
68554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=60"));
68654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
68754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
68854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgeInTheFutureWithDateHeader() throws Exception {
68954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(new MockResponse().addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
69054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=60"));
69154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
69254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
69354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgeInTheFutureWithNoDateHeader() throws Exception {
69454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(new MockResponse().addHeader("Cache-Control: max-age=60"));
69554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
69654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
69754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgeWithLastModifiedButNoServedDate() throws Exception {
69854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(
69954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
70054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: max-age=60"));
70154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
70254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
70354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgeInTheFutureWithDateAndLastModifiedHeaders() throws Exception {
70454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(
70554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
70654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
70754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: max-age=60"));
70854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
70954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
71054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgePreferredOverLowerSharedMaxAge() throws Exception {
71154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(new MockResponse().addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
71254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: s-maxage=60")
71354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=180"));
71454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
71554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
71654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgePreferredOverHigherMaxAge() throws Exception {
71754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
71854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: s-maxage=180")
71954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=60"));
72054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
72154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
72254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMethodOptionsIsNotCached() throws Exception {
72354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRequestMethod("OPTIONS", false);
72454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
72554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
72654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMethodGetIsCached() throws Exception {
72754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRequestMethod("GET", true);
72854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
72954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
73054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMethodHeadIsNotCached() throws Exception {
73154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // We could support this but choose not to for implementation simplicity
73254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRequestMethod("HEAD", false);
73354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
73454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
73554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMethodPostIsNotCached() throws Exception {
73654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // We could support this but choose not to for implementation simplicity
73754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRequestMethod("POST", false);
73854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
73954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
74054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMethodPutIsNotCached() throws Exception {
74154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRequestMethod("PUT", false);
74254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
74354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
74454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMethodDeleteIsNotCached() throws Exception {
74554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRequestMethod("DELETE", false);
74654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
74754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
74854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMethodTraceIsNotCached() throws Exception {
74954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRequestMethod("TRACE", false);
75054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
75154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
75254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testRequestMethod(String requestMethod, boolean expectCached) throws Exception {
75354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // 1. seed the cache (potentially)
75454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // 2. expect a cache hit or miss
75554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
75654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("X-Response-ID: 1"));
75754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("X-Response-ID: 2"));
75854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
75954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
76054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
76154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
76254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection request1 = openConnection(url);
76354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    request1.setRequestMethod(requestMethod);
76454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    addRequestBodyIfNecessary(requestMethod, request1);
76554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("1", request1.getHeaderField("X-Response-ID"));
76654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
76754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection request2 = openConnection(url);
76854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (expectCached) {
7693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      assertEquals("1", request2.getHeaderField("X-Response-ID"));
77054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
77154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertEquals("2", request2.getHeaderField("X-Response-ID"));
77254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
77354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
77454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
77554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void postInvalidatesCache() throws Exception {
77654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testMethodInvalidates("POST");
77754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
77854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
77954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void putInvalidatesCache() throws Exception {
78054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testMethodInvalidates("PUT");
78154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
78254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
78354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void deleteMethodInvalidatesCache() throws Exception {
78454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testMethodInvalidates("DELETE");
78554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
78654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
78754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testMethodInvalidates(String requestMethod) throws Exception {
78854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // 1. seed the cache
78954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // 2. invalidate it
79054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // 3. expect a cache miss
79154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
79254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().setBody("A").addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
79354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
79454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("C"));
79554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
79654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
79754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
79854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
79954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
80054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
80154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection invalidate = openConnection(url);
80254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    invalidate.setRequestMethod(requestMethod);
80354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    addRequestBodyIfNecessary(requestMethod, invalidate);
80454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(invalidate));
80554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
80654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("C", readAscii(openConnection(url)));
80754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
80854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
809a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  @Test public void postInvalidatesCacheWithUncacheableResponse() throws Exception {
810a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    // 1. seed the cache
811a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    // 2. invalidate it with uncacheable response
812a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    // 3. expect a cache miss
813a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    server.enqueue(
814a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        new MockResponse().setBody("A").addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
815a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    server.enqueue(new MockResponse().setBody("B").setResponseCode(500));
816a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    server.enqueue(new MockResponse().setBody("C"));
817a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    server.play();
818a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
819a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    URL url = server.getUrl("/");
820a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
821a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    assertEquals("A", readAscii(openConnection(url)));
822a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
823a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    HttpURLConnection invalidate = openConnection(url);
824a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    invalidate.setRequestMethod("POST");
825a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    addRequestBodyIfNecessary("POST", invalidate);
826a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    assertEquals("B", readAscii(invalidate));
827a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
828a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    assertEquals("C", readAscii(openConnection(url)));
829a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  }
830a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
83154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void etag() throws Exception {
83254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest =
83354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        assertConditionallyCached(new MockResponse().addHeader("ETag: v1"));
83454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(conditionalRequest.getHeaders().contains("If-None-Match: v1"));
83554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
83654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
83754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void etagAndExpirationDateInThePast() throws Exception {
83854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
83954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest = assertConditionallyCached(
84054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("ETag: v1")
84154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Last-Modified: " + lastModifiedDate)
84254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
84354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = conditionalRequest.getHeaders();
84454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-None-Match: v1"));
84554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
84654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
84754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
84854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void etagAndExpirationDateInTheFuture() throws Exception {
84954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(new MockResponse().addHeader("ETag: v1")
85054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
85154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
85254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
85354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
85454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cacheControlNoCache() throws Exception {
85554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Cache-Control: no-cache"));
85654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
85754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
85854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cacheControlNoCacheAndExpirationDateInTheFuture() throws Exception {
85954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
86054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest = assertConditionallyCached(
86154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + lastModifiedDate)
86254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
86354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: no-cache"));
86454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = conditionalRequest.getHeaders();
86554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
86654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
86754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
86854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void pragmaNoCache() throws Exception {
86954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Pragma: no-cache"));
87054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
87154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
87254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void pragmaNoCacheAndExpirationDateInTheFuture() throws Exception {
87354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
87454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest = assertConditionallyCached(
87554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + lastModifiedDate)
87654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
87754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Pragma: no-cache"));
87854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = conditionalRequest.getHeaders();
87954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
88054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
88154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
88254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cacheControlNoStore() throws Exception {
88354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Cache-Control: no-store"));
88454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
88554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
88654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cacheControlNoStoreAndExpirationDateInTheFuture() throws Exception {
88754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
88854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
88954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: no-store"));
89054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
89154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
89254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void partialRangeResponsesDoNotCorruptCache() throws Exception {
89354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // 1. request a range
89454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // 2. request a full document, expecting a cache miss
89554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("AA")
89654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_PARTIAL)
89754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
89854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Content-Range: bytes 1000-1001/2000"));
89954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("BB"));
90054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
90154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
90254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
90354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
90454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection range = openConnection(url);
90554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    range.addRequestProperty("Range", "bytes=1000-1001");
90654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("AA", readAscii(range));
90754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
90854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("BB", readAscii(openConnection(url)));
90954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
91054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
91154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void serverReturnsDocumentOlderThanCache() throws Exception {
91254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
91354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
91454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
91554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B")
91654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-4, TimeUnit.HOURS)));
91754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
91854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
91954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
92054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
92154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
92254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
92354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
92454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
92554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void nonIdentityEncodingAndConditionalCache() throws Exception {
92654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNonIdentityEncodingCached(
92754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
92854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
92954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
93054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
93154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void nonIdentityEncodingAndFullCache() throws Exception {
93254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNonIdentityEncodingCached(
93354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
93454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
93554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
93654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
93754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void assertNonIdentityEncodingCached(MockResponse response) throws Exception {
93854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
93954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        response.setBody(gzip("ABCABCABC".getBytes("UTF-8"))).addHeader("Content-Encoding: gzip"));
94054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
941166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
94254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
94354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
944166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
945166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    // At least three request/response pairs are required because after the first request is cached
946166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    // a different execution path might be taken. Thus modifications to the cache applied during
947166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    // the second request might not be visible until another request is performed.
948166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    assertEquals("ABCABCABC", readAscii(openConnection(server.getUrl("/"))));
94954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABCABCABC", readAscii(openConnection(server.getUrl("/"))));
95054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABCABCABC", readAscii(openConnection(server.getUrl("/"))));
95154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
95254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
953166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath  @Test public void notModifiedSpecifiesEncoding() throws Exception {
954166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    server.enqueue(new MockResponse()
955166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        .setBody(gzip("ABCABCABC".getBytes("UTF-8")))
956166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        .addHeader("Content-Encoding: gzip")
957166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
958166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
959166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    server.enqueue(new MockResponse()
960166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED)
961166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        .addHeader("Content-Encoding: gzip"));
962166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    server.enqueue(new MockResponse()
963166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        .setBody("DEFDEFDEF"));
964166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
965166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    server.play();
966166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    assertEquals("ABCABCABC", readAscii(openConnection(server.getUrl("/"))));
967166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    assertEquals("ABCABCABC", readAscii(openConnection(server.getUrl("/"))));
968166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    assertEquals("DEFDEFDEF", readAscii(openConnection(server.getUrl("/"))));
969166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath  }
970166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
971c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller  @Test public void conditionalCacheHitIsNotDoublePooled() throws Exception {
972c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    server.enqueue(new MockResponse().addHeader("ETag: v1").setBody("A"));
973c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    server.enqueue(new MockResponse()
974c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller        .clearHeaders()
975c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
976c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    server.play();
977c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller
978c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    ConnectionPool pool = ConnectionPool.getDefault();
979c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    pool.evictAll();
980c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    client.setConnectionPool(pool);
981c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller
982c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
983c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
984c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    assertEquals(1, client.getConnectionPool().getConnectionCount());
985c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller  }
986c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller
98754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void expiresDateBeforeModifiedDate() throws Exception {
98854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertConditionallyCached(
98954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
99054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(-2, TimeUnit.HOURS)));
99154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
99254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
99354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMaxAge() throws IOException {
99454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
99554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
99654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(-1, TimeUnit.MINUTES))
99754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
99854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
99954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
100054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
100154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
100254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
100354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
100454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "max-age=30");
100554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection));
100654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
100754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
100854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMinFresh() throws IOException {
100954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
101054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=60")
101154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
101254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
101354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
101454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
101554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
101654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
101754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
101854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "min-fresh=120");
101954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection));
102054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
102154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
102254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMaxStale() throws IOException {
102354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
102454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=120")
102554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(-4, TimeUnit.MINUTES)));
102654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
102754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
102854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
102954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
103054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
103154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
103254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "max-stale=180");
103354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
103454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("110 HttpURLConnection \"Response is stale\"",
103554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        connection.getHeaderField("Warning"));
103654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
103754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
103854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMaxStaleNotHonoredWithMustRevalidate() throws IOException {
103954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
104054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=120, must-revalidate")
104154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(-4, TimeUnit.MINUTES)));
104254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
104354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
104454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
104554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
104654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
104754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
104854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "max-stale=180");
104954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection));
105054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
105154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
105254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestOnlyIfCachedWithNoResponseCached() throws IOException {
105354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // (no responses enqueued)
105454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
105554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
105654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(server.getUrl("/"));
105754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "only-if-cached");
105854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertGatewayTimeout(connection);
10593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(1, cache.getRequestCount());
10603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(0, cache.getNetworkCount());
10613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(0, cache.getHitCount());
106254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
106354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
106454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestOnlyIfCachedWithFullResponseCached() throws IOException {
106554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
106654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=30")
106754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
106854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
106954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
107054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
107154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
107254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "only-if-cached");
1073faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(connection));
10743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(2, cache.getRequestCount());
10753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(1, cache.getNetworkCount());
10763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(1, cache.getHitCount());
107754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
107854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
107954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestOnlyIfCachedWithConditionalResponseCached() throws IOException {
108054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
108154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=30")
108254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(-1, TimeUnit.MINUTES)));
108354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
108454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
108554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
108654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(server.getUrl("/"));
108754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "only-if-cached");
108854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertGatewayTimeout(connection);
10893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(2, cache.getRequestCount());
10903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(1, cache.getNetworkCount());
10913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(0, cache.getHitCount());
109254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
109354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
109454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestOnlyIfCachedWithUnhelpfulResponseCached() throws IOException {
109554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A"));
109654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
109754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
109854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
109954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(server.getUrl("/"));
110054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "only-if-cached");
110154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertGatewayTimeout(connection);
11023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(2, cache.getRequestCount());
11033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(1, cache.getNetworkCount());
11043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(0, cache.getHitCount());
110554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
110654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
110754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestCacheControlNoCache() throws Exception {
110854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
110954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
111054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
111154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: max-age=60")
111254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setBody("A"));
111354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
111454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
111554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
111654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
111754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
111854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(url);
111954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setRequestProperty("Cache-Control", "no-cache");
112054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection));
112154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
112254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
112354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestPragmaNoCache() throws Exception {
112454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
112554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
112654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
112754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: max-age=60")
112854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setBody("A"));
112954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
113054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
113154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
113254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
113354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
113454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(url);
113554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setRequestProperty("Pragma", "no-cache");
113654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection));
113754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
113854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
113954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientSuppliedIfModifiedSinceWithCachedResult() throws Exception {
114054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response =
114154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("ETag: v3").addHeader("Cache-Control: max-age=0");
114254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String ifModifiedSinceDate = formatDate(-24, TimeUnit.HOURS);
114354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request =
114454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        assertClientSuppliedCondition(response, "If-Modified-Since", ifModifiedSinceDate);
114554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = request.getHeaders();
114654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + ifModifiedSinceDate));
114754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFalse(headers.contains("If-None-Match: v3"));
114854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
114954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
115054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientSuppliedIfNoneMatchSinceWithCachedResult() throws Exception {
115154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-3, TimeUnit.MINUTES);
115254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response = new MockResponse().addHeader("Last-Modified: " + lastModifiedDate)
115354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
115454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0");
115554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = assertClientSuppliedCondition(response, "If-None-Match", "v1");
115654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = request.getHeaders();
115754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-None-Match: v1"));
115854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFalse(headers.contains("If-Modified-Since: " + lastModifiedDate));
115954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
116054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
116154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private RecordedRequest assertClientSuppliedCondition(MockResponse seed, String conditionName,
116254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      String conditionValue) throws Exception {
116354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(seed.setBody("A"));
116454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
116554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
116654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
116754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
116854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
116954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
117054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(url);
117154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty(conditionName, conditionValue);
117254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, connection.getResponseCode());
117354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("", readAscii(connection));
117454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
117554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.takeRequest(); // seed
117654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return server.takeRequest();
117754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
117854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
11793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
11803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Confirm that {@link URLConnection#setIfModifiedSince} causes an
11813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * If-Modified-Since header with a GMT timestamp.
11823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   *
11833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * https://code.google.com/p/android/issues/detail?id=66135
11843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
118554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void setIfModifiedSince() throws Exception {
118654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A"));
118754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
118854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
118954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
119054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(url);
11913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.setIfModifiedSince(1393666200000L);
119254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
119354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = server.takeRequest();
11943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String ifModifiedSinceHeader = request.getHeader("If-Modified-Since");
11953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("Sat, 01 Mar 2014 09:30:00 GMT", ifModifiedSinceHeader);
11963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
11973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
11983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
11993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * For Last-Modified and Date headers, we should echo the date back in the
12003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * exact format we were served.
12013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
12023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void retainServedDateFormat() throws Exception {
12033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Serve a response with a non-standard date format that OkHttp supports.
12043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Date lastModifiedDate = new Date(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(-1));
12053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Date servedDate = new Date(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(-2));
12063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    DateFormat dateFormat = new SimpleDateFormat("EEE dd-MMM-yyyy HH:mm:ss z", Locale.US);
12073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    dateFormat.setTimeZone(TimeZone.getTimeZone("EDT"));
12083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String lastModifiedString = dateFormat.format(lastModifiedDate);
12093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String servedString = dateFormat.format(servedDate);
12103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
12113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // This response should be conditionally cached.
12123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse()
12133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("Last-Modified: " + lastModifiedString)
12143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("Expires: " + servedString)
12153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .setBody("A"));
12163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse()
12173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
12183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.play();
12193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
12203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
12213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
12223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
12233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // The first request has no conditions.
12243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    RecordedRequest request1 = server.takeRequest();
12253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertNull(request1.getHeader("If-Modified-Since"));
12263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
12273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // The 2nd request uses the server's date format.
12283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    RecordedRequest request2 = server.takeRequest();
12293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(lastModifiedString, request2.getHeader("If-Modified-Since"));
123054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
123154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
123254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientSuppliedConditionWithoutCachedResult() throws Exception {
123354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
123454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
123554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
123654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(server.getUrl("/"));
123754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String clientIfModifiedSince = formatDate(-24, TimeUnit.HOURS);
123854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("If-Modified-Since", clientIfModifiedSince);
123954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, connection.getResponseCode());
124054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("", readAscii(connection));
124154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
124254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
124354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void authorizationRequestHeaderPreventsCaching() throws Exception {
124454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
124554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-2, TimeUnit.MINUTES))
124654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: max-age=60")
124754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setBody("A"));
124854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
124954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
125054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
125154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
125254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(url);
125354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Authorization", "password");
125454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
125554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(url)));
125654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
125754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
125854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void authorizationResponseCachedWithSMaxAge() throws Exception {
125954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertAuthorizationRequestFullyCached(
126054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Cache-Control: s-maxage=60"));
126154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
126254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
126354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void authorizationResponseCachedWithPublic() throws Exception {
126454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertAuthorizationRequestFullyCached(new MockResponse().addHeader("Cache-Control: public"));
126554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
126654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
126754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void authorizationResponseCachedWithMustRevalidate() throws Exception {
126854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertAuthorizationRequestFullyCached(
126954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Cache-Control: must-revalidate"));
127054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
127154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
127254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public void assertAuthorizationRequestFullyCached(MockResponse response) throws Exception {
127354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response.addHeader("Cache-Control: max-age=60").setBody("A"));
127454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
127554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
127654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
127754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
127854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(url);
127954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Authorization", "password");
128054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
128154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
128254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
128354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
128454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void contentLocationDoesNotPopulateCache() throws Exception {
128554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
128654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Content-Location: /bar")
128754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
128854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
128954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
129054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
129154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/foo"))));
129254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(server.getUrl("/bar"))));
129354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
129454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
129554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void useCachesFalseDoesNotWriteToCache() throws Exception {
129654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
129754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Cache-Control: max-age=60").setBody("A").setBody("A"));
129854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
129954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
130054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
130154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
130254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setUseCaches(false);
130354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
130454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(server.getUrl("/"))));
130554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
130654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
130754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void useCachesFalseDoesNotReadFromCache() throws Exception {
130854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
130954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Cache-Control: max-age=60").setBody("A").setBody("A"));
131054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
131154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
131254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
131354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
131454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
131554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setUseCaches(false);
131654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection));
131754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
131854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
131954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void defaultUseCachesSetsInitialValueOnly() throws Exception {
132054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = new URL("http://localhost/");
132154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection c1 = openConnection(url);
132254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection c2 = openConnection(url);
132354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(c1.getDefaultUseCaches());
132454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    c1.setDefaultUseCaches(false);
132554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
132654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertTrue(c1.getUseCaches());
132754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertTrue(c2.getUseCaches());
132854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      URLConnection c3 = openConnection(url);
132954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertFalse(c3.getUseCaches());
133054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } finally {
133154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      c1.setDefaultUseCaches(true);
133254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
133354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
133454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
133554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectionIsReturnedToPoolAfterConditionalSuccess() throws Exception {
133654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
133754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
133854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
133954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
134054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
134154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
134254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
134354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/a"))));
134454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/a"))));
134554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(server.getUrl("/b"))));
134654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
134754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, server.takeRequest().getSequenceNumber());
134854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, server.takeRequest().getSequenceNumber());
134954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, server.takeRequest().getSequenceNumber());
135054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
135154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
135254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void statisticsConditionalCacheMiss() throws Exception {
135354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
135454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
135554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
135654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
135754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("C"));
135854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
135954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
136054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
136154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getRequestCount());
136254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getNetworkCount());
136354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, cache.getHitCount());
136454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(server.getUrl("/"))));
136554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("C", readAscii(openConnection(server.getUrl("/"))));
136654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(3, cache.getRequestCount());
136754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(3, cache.getNetworkCount());
136854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, cache.getHitCount());
136954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
137054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
137154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void statisticsConditionalCacheHit() throws Exception {
137254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
137354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
137454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
137554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
137654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
137754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
137854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
137954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
138054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getRequestCount());
138154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getNetworkCount());
138254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, cache.getHitCount());
138354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
138454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
138554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(3, cache.getRequestCount());
138654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(3, cache.getNetworkCount());
138754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, cache.getHitCount());
138854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
138954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
139054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void statisticsFullCacheHit() throws Exception {
139154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60").setBody("A"));
139254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
139354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
139454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
139554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getRequestCount());
139654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getNetworkCount());
139754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, cache.getHitCount());
139854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
139954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
140054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(3, cache.getRequestCount());
140154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getNetworkCount());
140254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, cache.getHitCount());
140354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
140454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
140554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyMatchesChangedRequestHeaderField() throws Exception {
140654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
140754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Accept-Language")
140854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
140954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
141054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
141154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
141254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
141354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection frConnection = openConnection(url);
141454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    frConnection.addRequestProperty("Accept-Language", "fr-CA");
141554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(frConnection));
141654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
141754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection enConnection = openConnection(url);
141854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    enConnection.addRequestProperty("Accept-Language", "en-US");
141954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(enConnection));
142054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
142154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
142254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyMatchesUnchangedRequestHeaderField() throws Exception {
142354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
142454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Accept-Language")
142554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
142654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
142754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
142854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
142954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
143054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(url);
143154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Language", "fr-CA");
143254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
143354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(url);
143454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("Accept-Language", "fr-CA");
143554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
143654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
143754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
143854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyMatchesAbsentRequestHeaderField() throws Exception {
143954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
144054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Foo")
144154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
144254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
144354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
144454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
144554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
144654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
144754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
144854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
144954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyMatchesAddedRequestHeaderField() throws Exception {
145054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
145154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Foo")
145254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
145354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
145454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
145554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
145654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
145754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection fooConnection = openConnection(server.getUrl("/"));
145854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    fooConnection.addRequestProperty("Foo", "bar");
145954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(fooConnection));
146054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
146154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
146254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyMatchesRemovedRequestHeaderField() throws Exception {
146354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
146454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Foo")
146554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
146654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
146754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
146854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
146954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection fooConnection = openConnection(server.getUrl("/"));
147054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    fooConnection.addRequestProperty("Foo", "bar");
147154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(fooConnection));
147254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(server.getUrl("/"))));
147354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
147454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
147554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyFieldsAreCaseInsensitive() throws Exception {
147654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
147754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: ACCEPT-LANGUAGE")
147854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
147954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
148054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
148154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
148254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
148354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(url);
148454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Language", "fr-CA");
148554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
148654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(url);
148754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("accept-language", "fr-CA");
148854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
148954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
149054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
149154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyMultipleFieldsWithMatch() throws Exception {
149254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
149354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Accept-Language, Accept-Charset")
149454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Accept-Encoding")
149554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
149654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
149754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
149854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
149954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
150054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(url);
150154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Language", "fr-CA");
150254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Charset", "UTF-8");
150354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Encoding", "identity");
150454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
150554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(url);
150654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("Accept-Language", "fr-CA");
150754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("Accept-Charset", "UTF-8");
150854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("Accept-Encoding", "identity");
150954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
151054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
151154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
151254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyMultipleFieldsWithNoMatch() throws Exception {
151354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
151454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Accept-Language, Accept-Charset")
151554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Accept-Encoding")
151654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
151754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
151854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
151954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
152054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
152154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection frConnection = openConnection(url);
152254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    frConnection.addRequestProperty("Accept-Language", "fr-CA");
152354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    frConnection.addRequestProperty("Accept-Charset", "UTF-8");
152454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    frConnection.addRequestProperty("Accept-Encoding", "identity");
152554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(frConnection));
152654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection enConnection = openConnection(url);
152754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    enConnection.addRequestProperty("Accept-Language", "en-CA");
152854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    enConnection.addRequestProperty("Accept-Charset", "UTF-8");
152954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    enConnection.addRequestProperty("Accept-Encoding", "identity");
153054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(enConnection));
153154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
153254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
153354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyMultipleFieldValuesWithMatch() throws Exception {
153454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
153554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Accept-Language")
153654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
153754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
153854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
153954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
154054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
154154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(url);
154254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Language", "fr-CA, fr-FR");
154354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Language", "en-US");
154454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
154554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
154654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(url);
154754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("Accept-Language", "fr-CA, fr-FR");
154854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("Accept-Language", "en-US");
154954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
155054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
155154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
155254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyMultipleFieldValuesWithNoMatch() throws Exception {
155354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
155454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Accept-Language")
155554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
155654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
155754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
155854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
155954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
156054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(url);
156154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Language", "fr-CA, fr-FR");
156254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Language", "en-US");
156354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
156454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
156554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(url);
156654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("Accept-Language", "fr-CA");
156754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("Accept-Language", "en-US");
156854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection2));
156954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
157054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
157154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyAsterisk() throws Exception {
157254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
157354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: *")
157454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
157554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
157654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
157754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
157854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
157954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(server.getUrl("/"))));
158054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
158154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
158254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyAndHttps() throws Exception {
158354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.useHttps(sslContext.getSocketFactory(), false);
158454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
158554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Accept-Language")
158654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
158754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
158854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
158954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
159054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
159154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpsURLConnection connection1 = (HttpsURLConnection) client.open(url);
159254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.setSSLSocketFactory(sslContext.getSocketFactory());
159354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
159454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Language", "en-US");
159554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
159654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
159754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpsURLConnection connection2 = (HttpsURLConnection) client.open(url);
159854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.setSSLSocketFactory(sslContext.getSocketFactory());
159954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
160054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("Accept-Language", "en-US");
160154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
160254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
160354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
160454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cachePlusCookies() throws Exception {
160554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader(
160654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        "Set-Cookie: a=FIRST; domain=" + server.getCookieDomain() + ";")
160754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
160854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
160954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
161054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader(
161154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        "Set-Cookie: a=SECOND; domain=" + server.getCookieDomain() + ";")
161254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
161354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
161454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
161554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
161654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
161754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCookies(url, "a=FIRST");
161854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
161954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCookies(url, "a=SECOND");
162054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
162154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
162254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getHeadersReturnsNetworkEndToEndHeaders() throws Exception {
162354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Allow: GET, HEAD")
162454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
162554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
162654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
162754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Allow: GET, HEAD, PUT")
162854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
162954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
163054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
163154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(server.getUrl("/"));
163254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
163354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET, HEAD", connection1.getHeaderField("Allow"));
163454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
163554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(server.getUrl("/"));
163654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
163754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET, HEAD, PUT", connection2.getHeaderField("Allow"));
163854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
163954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
164054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getHeadersReturnsCachedHopByHopHeaders() throws Exception {
164154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Transfer-Encoding: identity")
164254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
164354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
164454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
164554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Transfer-Encoding: none")
164654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
164754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
164854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
164954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(server.getUrl("/"));
165054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
165154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("identity", connection1.getHeaderField("Transfer-Encoding"));
165254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
165354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(server.getUrl("/"));
165454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
165554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("identity", connection2.getHeaderField("Transfer-Encoding"));
165654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
165754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
165854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getHeadersDeletesCached100LevelWarnings() throws Exception {
165954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Warning: 199 test danger")
166054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
166154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
166254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
166354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
166454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
166554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
166654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(server.getUrl("/"));
166754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
166854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("199 test danger", connection1.getHeaderField("Warning"));
166954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
167054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(server.getUrl("/"));
167154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
167254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(null, connection2.getHeaderField("Warning"));
167354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
167454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
167554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getHeadersRetainsCached200LevelWarnings() throws Exception {
167654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Warning: 299 test danger")
167754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
167854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
167954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
168054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
168154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
168254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
168354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(server.getUrl("/"));
168454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
168554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("299 test danger", connection1.getHeaderField("Warning"));
168654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
168754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(server.getUrl("/"));
168854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
168954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("299 test danger", connection2.getHeaderField("Warning"));
169054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
169154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
169254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public void assertCookies(URL url, String... expectedCookies) throws Exception {
169354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> actualCookies = new ArrayList<String>();
169454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (HttpCookie cookie : cookieManager.getCookieStore().get(url.toURI())) {
169554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      actualCookies.add(cookie.toString());
169654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
169754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(Arrays.asList(expectedCookies), actualCookies);
169854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
169954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
170054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cachePlusRange() throws Exception {
170154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().setResponseCode(HttpURLConnection.HTTP_PARTIAL)
170254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
170354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Content-Range: bytes 100-100/200")
170454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=60"));
170554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
170654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
170754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void conditionalHitUpdatesCache() throws Exception {
170854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(0, TimeUnit.SECONDS))
170954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
171054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
171154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=30")
171254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Allow: GET, HEAD")
171354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
171454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
171554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
171654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
171754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // cache miss; seed the cache
171854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection1 = openConnection(server.getUrl("/a"));
171954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
172054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(null, connection1.getHeaderField("Allow"));
172154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
172254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // conditional cache hit; update the cache
172354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection2 = openConnection(server.getUrl("/a"));
172454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_OK, connection2.getResponseCode());
172554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
172654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET, HEAD", connection2.getHeaderField("Allow"));
172754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
172854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // full cache hit
172954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection3 = openConnection(server.getUrl("/a"));
173054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection3));
173154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET, HEAD", connection3.getHeaderField("Allow"));
173254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
173354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, server.getRequestCount());
173454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
173554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1736faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void responseSourceHeaderCached() throws IOException {
1737faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("A")
1738faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Cache-Control: max-age=30")
1739faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
1740faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.play();
1741faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1742faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
1743faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    URLConnection connection = openConnection(server.getUrl("/"));
1744faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    connection.addRequestProperty("Cache-Control", "only-if-cached");
1745faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(connection));
1746faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
17473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String source = connection.getHeaderField(OkHeaders.RESPONSE_SOURCE);
17483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(ResponseSource.CACHE + " 200", source);
1749faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
1750faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1751faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void responseSourceHeaderConditionalCacheFetched() throws IOException {
1752faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("A")
1753faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Cache-Control: max-age=30")
1754faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Date: " + formatDate(-31, TimeUnit.MINUTES)));
1755faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("B")
1756faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Cache-Control: max-age=30")
1757faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
1758faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.play();
1759faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1760faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
1761faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    HttpURLConnection connection = openConnection(server.getUrl("/"));
1762faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("B", readAscii(connection));
1763faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
17643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String source = connection.getHeaderField(OkHeaders.RESPONSE_SOURCE);
17653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(ResponseSource.CONDITIONAL_CACHE + " 200", source);
1766faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
1767faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1768faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void responseSourceHeaderConditionalCacheNotFetched() throws IOException {
1769faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("A")
1770faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Cache-Control: max-age=0")
1771faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
1772faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setResponseCode(304));
1773faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.play();
1774faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1775faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
1776faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    HttpURLConnection connection = openConnection(server.getUrl("/"));
1777faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(connection));
1778faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
17793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String source = connection.getHeaderField(OkHeaders.RESPONSE_SOURCE);
17803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(ResponseSource.CONDITIONAL_CACHE + " 304", source);
1781faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
1782faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1783faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void responseSourceHeaderFetched() throws IOException {
1784faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("A"));
1785faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.play();
1786faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1787faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    URLConnection connection = openConnection(server.getUrl("/"));
1788faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(connection));
1789faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
17903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String source = connection.getHeaderField(OkHeaders.RESPONSE_SOURCE);
17913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(ResponseSource.NETWORK + " 200", source);
1792faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
1793faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1794faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void emptyResponseHeaderNameFromCacheIsLenient() throws Exception {
1795faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse()
1796faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Cache-Control: max-age=120")
1797faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader(": A")
1798faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .setBody("body"));
1799faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.play();
1800faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    HttpURLConnection connection = client.open(server.getUrl("/"));
1801faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", connection.getHeaderField(""));
1802faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
1803faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
180454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
1805166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath   * Old implementations of OkHttp's response cache wrote header fields like
1806166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath   * ":status: 200 OK". This broke our cached response parser because it split
1807166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath   * on the first colon. This regression test exists to help us read these old
1808166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath   * bad cache entries.
1809166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath   *
1810166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath   * https://github.com/square/okhttp/issues/227
1811166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath   */
1812166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath  @Test public void testGoldenCacheResponse() throws Exception {
1813166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    cache.close();
1814166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    server.enqueue(new MockResponse()
1815166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        .clearHeaders()
1816166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
1817166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    server.play();
1818166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
1819166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    URL url = server.getUrl("/");
1820166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    String urlKey = Util.hash(url.toString());
1821166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    String entryMetadata = ""
1822166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "" + url + "\n"
1823166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "GET\n"
1824166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "0\n"
1825166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "HTTP/1.1 200 OK\n"
1826166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "7\n"
1827166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + ":status: 200 OK\n"
1828166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + ":version: HTTP/1.1\n"
1829166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "etag: foo\n"
1830166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "content-length: 3\n"
1831166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "OkHttp-Received-Millis: " + System.currentTimeMillis() + "\n"
1832166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "X-Android-Response-Source: NETWORK 200\n"
1833166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "OkHttp-Sent-Millis: " + System.currentTimeMillis() + "\n"
1834166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "\n"
1835166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA\n"
1836166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "1\n"
1837166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "MIIBpDCCAQ2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDEw1qd2lsc29uLmxvY2FsMB4XDTEzMDgy"
1838166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "OTA1MDE1OVoXDTEzMDgzMDA1MDE1OVowGDEWMBQGA1UEAxMNandpbHNvbi5sb2NhbDCBnzANBgkqhkiG9w0BAQEF"
1839166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "AAOBjQAwgYkCgYEAlFW+rGo/YikCcRghOyKkJanmVmJSce/p2/jH1QvNIFKizZdh8AKNwojt3ywRWaDULA/RlCUc"
1840166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "ltF3HGNsCyjQI/+Lf40x7JpxXF8oim1E6EtDoYtGWAseelawus3IQ13nmo6nWzfyCA55KhAWf4VipelEy8DjcuFK"
1841166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "v6L0xwXnI0ECAwEAATANBgkqhkiG9w0BAQsFAAOBgQAuluNyPo1HksU3+Mr/PyRQIQS4BI7pRXN8mcejXmqyscdP"
1842166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "7S6J21FBFeRR8/XNjVOp4HT9uSc2hrRtTEHEZCmpyoxixbnM706ikTmC7SN/GgM+SmcoJ1ipJcNcl8N0X6zym4dm"
1843166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "yFfXKHu2PkTo7QFdpOJFvP3lIigcSZXozfmEDg==\n"
1844166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "-1\n";
1845166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    String entryBody = "abc";
1846166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    String journalBody = ""
1847166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "libcore.io.DiskLruCache\n"
1848166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "1\n"
1849166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "201105\n"
1850166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "2\n"
1851166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "\n"
1852166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        + "CLEAN " + urlKey + " " + entryMetadata.length() + " " + entryBody.length() + "\n";
1853166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    writeFile(cache.getDirectory(), urlKey + ".0", entryMetadata);
1854166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    writeFile(cache.getDirectory(), urlKey + ".1", entryBody);
1855166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    writeFile(cache.getDirectory(), "journal", journalBody);
1856166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    cache = new HttpResponseCache(cache.getDirectory(), Integer.MAX_VALUE);
18573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.setOkResponseCache(cache);
1858166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
1859166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    HttpURLConnection connection = client.open(url);
1860166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    assertEquals(entryBody, readAscii(connection));
1861166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    assertEquals("3", connection.getHeaderField("Content-Length"));
1862166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    assertEquals("foo", connection.getHeaderField("etag"));
1863166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath  }
1864166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
18654944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller  // Older versions of OkHttp use ResponseCache.get() and ResponseCache.put(). For compatibility
18664944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller  // with Android apps when the Android-bundled and and an older app-bundled OkHttp library are in
18674944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller  // use at the same time the HttpResponseCache must behave as it always used to. That's not the
18684944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller  // same as a fully API-compliant {@link ResponseCache}: That means that the cache
18694944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller  // doesn't throw an exception from get() or put() and also does not cache requests/responses from
18704944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller  // anything other than the variant of OkHttp that it comes with. It does still return values from
18714944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller  // get() and it is not expected to implement any cache-control logic.
18724944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller  @Test public void testHttpResponseCacheBackwardsCompatible() throws Exception {
18734944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    assertSame(cache, ResponseCache.getDefault());
18744944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    assertEquals(0, cache.getRequestCount());
18754944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller
18764944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    String body = "Body";
18774944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    server.enqueue(new MockResponse()
18784944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
18794944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
18804944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller        .setBody(body));
18814944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    server.play();
18824944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller
18834944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    URL url = server.getUrl("/");
18844944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller
18854944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    // Here we use a HttpURLConnection from URL to represent a non-OkHttp HttpURLConnection. In
18864944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    // Android this would be com.android.okhttp.internal.http.HttpURLConnectionImpl. In tests this
18874944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    // is some other implementation.
18884944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    HttpURLConnection javaConnection = (HttpURLConnection) url.openConnection();
18894944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    assertFalse("This test relies on url.openConnection() not returning an OkHttp connection",
18904944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller        javaConnection instanceof HttpURLConnectionImpl);
18914944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    javaConnection.disconnect();
18924944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller
18934944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    // This should simply be discarded. It doesn't matter the connection is not useful.
18944944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    cache.put(url.toURI(), javaConnection);
18954944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller
18964944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    // Confirm the initial cache state.
18974944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    assertNull(cache.get(url.toURI(), "GET", new HashMap<String, List<String>>()));
18984944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller
18994944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    // Now cache a response
19004944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    HttpURLConnection okHttpConnection = openConnection(url);
19014944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    assertEquals(body, readAscii(okHttpConnection));
19024944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    okHttpConnection.disconnect();
19034944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller
19044944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    assertEquals(1, server.getRequestCount());
19054944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    assertEquals(0, cache.getHitCount());
19064944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller
19074944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    // OkHttp should now find the result cached.
19084944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    HttpURLConnection okHttpConnection2 = openConnection(url);
19094944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    assertEquals(body, readAscii(okHttpConnection2));
19104944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    okHttpConnection2.disconnect();
19114944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller
19124944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    assertEquals(1, server.getRequestCount());
19134944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    assertEquals(1, cache.getHitCount());
19144944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller
19154944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    // Confirm the unfortunate get() behavior.
19164944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    assertNotNull(cache.get(url.toURI(), "GET", new HashMap<String, List<String>>()));
19174944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    // Only OkHttp makes the necessary callbacks to increment the cache stats.
19184944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller    assertEquals(1, cache.getHitCount());
19194944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller  }
19204944713f5c5b141966ac82973d6a31a634e8e01eNeil Fuller
1921166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath  private void writeFile(File directory, String file, String content) throws IOException {
1922166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    OutputStream out = new FileOutputStream(new File(directory, file));
1923166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    out.write(content.getBytes(Util.UTF_8));
1924166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    out.close();
1925166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath  }
1926166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
1927166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath  /**
192854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @param delta the offset from the current date to use. Negative
192954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * values yield dates in the past; positive values yield dates in the
193054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * future.
193154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
193254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private String formatDate(long delta, TimeUnit timeUnit) {
193354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return formatDate(new Date(System.currentTimeMillis() + timeUnit.toMillis(delta)));
193454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
193554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
193654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private String formatDate(Date date) {
193754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    DateFormat rfc1123 = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
1938166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    rfc1123.setTimeZone(TimeZone.getTimeZone("GMT"));
193954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return rfc1123.format(date);
194054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
194154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
194254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void addRequestBodyIfNecessary(String requestMethod, HttpURLConnection invalidate)
194354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throws IOException {
194454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (requestMethod.equals("POST") || requestMethod.equals("PUT")) {
194554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      invalidate.setDoOutput(true);
194654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      OutputStream requestBody = invalidate.getOutputStream();
194754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      requestBody.write('x');
194854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      requestBody.close();
194954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
195054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
195154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
195254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void assertNotCached(MockResponse response) throws Exception {
195354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response.setBody("A"));
195454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
195554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
195654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
195754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
195854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
195954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(url)));
196054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
196154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
196254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** @return the request with the conditional get headers. */
196354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private RecordedRequest assertConditionallyCached(MockResponse response) throws Exception {
196454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // scenario 1: condition succeeds
196554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response.setBody("A").setStatus("HTTP/1.1 200 A-OK"));
196654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
196754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
196854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // scenario 2: condition fails
196954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response.setBody("B").setStatus("HTTP/1.1 200 B-OK"));
197054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setStatus("HTTP/1.1 200 C-OK").setBody("C"));
197154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
197254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
197354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
197454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL valid = server.getUrl("/valid");
197554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection1 = openConnection(valid);
197654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
197754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_OK, connection1.getResponseCode());
197854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A-OK", connection1.getResponseMessage());
197954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection2 = openConnection(valid);
198054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
198154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_OK, connection2.getResponseCode());
198254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A-OK", connection2.getResponseMessage());
198354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
198454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL invalid = server.getUrl("/invalid");
198554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection3 = openConnection(invalid);
198654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection3));
198754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_OK, connection3.getResponseCode());
198854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B-OK", connection3.getResponseMessage());
198954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection4 = openConnection(invalid);
199054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("C", readAscii(connection4));
199154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_OK, connection4.getResponseCode());
199254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("C-OK", connection4.getResponseMessage());
199354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
199454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.takeRequest(); // regular get
199554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return server.takeRequest(); // conditional get
199654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
199754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
199854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void assertFullyCached(MockResponse response) throws Exception {
199954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response.setBody("A"));
200054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response.setBody("B"));
200154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
200254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
200354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
200454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
200554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
200654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
200754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
200854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
200954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Shortens the body of {@code response} but not the corresponding headers.
201054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Only useful to test how clients respond to the premature conclusion of
201154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * the HTTP body.
201254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
201354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private MockResponse truncateViolently(MockResponse response, int numBytesToKeep) {
201454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    response.setSocketPolicy(DISCONNECT_AT_END);
201554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = new ArrayList<String>(response.getHeaders());
201654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    response.setBody(Arrays.copyOfRange(response.getBody(), 0, numBytesToKeep));
201754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    response.getHeaders().clear();
201854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    response.getHeaders().addAll(headers);
201954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return response;
202054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
202154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
202254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
202354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Reads {@code count} characters from the stream. If the stream is
202454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * exhausted before {@code count} characters can be read, the remaining
202554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * characters are returned and the stream is closed.
202654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
202754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private String readAscii(URLConnection connection, int count) throws IOException {
202854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection httpConnection = (HttpURLConnection) connection;
202954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream in = httpConnection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST
20303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        ? connection.getInputStream()
20313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        : httpConnection.getErrorStream();
203254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    StringBuilder result = new StringBuilder();
203354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 0; i < count; i++) {
203454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      int value = in.read();
203554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (value == -1) {
20367899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        in.close();
203754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        break;
203854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
203954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      result.append((char) value);
204054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
204154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return result.toString();
204254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
204354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
204454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private String readAscii(URLConnection connection) throws IOException {
204554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return readAscii(connection, Integer.MAX_VALUE);
204654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
204754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
204854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void reliableSkip(InputStream in, int length) throws IOException {
204954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    while (length > 0) {
205054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      length -= in.skip(length);
205154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
205254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
205354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
205454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void assertGatewayTimeout(HttpURLConnection connection) throws IOException {
205554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
205654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.getInputStream();
205754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
205854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (FileNotFoundException expected) {
205954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
206054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(504, connection.getResponseCode());
206154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(-1, connection.getErrorStream().read());
20623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(ResponseSource.NONE + " 504",
20633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        connection.getHeaderField(OkHeaders.RESPONSE_SOURCE));
206454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
206554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
206654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  enum TransferKind {
206754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    CHUNKED() {
206854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      @Override void setBody(MockResponse response, byte[] content, int chunkSize)
206954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          throws IOException {
207054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        response.setChunkedBody(content, chunkSize);
207154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
207254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    },
207354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    FIXED_LENGTH() {
207454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      @Override void setBody(MockResponse response, byte[] content, int chunkSize) {
207554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        response.setBody(content);
207654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
207754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    },
207854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    END_OF_STREAM() {
207954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      @Override void setBody(MockResponse response, byte[] content, int chunkSize) {
208054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        response.setBody(content);
20817899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        response.setSocketPolicy(DISCONNECT_AT_END);
208254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        for (Iterator<String> h = response.getHeaders().iterator(); h.hasNext(); ) {
208354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          if (h.next().startsWith("Content-Length:")) {
208454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            h.remove();
208554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            break;
208654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          }
20877899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        }
208854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
208954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    };
20907899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
209154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    abstract void setBody(MockResponse response, byte[] content, int chunkSize) throws IOException;
209254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
209354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    void setBody(MockResponse response, String content, int chunkSize) throws IOException {
209454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      setBody(response, content.getBytes("UTF-8"), chunkSize);
209554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
209654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
209754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
209854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private <T> List<T> toListOrNull(T[] arrayOrNull) {
209954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return arrayOrNull != null ? Arrays.asList(arrayOrNull) : null;
210054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
210154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
210254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Returns a gzipped copy of {@code bytes}. */
210354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public byte[] gzip(byte[] bytes) throws IOException {
210454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
210554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    OutputStream gzippedOut = new GZIPOutputStream(bytesOut);
210654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    gzippedOut.write(bytes);
210754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    gzippedOut.close();
210854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return bytesOut.toByteArray();
210954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
211054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
21113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  static abstract class AbstractOkResponseCache implements OkResponseCache {
21123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public Response get(Request request) throws IOException {
21133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return null;
211454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
211554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
21163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public CacheRequest put(Response response) throws IOException {
21173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return null;
21183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
21193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
21203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public boolean maybeRemove(Request request) throws IOException {
21213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return false;
21223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
21233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
21243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public void update(Response cached, Response network) throws IOException {
21253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
21263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
21273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public void trackConditionalCacheHit() {
21283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
21293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
21303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public void trackResponse(ResponseSource source) {
21317899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
213254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
21337899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath}
2134