17899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath/*
23c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Copyright (C) 2014 Square, Inc.
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
192231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.OkHttpClient;
20faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport com.squareup.okhttp.ResponseSource;
212231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.internal.SslContextBuilder;
22166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport com.squareup.okhttp.mockwebserver.MockResponse;
23166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport com.squareup.okhttp.mockwebserver.MockWebServer;
24166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport com.squareup.okhttp.mockwebserver.RecordedRequest;
257899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.BufferedReader;
263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.ByteArrayInputStream;
277899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.ByteArrayOutputStream;
287899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.FileNotFoundException;
297899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.IOException;
307899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.InputStream;
317899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.InputStreamReader;
327899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.OutputStream;
337899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.CacheRequest;
347899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.CacheResponse;
357899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.CookieManager;
367899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.HttpCookie;
377899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.HttpURLConnection;
387899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.ResponseCache;
397899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.SecureCacheResponse;
407899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.URI;
417899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.URISyntaxException;
427899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.URL;
437899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.URLConnection;
442231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.security.Principal;
452231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.security.cert.Certificate;
467899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.text.DateFormat;
477899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.text.SimpleDateFormat;
487899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.ArrayList;
497899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.Arrays;
507899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.Date;
513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.HashMap;
527899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.Iterator;
537899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.List;
547899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.Locale;
557899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.Map;
567899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.TimeZone;
577899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.concurrent.TimeUnit;
587899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.concurrent.atomic.AtomicReference;
597899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.zip.GZIPOutputStream;
602231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport javax.net.ssl.HostnameVerifier;
612231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport javax.net.ssl.HttpsURLConnection;
622231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport javax.net.ssl.SSLContext;
633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport javax.net.ssl.SSLPeerUnverifiedException;
642231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport javax.net.ssl.SSLSession;
652231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport org.junit.After;
6654cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport org.junit.Before;
6754cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport org.junit.Test;
6854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
69166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport static com.squareup.okhttp.mockwebserver.SocketPolicy.DISCONNECT_AT_END;
702231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.assertEquals;
712231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.assertFalse;
722231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.assertNotNull;
732231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.assertNull;
743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.assertSame;
752231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.assertTrue;
762231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.fail;
777899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller/**
793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Tests for interaction between OkHttp and the ResponseCache. This test is based on
803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * {@link com.squareup.okhttp.internal.http.HttpResponseCacheTest}. Some tests for the
813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * {@link com.squareup.okhttp.OkResponseCache} found in HttpResponseCacheTest provide
823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * coverage for ResponseCache as well.
833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */
843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpublic final class ResponseCacheTest {
8554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static final HostnameVerifier NULL_HOSTNAME_VERIFIER = new HostnameVerifier() {
8654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public boolean verify(String s, SSLSession sslSession) {
8754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return true;
8854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
8954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  };
903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private static final SSLContext sslContext = SslContextBuilder.localhost();
923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private OkHttpClient client;
943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private MockWebServer server;
953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private MockWebServer server2;
963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private ResponseCache cache;
9754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
9854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Before public void setUp() throws Exception {
993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server =  new MockWebServer();
100166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    server.setNpnEnabled(false);
1013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server2 =  new MockWebServer();
1023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client = new OkHttpClient();
1043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    cache = new InMemoryResponseCache();
1053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    ResponseCache.setDefault(cache);
10654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
10754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
10854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @After public void tearDown() throws Exception {
10954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.shutdown();
11074623587bf099735cf16ec2298005f12fd3fcb07jwilson    server2.shutdown();
1113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    CookieManager.setDefault(null);
11254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
11354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
11454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private HttpURLConnection openConnection(URL url) {
11554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return client.open(url);
11654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
11754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void responseCacheAccessWithOkHttpMember() throws IOException {
1193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    ResponseCache.setDefault(null);
1203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.setResponseCache(cache);
1213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertSame(cache, client.getResponseCache());
1223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertTrue(client.getOkResponseCache() instanceof ResponseCacheAdapter);
12354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
12454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void responseCacheAccessWithGlobalDefault() throws IOException {
1263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    ResponseCache.setDefault(cache);
1273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.setResponseCache(null);
1283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertNull(client.getOkResponseCache());
1293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertNull(client.getResponseCache());
13054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
13154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
13254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCachingAndInputStreamSkipWithFixedLength() throws IOException {
13354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testResponseCaching(TransferKind.FIXED_LENGTH);
13454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
13554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
13654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCachingAndInputStreamSkipWithChunkedEncoding() throws IOException {
13754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testResponseCaching(TransferKind.CHUNKED);
13854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
13954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
14054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCachingAndInputStreamSkipWithNoLengthHeaders() throws IOException {
14154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testResponseCaching(TransferKind.END_OF_STREAM);
14254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
14354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
14454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
14554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * HttpURLConnection.getInputStream().skip(long) causes ResponseCache corruption
14654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * http://code.google.com/p/android/issues/detail?id=8175
14754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
14854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testResponseCaching(TransferKind transferKind) throws IOException {
14954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response =
15054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
15154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
15254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setStatus("HTTP/1.1 200 Fantastic");
15354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transferKind.setBody(response, "I love puppies but hate spiders", 1);
15454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
15554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
15654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
15754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Make sure that calling skip() doesn't omit bytes from the cache.
15854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection urlConnection = openConnection(server.getUrl("/"));
15954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream in = urlConnection.getInputStream();
16054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("I love ", readAscii(urlConnection, "I love ".length()));
16154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    reliableSkip(in, "puppies but hate ".length());
16254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("spiders", readAscii(urlConnection, "spiders".length()));
16354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(-1, in.read());
16454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    in.close();
16554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
16654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    urlConnection = openConnection(server.getUrl("/")); // cached!
16754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    in = urlConnection.getInputStream();
16854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("I love puppies but hate spiders",
16954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        readAscii(urlConnection, "I love puppies but hate spiders".length()));
17054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(200, urlConnection.getResponseCode());
17154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("Fantastic", urlConnection.getResponseMessage());
17254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
17354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(-1, in.read());
17454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    in.close();
17554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
17654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
17754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void secureResponseCaching() throws IOException {
17854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.useHttps(sslContext.getSocketFactory(), false);
17954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
18054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
18154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("ABC"));
18254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
18354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpsURLConnection c1 = (HttpsURLConnection) openConnection(server.getUrl("/"));
1853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    c1.setSSLSocketFactory(sslContext.getSocketFactory());
1863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    c1.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
1873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("ABC", readAscii(c1));
18854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
18954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // OpenJDK 6 fails on this line, complaining that the connection isn't open yet
1903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String suite = c1.getCipherSuite();
1913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    List<Certificate> localCerts = toListOrNull(c1.getLocalCertificates());
1923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    List<Certificate> serverCerts = toListOrNull(c1.getServerCertificates());
1933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Principal peerPrincipal = c1.getPeerPrincipal();
1943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Principal localPrincipal = c1.getLocalPrincipal();
19554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpsURLConnection c2 = (HttpsURLConnection) openConnection(server.getUrl("/")); // cached!
1973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    c2.setSSLSocketFactory(sslContext.getSocketFactory());
1983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    c2.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
1993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("ABC", readAscii(c2));
20054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(suite, c2.getCipherSuite());
2023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(localCerts, toListOrNull(c2.getLocalCertificates()));
2033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(serverCerts, toListOrNull(c2.getServerCertificates()));
2043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(peerPrincipal, c2.getPeerPrincipal());
2053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(localPrincipal, c2.getLocalPrincipal());
20654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
20754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
20854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cacheReturnsInsecureResponseForSecureRequest() throws IOException {
20954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.useHttps(sslContext.getSocketFactory(), false);
21054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("ABC"));
21154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("DEF"));
21254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
21354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.setResponseCache(new InsecureResponseCache(new InMemoryResponseCache()));
21554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpsURLConnection connection1 = (HttpsURLConnection) openConnection(server.getUrl("/"));
21754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.setSSLSocketFactory(sslContext.getSocketFactory());
21854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
21954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(connection1));
22054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
22154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Not cached!
2223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpsURLConnection connection2 = (HttpsURLConnection) openConnection(server.getUrl("/"));
22354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.setSSLSocketFactory(sslContext.getSocketFactory());
22454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
22554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("DEF", readAscii(connection2));
22654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
22754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
22854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCachingAndRedirects() throws Exception {
22954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
23054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
23154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
23254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Location: /foo"));
23354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
23454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
23554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("ABC"));
23654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("DEF"));
23754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
23854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
23954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(server.getUrl("/"));
24054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(connection));
24154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
24254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection = openConnection(server.getUrl("/")); // cached!
24354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(connection));
24454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
24554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
24654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void redirectToCachedResult() throws Exception {
24754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60").setBody("ABC"));
24854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
24954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Location: /foo"));
25054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("DEF"));
25154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
25254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
25354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(openConnection(server.getUrl("/foo"))));
25454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request1 = server.takeRequest();
25554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET /foo HTTP/1.1", request1.getRequestLine());
25654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, request1.getSequenceNumber());
25754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
25854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(openConnection(server.getUrl("/bar"))));
25954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request2 = server.takeRequest();
26054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET /bar HTTP/1.1", request2.getRequestLine());
26154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, request2.getSequenceNumber());
26254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
26354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // an unrelated request should reuse the pooled connection
26454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("DEF", readAscii(openConnection(server.getUrl("/baz"))));
26554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request3 = server.takeRequest();
26654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET /baz HTTP/1.1", request3.getRequestLine());
26754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, request3.getSequenceNumber());
26854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
26954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
27054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void secureResponseCachingAndRedirects() throws IOException {
27154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.useHttps(sslContext.getSocketFactory(), false);
27254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
27354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
27454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
27554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Location: /foo"));
27654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
27754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
27854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("ABC"));
27954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("DEF"));
28054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
28154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
282166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    client.setSslSocketFactory(sslContext.getSocketFactory());
283166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    client.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
284166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
2853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpsURLConnection connection1 = (HttpsURLConnection) openConnection(server.getUrl("/"));
28654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(connection1));
2873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertNotNull(connection1.getCipherSuite());
28854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
28954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Cached!
2903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpsURLConnection connection2 = (HttpsURLConnection) openConnection(server.getUrl("/"));
29154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(connection2));
2923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertNotNull(connection2.getCipherSuite());
29354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(connection1.getCipherSuite(), connection2.getCipherSuite());
29574623587bf099735cf16ec2298005f12fd3fcb07jwilson  }
29674623587bf099735cf16ec2298005f12fd3fcb07jwilson
29774623587bf099735cf16ec2298005f12fd3fcb07jwilson  /**
29874623587bf099735cf16ec2298005f12fd3fcb07jwilson   * We've had bugs where caching and cross-protocol redirects yield class
29974623587bf099735cf16ec2298005f12fd3fcb07jwilson   * cast exceptions internal to the cache because we incorrectly assumed that
30074623587bf099735cf16ec2298005f12fd3fcb07jwilson   * HttpsURLConnection was always HTTPS and HttpURLConnection was always HTTP;
30174623587bf099735cf16ec2298005f12fd3fcb07jwilson   * in practice redirects mean that each can do either.
30274623587bf099735cf16ec2298005f12fd3fcb07jwilson   *
30374623587bf099735cf16ec2298005f12fd3fcb07jwilson   * https://github.com/square/okhttp/issues/214
30474623587bf099735cf16ec2298005f12fd3fcb07jwilson   */
30574623587bf099735cf16ec2298005f12fd3fcb07jwilson  @Test public void secureResponseCachingAndProtocolRedirects() throws IOException {
30674623587bf099735cf16ec2298005f12fd3fcb07jwilson    server2.useHttps(sslContext.getSocketFactory(), false);
30774623587bf099735cf16ec2298005f12fd3fcb07jwilson    server2.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
30874623587bf099735cf16ec2298005f12fd3fcb07jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
30974623587bf099735cf16ec2298005f12fd3fcb07jwilson        .setBody("ABC"));
31074623587bf099735cf16ec2298005f12fd3fcb07jwilson    server2.enqueue(new MockResponse().setBody("DEF"));
31174623587bf099735cf16ec2298005f12fd3fcb07jwilson    server2.play();
31274623587bf099735cf16ec2298005f12fd3fcb07jwilson
31374623587bf099735cf16ec2298005f12fd3fcb07jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
31474623587bf099735cf16ec2298005f12fd3fcb07jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
31574623587bf099735cf16ec2298005f12fd3fcb07jwilson        .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
31674623587bf099735cf16ec2298005f12fd3fcb07jwilson        .addHeader("Location: " + server2.getUrl("/")));
31774623587bf099735cf16ec2298005f12fd3fcb07jwilson    server.play();
31874623587bf099735cf16ec2298005f12fd3fcb07jwilson
31974623587bf099735cf16ec2298005f12fd3fcb07jwilson    client.setSslSocketFactory(sslContext.getSocketFactory());
32074623587bf099735cf16ec2298005f12fd3fcb07jwilson    client.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
32174623587bf099735cf16ec2298005f12fd3fcb07jwilson
3223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpURLConnection connection1 = openConnection(server.getUrl("/"));
32374623587bf099735cf16ec2298005f12fd3fcb07jwilson    assertEquals("ABC", readAscii(connection1));
32474623587bf099735cf16ec2298005f12fd3fcb07jwilson
32574623587bf099735cf16ec2298005f12fd3fcb07jwilson    // Cached!
3263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpURLConnection connection2 = openConnection(server.getUrl("/"));
32774623587bf099735cf16ec2298005f12fd3fcb07jwilson    assertEquals("ABC", readAscii(connection2));
32854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
32954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
33054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCacheRequestHeaders() throws IOException, URISyntaxException {
33154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("ABC"));
33254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
33354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
33454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    final AtomicReference<Map<String, List<String>>> requestHeadersRef =
33554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new AtomicReference<Map<String, List<String>>>();
3363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.setResponseCache(new ResponseCache() {
3373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      @Override
3383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      public CacheResponse get(URI uri, String requestMethod,
33954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          Map<String, List<String>> requestHeaders) throws IOException {
34054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        requestHeadersRef.set(requestHeaders);
34154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return null;
34254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
3433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
3443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      @Override
3453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      public CacheRequest put(URI uri, URLConnection conn) throws IOException {
34654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return null;
34754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
34854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    });
34954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
35054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
35154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection urlConnection = openConnection(url);
35254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    urlConnection.addRequestProperty("A", "android");
35354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    readAscii(urlConnection);
35454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(Arrays.asList("android"), requestHeadersRef.get().get("A"));
35554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
35654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
35754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void serverDisconnectsPrematurelyWithContentLengthHeader() throws IOException {
35854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testServerPrematureDisconnect(TransferKind.FIXED_LENGTH);
35954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
36054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
36154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void serverDisconnectsPrematurelyWithChunkedEncoding() throws IOException {
36254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testServerPrematureDisconnect(TransferKind.CHUNKED);
36354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
36454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
36554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void serverDisconnectsPrematurelyWithNoLengthHeaders() throws IOException {
36654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Intentionally empty. This case doesn't make sense because there's no
36754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // such thing as a premature disconnect when the disconnect itself
36854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // indicates the end of the data stream.
36954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
37054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
37154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testServerPrematureDisconnect(TransferKind transferKind) throws IOException {
37254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response = new MockResponse();
37354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transferKind.setBody(response, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 16);
37454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(truncateViolently(response, 16));
37554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("Request #2"));
37654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
37754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
37854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    BufferedReader reader = new BufferedReader(
37954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new InputStreamReader(openConnection(server.getUrl("/")).getInputStream()));
38054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABCDE", reader.readLine());
38154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
38254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      reader.readLine();
38354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail("This implementation silently ignored a truncated HTTP body.");
38454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException expected) {
3853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        expected.printStackTrace();
38654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } finally {
38754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      reader.close();
38854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
38954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
39054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
39154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("Request #2", readAscii(connection));
39254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
39354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
39454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientPrematureDisconnectWithContentLengthHeader() throws IOException {
39554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testClientPrematureDisconnect(TransferKind.FIXED_LENGTH);
39654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
39754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
39854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientPrematureDisconnectWithChunkedEncoding() throws IOException {
39954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testClientPrematureDisconnect(TransferKind.CHUNKED);
40054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
40154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
40254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientPrematureDisconnectWithNoLengthHeaders() throws IOException {
40354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testClientPrematureDisconnect(TransferKind.END_OF_STREAM);
40454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
40554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
40654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testClientPrematureDisconnect(TransferKind transferKind) throws IOException {
40754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Setting a low transfer speed ensures that stream discarding will time out.
4083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    MockResponse response = new MockResponse().throttleBody(6, 1, TimeUnit.SECONDS);
40954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transferKind.setBody(response, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 1024);
41054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
41154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("Request #2"));
41254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
41354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
41454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
41554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream in = connection.getInputStream();
41654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABCDE", readAscii(connection, 5));
41754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    in.close();
41854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
41954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      in.read();
42054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail("Expected an IOException because the stream is closed.");
42154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException expected) {
42254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
42354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
42454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection = openConnection(server.getUrl("/"));
42554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("Request #2", readAscii(connection));
42654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
42754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
42854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void defaultExpirationDateFullyCachedForLessThan24Hours() throws Exception {
42954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //      last modified: 105 seconds ago
43054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //             served:   5 seconds ago
43154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //   default lifetime: (105 - 5) / 10 = 10 seconds
43254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //            expires:  10 seconds from served date = 5 seconds from now
43354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
43454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-105, TimeUnit.SECONDS))
43554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Date: " + formatDate(-5, TimeUnit.SECONDS))
43654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setBody("A"));
43754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
43854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
43954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
44054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
44154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(url);
44254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
44354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNull(connection.getHeaderField("Warning"));
44454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
44554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
44654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void defaultExpirationDateConditionallyCached() throws Exception {
44754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //      last modified: 115 seconds ago
44854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //             served:  15 seconds ago
44954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //   default lifetime: (115 - 15) / 10 = 10 seconds
45054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //            expires:  10 seconds from served date = 5 seconds ago
45154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-115, TimeUnit.SECONDS);
45254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest = assertConditionallyCached(
45354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + lastModifiedDate)
45454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Date: " + formatDate(-15, TimeUnit.SECONDS)));
45554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = conditionalRequest.getHeaders();
45654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
45754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
45854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
45954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void defaultExpirationDateFullyCachedForMoreThan24Hours() throws Exception {
46054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //      last modified: 105 days ago
46154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //             served:   5 days ago
46254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //   default lifetime: (105 - 5) / 10 = 10 days
46354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //            expires:  10 days from served date = 5 days from now
46454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-105, TimeUnit.DAYS))
46554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(-5, TimeUnit.DAYS))
46654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
46754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
46854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
46954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
47054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
47154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
47254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("113 HttpURLConnection \"Heuristic expiration\"",
47354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        connection.getHeaderField("Warning"));
47454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
47554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
47654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void noDefaultExpirationForUrlsWithQueryString() throws Exception {
47754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
47854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-105, TimeUnit.SECONDS))
47954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Date: " + formatDate(-5, TimeUnit.SECONDS))
48054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setBody("A"));
48154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
48254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
48354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
48454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/?foo=bar");
48554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
48654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(url)));
48754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
48854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
48954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void expirationDateInThePastWithLastModifiedHeader() throws Exception {
49054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
49154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest = assertConditionallyCached(
49254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + lastModifiedDate)
49354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
49454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = conditionalRequest.getHeaders();
49554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
49654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
49754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
49854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void expirationDateInThePastWithNoLastModifiedHeader() throws Exception {
49954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
50054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
50154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
50254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void expirationDateInTheFuture() throws Exception {
50354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(new MockResponse().addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
50454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
50554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
50654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgePreferredWithMaxAgeAndExpires() throws Exception {
50754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(new MockResponse().addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
50854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS))
50954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=60"));
51054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
51154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
51254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgeInThePastWithDateAndLastModifiedHeaders() throws Exception {
51354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
51454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest = assertConditionallyCached(
51554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Date: " + formatDate(-120, TimeUnit.SECONDS))
51654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Last-Modified: " + lastModifiedDate)
51754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: max-age=60"));
51854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = conditionalRequest.getHeaders();
51954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
52054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
52154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
52254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgeInThePastWithDateHeaderButNoLastModifiedHeader() throws Exception {
52354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Chrome interprets max-age relative to the local clock. Both our cache
52454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // and Firefox both use the earlier of the local and server's clock.
52554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Date: " + formatDate(-120, TimeUnit.SECONDS))
52654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=60"));
52754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
52854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
52954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgeInTheFutureWithDateHeader() throws Exception {
53054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(new MockResponse().addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
53154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=60"));
53254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
53354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
53454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgeInTheFutureWithNoDateHeader() throws Exception {
53554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(new MockResponse().addHeader("Cache-Control: max-age=60"));
53654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
53754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
53854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgeWithLastModifiedButNoServedDate() throws Exception {
53954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(
54054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
54154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: max-age=60"));
54254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
54354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
54454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgeInTheFutureWithDateAndLastModifiedHeaders() throws Exception {
54554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(
54654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
54754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
54854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: max-age=60"));
54954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
55054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
55154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgePreferredOverLowerSharedMaxAge() throws Exception {
55254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(new MockResponse().addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
55354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: s-maxage=60")
55454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=180"));
55554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
55654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
55754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgePreferredOverHigherMaxAge() throws Exception {
55854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
55954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: s-maxage=180")
56054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=60"));
56154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
56254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
5633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
5643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Tests that the ResponseCache can cache something. The InMemoryResponseCache only caches GET
5653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * requests.
5663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
5673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void responseCacheCanCache() throws Exception {
56854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRequestMethod("GET", true);
56954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
57054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
5713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
5723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Confirm the ResponseCache can elect to not cache something. The InMemoryResponseCache only
5733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * caches GET requests.
5743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
5753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void responseCacheCanIgnore() throws Exception {
57654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRequestMethod("HEAD", false);
57754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
57854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
57954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testRequestMethod(String requestMethod, boolean expectCached) throws Exception {
58054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // 1. seed the cache (potentially)
58154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // 2. expect a cache hit or miss
58254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
58354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("X-Response-ID: 1"));
58454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("X-Response-ID: 2"));
58554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
58654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
58754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
58854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
58954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection request1 = openConnection(url);
59054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    request1.setRequestMethod(requestMethod);
59154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    addRequestBodyIfNecessary(requestMethod, request1);
59254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("1", request1.getHeaderField("X-Response-ID"));
59354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
59454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection request2 = openConnection(url);
59554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (expectCached) {
5963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      assertEquals("1", request2.getHeaderField("X-Response-ID"));
59754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
59854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertEquals("2", request2.getHeaderField("X-Response-ID"));
59954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
60054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
60154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
6023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
6033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Equivalent to {@link HttpResponseCacheTest#postInvalidatesCacheWithUncacheableResponse()} but
6043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * demonstrating that {@link ResponseCache} provides no mechanism for cache invalidation as the
6053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * result of locally-made requests. In reality invalidation could take place from other clients at
6063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * any time.
6073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
608a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  @Test public void postInvalidatesCacheWithUncacheableResponse() throws Exception {
609a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    // 1. seed the cache
610a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    // 2. invalidate it with uncacheable response
6113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // 3. the cache to return the original value
612a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    server.enqueue(
613a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        new MockResponse().setBody("A").addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
614a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    server.enqueue(new MockResponse().setBody("B").setResponseCode(500));
615a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    server.play();
616a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
617a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    URL url = server.getUrl("/");
618a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
619a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    assertEquals("A", readAscii(openConnection(url)));
620a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
621a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    HttpURLConnection invalidate = openConnection(url);
622a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    invalidate.setRequestMethod("POST");
623a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    addRequestBodyIfNecessary("POST", invalidate);
624a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    assertEquals("B", readAscii(invalidate));
625a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
6263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("A", readAscii(openConnection(url)));
627a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  }
628a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
62954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void etag() throws Exception {
63054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest =
63154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        assertConditionallyCached(new MockResponse().addHeader("ETag: v1"));
63254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(conditionalRequest.getHeaders().contains("If-None-Match: v1"));
63354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
63454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
63554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void etagAndExpirationDateInThePast() throws Exception {
63654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
63754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest = assertConditionallyCached(
63854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("ETag: v1")
63954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Last-Modified: " + lastModifiedDate)
64054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
64154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = conditionalRequest.getHeaders();
64254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-None-Match: v1"));
64354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
64454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
64554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
64654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void etagAndExpirationDateInTheFuture() throws Exception {
64754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(new MockResponse().addHeader("ETag: v1")
64854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
64954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
65054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
65154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
65254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cacheControlNoCache() throws Exception {
65354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Cache-Control: no-cache"));
65454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
65554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
65654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cacheControlNoCacheAndExpirationDateInTheFuture() throws Exception {
65754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
65854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest = assertConditionallyCached(
65954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + lastModifiedDate)
66054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
66154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: no-cache"));
66254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = conditionalRequest.getHeaders();
66354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
66454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
66554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
66654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void pragmaNoCache() throws Exception {
66754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Pragma: no-cache"));
66854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
66954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
67054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void pragmaNoCacheAndExpirationDateInTheFuture() throws Exception {
67154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
67254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest = assertConditionallyCached(
67354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + lastModifiedDate)
67454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
67554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Pragma: no-cache"));
67654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = conditionalRequest.getHeaders();
67754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
67854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
67954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
68054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cacheControlNoStore() throws Exception {
68154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Cache-Control: no-store"));
68254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
68354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
68454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cacheControlNoStoreAndExpirationDateInTheFuture() throws Exception {
68554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
68654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
68754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: no-store"));
68854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
68954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
69054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void partialRangeResponsesDoNotCorruptCache() throws Exception {
69154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // 1. request a range
69254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // 2. request a full document, expecting a cache miss
69354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("AA")
69454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_PARTIAL)
69554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
69654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Content-Range: bytes 1000-1001/2000"));
69754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("BB"));
69854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
69954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
70054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
70154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
70254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection range = openConnection(url);
70354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    range.addRequestProperty("Range", "bytes=1000-1001");
70454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("AA", readAscii(range));
70554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
70654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("BB", readAscii(openConnection(url)));
70754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
70854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
70954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void serverReturnsDocumentOlderThanCache() throws Exception {
71054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
71154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
71254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
71354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B")
71454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-4, TimeUnit.HOURS)));
71554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
71654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
71754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
71854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
71954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
72054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
72154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
72254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
72354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void nonIdentityEncodingAndConditionalCache() throws Exception {
72454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNonIdentityEncodingCached(
72554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
72654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
72754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
72854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
72954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void nonIdentityEncodingAndFullCache() throws Exception {
73054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNonIdentityEncodingCached(
73154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
73254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
73354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
73454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
73554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void assertNonIdentityEncodingCached(MockResponse response) throws Exception {
73654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
73754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        response.setBody(gzip("ABCABCABC".getBytes("UTF-8"))).addHeader("Content-Encoding: gzip"));
73854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
739166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
74054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
74154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
742166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
743166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    // At least three request/response pairs are required because after the first request is cached
744166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    // a different execution path might be taken. Thus modifications to the cache applied during
745166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    // the second request might not be visible until another request is performed.
746166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    assertEquals("ABCABCABC", readAscii(openConnection(server.getUrl("/"))));
74754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABCABCABC", readAscii(openConnection(server.getUrl("/"))));
74854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABCABCABC", readAscii(openConnection(server.getUrl("/"))));
74954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
75054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
751166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath  @Test public void notModifiedSpecifiesEncoding() throws Exception {
752166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    server.enqueue(new MockResponse()
753166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        .setBody(gzip("ABCABCABC".getBytes("UTF-8")))
754166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        .addHeader("Content-Encoding: gzip")
755166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
756166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
757166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    server.enqueue(new MockResponse()
758166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED)
759166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        .addHeader("Content-Encoding: gzip"));
760166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    server.enqueue(new MockResponse()
761166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        .setBody("DEFDEFDEF"));
762166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
763166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    server.play();
764166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    assertEquals("ABCABCABC", readAscii(openConnection(server.getUrl("/"))));
765166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    assertEquals("ABCABCABC", readAscii(openConnection(server.getUrl("/"))));
766166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    assertEquals("DEFDEFDEF", readAscii(openConnection(server.getUrl("/"))));
767166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath  }
768166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
76954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void expiresDateBeforeModifiedDate() throws Exception {
77054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertConditionallyCached(
77154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
77254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(-2, TimeUnit.HOURS)));
77354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
77454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
77554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMaxAge() throws IOException {
77654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
77754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
77854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(-1, TimeUnit.MINUTES))
77954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
78054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
78154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
78254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
78354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
78454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
78554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
78654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "max-age=30");
78754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection));
78854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
78954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
79054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMinFresh() throws IOException {
79154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
79254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=60")
79354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
79454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
79554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
79654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
79754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
79854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
79954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
80054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "min-fresh=120");
80154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection));
80254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
80354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
80454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMaxStale() throws IOException {
80554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
80654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=120")
80754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(-4, TimeUnit.MINUTES)));
80854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
80954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
81054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
81154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
81254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
81354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
81454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "max-stale=180");
81554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
81654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("110 HttpURLConnection \"Response is stale\"",
81754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        connection.getHeaderField("Warning"));
81854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
81954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
82054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMaxStaleNotHonoredWithMustRevalidate() throws IOException {
82154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
82254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=120, must-revalidate")
82354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(-4, TimeUnit.MINUTES)));
82454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
82554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
82654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
82754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
82854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
82954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
83054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "max-stale=180");
83154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection));
83254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
83354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
83454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestOnlyIfCachedWithNoResponseCached() throws IOException {
83554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // (no responses enqueued)
83654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
83754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
83854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(server.getUrl("/"));
83954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "only-if-cached");
84054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertGatewayTimeout(connection);
84154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
84254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
84354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestOnlyIfCachedWithFullResponseCached() throws IOException {
84454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
84554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=30")
84654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
84754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
84854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
84954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
85054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
85154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "only-if-cached");
852faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(connection));
85354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
85454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
85554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestOnlyIfCachedWithConditionalResponseCached() throws IOException {
85654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
85754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=30")
85854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(-1, TimeUnit.MINUTES)));
85954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
86054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
86154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
86254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(server.getUrl("/"));
86354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "only-if-cached");
86454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertGatewayTimeout(connection);
86554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
86654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
86754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestOnlyIfCachedWithUnhelpfulResponseCached() throws IOException {
86854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A"));
86954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
87054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
87154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
87254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(server.getUrl("/"));
87354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "only-if-cached");
87454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertGatewayTimeout(connection);
87554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
87654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
87754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestCacheControlNoCache() throws Exception {
87854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
87954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
88054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
88154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: max-age=60")
88254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setBody("A"));
88354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
88454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
88554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
88654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
88754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
88854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(url);
88954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setRequestProperty("Cache-Control", "no-cache");
89054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection));
89154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
89254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
89354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestPragmaNoCache() throws Exception {
89454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
89554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
89654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
89754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: max-age=60")
89854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setBody("A"));
89954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
90054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
90154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
90254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
90354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
90454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(url);
90554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setRequestProperty("Pragma", "no-cache");
90654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection));
90754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
90854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
90954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientSuppliedIfModifiedSinceWithCachedResult() throws Exception {
91054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response =
91154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("ETag: v3").addHeader("Cache-Control: max-age=0");
91254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String ifModifiedSinceDate = formatDate(-24, TimeUnit.HOURS);
91354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request =
91454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        assertClientSuppliedCondition(response, "If-Modified-Since", ifModifiedSinceDate);
91554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = request.getHeaders();
91654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + ifModifiedSinceDate));
91754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFalse(headers.contains("If-None-Match: v3"));
91854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
91954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
92054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientSuppliedIfNoneMatchSinceWithCachedResult() throws Exception {
92154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-3, TimeUnit.MINUTES);
92254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response = new MockResponse().addHeader("Last-Modified: " + lastModifiedDate)
92354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
92454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0");
92554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = assertClientSuppliedCondition(response, "If-None-Match", "v1");
92654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = request.getHeaders();
92754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-None-Match: v1"));
92854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFalse(headers.contains("If-Modified-Since: " + lastModifiedDate));
92954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
93054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
93154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private RecordedRequest assertClientSuppliedCondition(MockResponse seed, String conditionName,
93254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      String conditionValue) throws Exception {
93354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(seed.setBody("A"));
93454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
93554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
93654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
93754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
93854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
93954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
94054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(url);
94154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty(conditionName, conditionValue);
94254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, connection.getResponseCode());
94354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("", readAscii(connection));
94454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
94554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.takeRequest(); // seed
94654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return server.takeRequest();
94754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
94854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
94954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void setIfModifiedSince() throws Exception {
95054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Date since = new Date();
95154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A"));
95254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
95354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
95454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
95554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(url);
95654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setIfModifiedSince(since.getTime());
95754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
95854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = server.takeRequest();
95954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(request.getHeaders().contains("If-Modified-Since: " + formatDate(since)));
96054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
96154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
96254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientSuppliedConditionWithoutCachedResult() throws Exception {
96354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
96454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
96554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
96654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(server.getUrl("/"));
96754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String clientIfModifiedSince = formatDate(-24, TimeUnit.HOURS);
96854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("If-Modified-Since", clientIfModifiedSince);
96954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, connection.getResponseCode());
97054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("", readAscii(connection));
97154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
97254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
97354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void authorizationRequestHeaderPreventsCaching() throws Exception {
97454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
97554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-2, TimeUnit.MINUTES))
97654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: max-age=60")
97754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setBody("A"));
97854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
97954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
98054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
98154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
98254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(url);
98354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Authorization", "password");
98454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
98554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(url)));
98654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
98754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
98854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void authorizationResponseCachedWithSMaxAge() throws Exception {
98954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertAuthorizationRequestFullyCached(
99054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Cache-Control: s-maxage=60"));
99154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
99254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
99354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void authorizationResponseCachedWithPublic() throws Exception {
99454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertAuthorizationRequestFullyCached(new MockResponse().addHeader("Cache-Control: public"));
99554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
99654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
99754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void authorizationResponseCachedWithMustRevalidate() throws Exception {
99854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertAuthorizationRequestFullyCached(
99954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Cache-Control: must-revalidate"));
100054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
100154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
100254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public void assertAuthorizationRequestFullyCached(MockResponse response) throws Exception {
100354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response.addHeader("Cache-Control: max-age=60").setBody("A"));
100454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
100554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
100654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
100754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
100854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(url);
100954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Authorization", "password");
101054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
101154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
101254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
101354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
101454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void contentLocationDoesNotPopulateCache() throws Exception {
101554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
101654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Content-Location: /bar")
101754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
101854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
101954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
102054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
102154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/foo"))));
102254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(server.getUrl("/bar"))));
102354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
102454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
102554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void useCachesFalseDoesNotWriteToCache() throws Exception {
102654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
102754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Cache-Control: max-age=60").setBody("A").setBody("A"));
102854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
102954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
103054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
103154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
103254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setUseCaches(false);
103354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
103454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(server.getUrl("/"))));
103554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
103654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
103754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void useCachesFalseDoesNotReadFromCache() throws Exception {
103854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
103954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Cache-Control: max-age=60").setBody("A").setBody("A"));
104054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
104154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
104254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
104354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
104454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
104554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setUseCaches(false);
104654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection));
104754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
104854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
104954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void defaultUseCachesSetsInitialValueOnly() throws Exception {
105054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = new URL("http://localhost/");
105154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection c1 = openConnection(url);
105254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection c2 = openConnection(url);
105354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(c1.getDefaultUseCaches());
105454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    c1.setDefaultUseCaches(false);
105554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
105654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertTrue(c1.getUseCaches());
105754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertTrue(c2.getUseCaches());
105854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      URLConnection c3 = openConnection(url);
105954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertFalse(c3.getUseCaches());
106054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } finally {
106154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      c1.setDefaultUseCaches(true);
106254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
106354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
106454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
106554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectionIsReturnedToPoolAfterConditionalSuccess() throws Exception {
106654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
106754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
106854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
106954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
107054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
107154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
107254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
107354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/a"))));
107454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/a"))));
107554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(server.getUrl("/b"))));
107654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
107754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, server.takeRequest().getSequenceNumber());
107854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, server.takeRequest().getSequenceNumber());
107954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, server.takeRequest().getSequenceNumber());
108054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
108154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
10823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
10833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Confirms the cache implementation may determine the criteria for caching. In real caches
10843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * this would be the "Vary" headers.
10853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
10863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void cacheCanUseCriteriaBesidesVariantObeyed() throws Exception {
10873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(
10883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        new MockResponse().addHeader("Cache-Control: max-age=60")
10893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            .addHeader(InMemoryResponseCache.CACHE_VARIANT_HEADER, "A").setBody("A"));
10903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(
10913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        new MockResponse().addHeader("Cache-Control: max-age=60")
10923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            .addHeader(InMemoryResponseCache.CACHE_VARIANT_HEADER, "B").setBody("B"));
109354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
109454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
109554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
109654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(url);
10973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection1.addRequestProperty(InMemoryResponseCache.CACHE_VARIANT_HEADER, "A");
109854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
109954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(url);
11003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection2.addRequestProperty(InMemoryResponseCache.CACHE_VARIANT_HEADER, "A");
110154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
11023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(1, server.getRequestCount());
110354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
11043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    URLConnection connection3 = openConnection(url);
11053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection3.addRequestProperty(InMemoryResponseCache.CACHE_VARIANT_HEADER, "B");
11063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("B", readAscii(connection3));
11073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(2, server.getRequestCount());
110854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
11093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    URLConnection connection4 = openConnection(url);
11103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection4.addRequestProperty(InMemoryResponseCache.CACHE_VARIANT_HEADER, "A");
11113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("A", readAscii(connection4));
11123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(2, server.getRequestCount());
111354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
111454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
111554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cachePlusCookies() throws Exception {
111654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader(
111754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        "Set-Cookie: a=FIRST; domain=" + server.getCookieDomain() + ";")
111854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
111954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
112054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
112154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader(
112254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        "Set-Cookie: a=SECOND; domain=" + server.getCookieDomain() + ";")
112354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
112454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
112554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
11263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    CookieManager cookieManager = new CookieManager();
11273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    CookieManager.setDefault(cookieManager);
11283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
112954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
113054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
11313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertCookies(cookieManager, url, "a=FIRST");
113254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
11333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertCookies(cookieManager, url, "a=SECOND");
113454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
113554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
113654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getHeadersReturnsNetworkEndToEndHeaders() throws Exception {
113754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Allow: GET, HEAD")
113854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
113954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
114054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
114154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Allow: GET, HEAD, PUT")
114254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
114354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
114454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
114554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(server.getUrl("/"));
114654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
114754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET, HEAD", connection1.getHeaderField("Allow"));
114854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
114954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(server.getUrl("/"));
115054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
115154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET, HEAD, PUT", connection2.getHeaderField("Allow"));
115254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
115354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
115454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getHeadersReturnsCachedHopByHopHeaders() throws Exception {
115554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Transfer-Encoding: identity")
115654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
115754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
115854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
115954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Transfer-Encoding: none")
116054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
116154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
116254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
116354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(server.getUrl("/"));
116454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
116554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("identity", connection1.getHeaderField("Transfer-Encoding"));
116654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
116754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(server.getUrl("/"));
116854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
116954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("identity", connection2.getHeaderField("Transfer-Encoding"));
117054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
117154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
117254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getHeadersDeletesCached100LevelWarnings() throws Exception {
117354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Warning: 199 test danger")
117454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
117554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
117654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
117754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
117854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
117954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
118054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(server.getUrl("/"));
118154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
118254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("199 test danger", connection1.getHeaderField("Warning"));
118354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
118454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(server.getUrl("/"));
118554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
118654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(null, connection2.getHeaderField("Warning"));
118754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
118854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
118954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getHeadersRetainsCached200LevelWarnings() throws Exception {
119054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Warning: 299 test danger")
119154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
119254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
119354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
119454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
119554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
119654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
119754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(server.getUrl("/"));
119854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
119954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("299 test danger", connection1.getHeaderField("Warning"));
120054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
120154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(server.getUrl("/"));
120254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
120354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("299 test danger", connection2.getHeaderField("Warning"));
120454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
120554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
12063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public void assertCookies(CookieManager cookieManager, URL url, String... expectedCookies)
12073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      throws Exception {
120854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> actualCookies = new ArrayList<String>();
120954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (HttpCookie cookie : cookieManager.getCookieStore().get(url.toURI())) {
121054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      actualCookies.add(cookie.toString());
121154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
121254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(Arrays.asList(expectedCookies), actualCookies);
121354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
121454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
121554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cachePlusRange() throws Exception {
121654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().setResponseCode(HttpURLConnection.HTTP_PARTIAL)
121754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
121854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Content-Range: bytes 100-100/200")
121954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=60"));
122054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
122154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
12223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
12233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Equivalent to {@link HttpResponseCacheTest#conditionalHitUpdatesCache()}, except a Java
12243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * standard cache has no means to update the headers for an existing entry so the behavior is
12253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * different.
12263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
12273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void conditionalHitDoesNotUpdateCache() throws Exception {
12283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // A response that is cacheable, but with a short life.
122954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(0, TimeUnit.SECONDS))
123054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
123154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
12323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // A response that refers to the previous response, but is cacheable with a long life.
12333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Contains a header we can recognize as having come from the server.
123454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=30")
123554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Allow: GET, HEAD")
123654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
12373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // A response that is cacheable with a long life.
12383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse().setBody("B").addHeader("Cache-Control: max-age=30"));
12393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // A response that should never be requested.
12403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse().setBody("C"));
124154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
124254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
12433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // cache miss; seed the cache with an entry that will require a network hit to be sure it is
12443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // still valid
124554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection1 = openConnection(server.getUrl("/a"));
124654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
124754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(null, connection1.getHeaderField("Allow"));
124854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
12493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // conditional cache hit; The cached data should be returned, but the cache is not updated.
125054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection2 = openConnection(server.getUrl("/a"));
125154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_OK, connection2.getResponseCode());
125254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
125354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET, HEAD", connection2.getHeaderField("Allow"));
125454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
12553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // conditional cache hit; The server responds with new data. The cache is updated.
125654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection3 = openConnection(server.getUrl("/a"));
12573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("B", readAscii(connection3));
125854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
12593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // full cache hit; The data from connection3 has now replaced that from connection 1.
12603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpURLConnection connection4 = openConnection(server.getUrl("/a"));
12613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("B", readAscii(connection4));
12623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
12633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(3, server.getRequestCount());
126454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
126554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1266faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void responseSourceHeaderCached() throws IOException {
1267faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("A")
1268faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Cache-Control: max-age=30")
1269faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
1270faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.play();
1271faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1272faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
1273faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    URLConnection connection = openConnection(server.getUrl("/"));
1274faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    connection.addRequestProperty("Cache-Control", "only-if-cached");
1275faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(connection));
1276faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
12773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String source = connection.getHeaderField(OkHeaders.RESPONSE_SOURCE);
12783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(ResponseSource.CACHE + " 200", source);
1279faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
1280faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1281faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void responseSourceHeaderConditionalCacheFetched() throws IOException {
1282faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("A")
1283faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Cache-Control: max-age=30")
1284faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Date: " + formatDate(-31, TimeUnit.MINUTES)));
1285faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("B")
1286faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Cache-Control: max-age=30")
1287faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
1288faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.play();
1289faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1290faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
1291faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    HttpURLConnection connection = openConnection(server.getUrl("/"));
1292faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("B", readAscii(connection));
1293faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
12943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String source = connection.getHeaderField(OkHeaders.RESPONSE_SOURCE);
12953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(ResponseSource.CONDITIONAL_CACHE + " 200", source);
1296faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
1297faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1298faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void responseSourceHeaderConditionalCacheNotFetched() throws IOException {
1299faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("A")
1300faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Cache-Control: max-age=0")
1301faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
1302faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setResponseCode(304));
1303faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.play();
1304faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1305faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
1306faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    HttpURLConnection connection = openConnection(server.getUrl("/"));
1307faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(connection));
1308faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
13093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String source = connection.getHeaderField(OkHeaders.RESPONSE_SOURCE);
13103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(ResponseSource.CONDITIONAL_CACHE + " 304", source);
1311faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
1312faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1313faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void responseSourceHeaderFetched() throws IOException {
1314faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("A"));
1315faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.play();
1316faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1317faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    URLConnection connection = openConnection(server.getUrl("/"));
1318faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(connection));
1319faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
13203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String source = connection.getHeaderField(OkHeaders.RESPONSE_SOURCE);
13213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(ResponseSource.NETWORK + " 200", source);
1322faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
1323faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1324faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void emptyResponseHeaderNameFromCacheIsLenient() throws Exception {
1325faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse()
1326faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Cache-Control: max-age=120")
1327faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader(": A")
1328faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .setBody("body"));
1329faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.play();
13303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpURLConnection connection = openConnection(server.getUrl("/"));
1331faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", connection.getHeaderField(""));
1332faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
1333faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
133454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
133554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @param delta the offset from the current date to use. Negative
133654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * values yield dates in the past; positive values yield dates in the
133754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * future.
133854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
133954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private String formatDate(long delta, TimeUnit timeUnit) {
134054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return formatDate(new Date(System.currentTimeMillis() + timeUnit.toMillis(delta)));
134154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
134254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
134354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private String formatDate(Date date) {
134454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    DateFormat rfc1123 = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
1345166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    rfc1123.setTimeZone(TimeZone.getTimeZone("GMT"));
134654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return rfc1123.format(date);
134754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
134854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
134954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void addRequestBodyIfNecessary(String requestMethod, HttpURLConnection invalidate)
135054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throws IOException {
135154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (requestMethod.equals("POST") || requestMethod.equals("PUT")) {
135254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      invalidate.setDoOutput(true);
135354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      OutputStream requestBody = invalidate.getOutputStream();
135454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      requestBody.write('x');
135554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      requestBody.close();
135654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
135754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
135854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
135954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void assertNotCached(MockResponse response) throws Exception {
136054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response.setBody("A"));
136154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
136254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
136354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
136454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
136554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
136654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(url)));
136754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
136854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
136954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** @return the request with the conditional get headers. */
137054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private RecordedRequest assertConditionallyCached(MockResponse response) throws Exception {
137154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // scenario 1: condition succeeds
137254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response.setBody("A").setStatus("HTTP/1.1 200 A-OK"));
137354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
137454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
137554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // scenario 2: condition fails
137654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response.setBody("B").setStatus("HTTP/1.1 200 B-OK"));
137754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setStatus("HTTP/1.1 200 C-OK").setBody("C"));
137854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
137954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
138054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
138154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL valid = server.getUrl("/valid");
138254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection1 = openConnection(valid);
138354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
138454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_OK, connection1.getResponseCode());
138554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A-OK", connection1.getResponseMessage());
138654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection2 = openConnection(valid);
138754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
138854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_OK, connection2.getResponseCode());
138954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A-OK", connection2.getResponseMessage());
139054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
139154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL invalid = server.getUrl("/invalid");
139254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection3 = openConnection(invalid);
139354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection3));
139454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_OK, connection3.getResponseCode());
139554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B-OK", connection3.getResponseMessage());
139654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection4 = openConnection(invalid);
139754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("C", readAscii(connection4));
139854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_OK, connection4.getResponseCode());
139954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("C-OK", connection4.getResponseMessage());
140054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
140154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.takeRequest(); // regular get
140254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return server.takeRequest(); // conditional get
140354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
140454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
140554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void assertFullyCached(MockResponse response) throws Exception {
140654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response.setBody("A"));
140754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response.setBody("B"));
140854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
140954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
141054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
141154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
141254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
141354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
141454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
141554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
141654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Shortens the body of {@code response} but not the corresponding headers.
141754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Only useful to test how clients respond to the premature conclusion of
141854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * the HTTP body.
141954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
142054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private MockResponse truncateViolently(MockResponse response, int numBytesToKeep) {
142154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    response.setSocketPolicy(DISCONNECT_AT_END);
142254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = new ArrayList<String>(response.getHeaders());
142354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    response.setBody(Arrays.copyOfRange(response.getBody(), 0, numBytesToKeep));
142454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    response.getHeaders().clear();
142554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    response.getHeaders().addAll(headers);
142654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return response;
142754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
142854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
142954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
143054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Reads {@code count} characters from the stream. If the stream is
143154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * exhausted before {@code count} characters can be read, the remaining
143254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * characters are returned and the stream is closed.
143354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
143454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private String readAscii(URLConnection connection, int count) throws IOException {
143554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection httpConnection = (HttpURLConnection) connection;
143654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream in = httpConnection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST
143754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        ? connection.getInputStream() : httpConnection.getErrorStream();
143854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    StringBuilder result = new StringBuilder();
143954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 0; i < count; i++) {
144054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      int value = in.read();
144154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (value == -1) {
14427899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        in.close();
144354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        break;
144454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
144554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      result.append((char) value);
144654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
144754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return result.toString();
144854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
144954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
145054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private String readAscii(URLConnection connection) throws IOException {
145154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return readAscii(connection, Integer.MAX_VALUE);
145254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
145354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
145454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void reliableSkip(InputStream in, int length) throws IOException {
145554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    while (length > 0) {
145654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      length -= in.skip(length);
145754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
145854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
145954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
146054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void assertGatewayTimeout(HttpURLConnection connection) throws IOException {
146154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
146254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.getInputStream();
146354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
146454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (FileNotFoundException expected) {
146554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
146654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(504, connection.getResponseCode());
146754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(-1, connection.getErrorStream().read());
14683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(ResponseSource.NONE + " 504",
14693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        connection.getHeaderField(OkHeaders.RESPONSE_SOURCE));
147054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
147154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
147254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  enum TransferKind {
147354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    CHUNKED() {
147454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      @Override void setBody(MockResponse response, byte[] content, int chunkSize)
147554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          throws IOException {
147654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        response.setChunkedBody(content, chunkSize);
147754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
147854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    },
147954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    FIXED_LENGTH() {
148054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      @Override void setBody(MockResponse response, byte[] content, int chunkSize) {
148154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        response.setBody(content);
148254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
148354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    },
148454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    END_OF_STREAM() {
148554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      @Override void setBody(MockResponse response, byte[] content, int chunkSize) {
148654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        response.setBody(content);
14877899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        response.setSocketPolicy(DISCONNECT_AT_END);
148854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        for (Iterator<String> h = response.getHeaders().iterator(); h.hasNext(); ) {
148954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          if (h.next().startsWith("Content-Length:")) {
149054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            h.remove();
149154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            break;
149254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          }
14937899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        }
149454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
149554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    };
14967899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
149754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    abstract void setBody(MockResponse response, byte[] content, int chunkSize) throws IOException;
149854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
149954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    void setBody(MockResponse response, String content, int chunkSize) throws IOException {
150054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      setBody(response, content.getBytes("UTF-8"), chunkSize);
150154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
150254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
150354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
150454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private <T> List<T> toListOrNull(T[] arrayOrNull) {
150554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return arrayOrNull != null ? Arrays.asList(arrayOrNull) : null;
150654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
150754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
150854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Returns a gzipped copy of {@code bytes}. */
150954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public byte[] gzip(byte[] bytes) throws IOException {
151054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
151154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    OutputStream gzippedOut = new GZIPOutputStream(bytesOut);
151254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    gzippedOut.write(bytes);
151354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    gzippedOut.close();
151454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return bytesOut.toByteArray();
151554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
151654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
15173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private static class InsecureResponseCache extends ResponseCache {
15183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
15193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    private final ResponseCache delegate;
15203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
15213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    private InsecureResponseCache(ResponseCache delegate) {
15223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      this.delegate = delegate;
15233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
15243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
152554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public CacheRequest put(URI uri, URLConnection connection) throws IOException {
15263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return delegate.put(uri, connection);
152754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
152854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
152954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public CacheResponse get(URI uri, String requestMethod,
153054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        Map<String, List<String>> requestHeaders) throws IOException {
15313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      final CacheResponse response = delegate.get(uri, requestMethod, requestHeaders);
153254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (response instanceof SecureCacheResponse) {
153354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return new CacheResponse() {
153454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          @Override public InputStream getBody() throws IOException {
153554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            return response.getBody();
153654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          }
153754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          @Override public Map<String, List<String>> getHeaders() throws IOException {
153854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            return response.getHeaders();
153954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          }
15407899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        };
154154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
154254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return response;
15437899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
154454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
15453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
15463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
15473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * A trivial and non-thread-safe implementation of ResponseCache that uses an in-memory map to
15483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * cache GETs.
15493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
15503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private static class InMemoryResponseCache extends ResponseCache {
15513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
15523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    /** A request / response header that acts a bit like Vary but without the complexity. */
15533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    public static final String CACHE_VARIANT_HEADER = "CacheVariant";
15543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
15553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    private static class Key {
15563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      private final URI uri;
15573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      private final String cacheVariant;
15583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
15593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      private Key(URI uri, String cacheVariant) {
15603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        this.uri = uri;
15613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        this.cacheVariant = cacheVariant;
15623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
15633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
15643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      @Override
15653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      public boolean equals(Object o) {
15663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (this == o) {
15673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          return true;
15683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
15693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (o == null || getClass() != o.getClass()) {
15703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          return false;
15713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
15723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
15733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        Key key = (Key) o;
15743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
15753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (cacheVariant != null ? !cacheVariant.equals(key.cacheVariant)
15763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            : key.cacheVariant != null) {
15773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          return false;
15783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
15793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (!uri.equals(key.uri)) {
15803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          return false;
15813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
15823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
15833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return true;
15843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
15853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
15863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      @Override
15873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      public int hashCode() {
15883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        int result = uri.hashCode();
15893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        result = 31 * result + (cacheVariant != null ? cacheVariant.hashCode() : 0);
15903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return result;
15913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
15923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
15933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
15943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    private class Entry {
15953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
15963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      private final URI uri;
15973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      private final String cacheVariant;
15983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      private final String method;
15993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      private final Map<String, List<String>> responseHeaders;
16003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      private final String cipherSuite;
16013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      private final Certificate[] serverCertificates;
16023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      private final Certificate[] localCertificates;
16033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      private byte[] body;
16043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
16053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      public Entry(URI uri, URLConnection urlConnection) {
16063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        this.uri = uri;
16073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        HttpURLConnection httpUrlConnection = (HttpURLConnection) urlConnection;
16083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        method = httpUrlConnection.getRequestMethod();
16093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        cacheVariant = urlConnection.getHeaderField(CACHE_VARIANT_HEADER);
16103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        responseHeaders = urlConnection.getHeaderFields();
16113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (urlConnection instanceof HttpsURLConnection) {
16123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          HttpsURLConnection httpsURLConnection = (HttpsURLConnection) urlConnection;
16133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          cipherSuite = httpsURLConnection.getCipherSuite();
16143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          Certificate[] serverCertificates;
16153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          try {
16163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            serverCertificates = httpsURLConnection.getServerCertificates();
16173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          } catch (SSLPeerUnverifiedException e) {
16183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            serverCertificates = null;
16193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          }
16203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          this.serverCertificates = serverCertificates;
16213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          localCertificates = httpsURLConnection.getLocalCertificates();
16223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        } else {
16233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          cipherSuite = null;
16243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          serverCertificates = null;
16253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          localCertificates = null;
16263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
16273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
16283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
16293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      public CacheResponse asCacheResponse() {
16303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (!method.equals(this.method)) {
16313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          return null;
16323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
16333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
16343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        // Handle SSL
16353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (cipherSuite != null) {
16363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          return new SecureCacheResponse() {
16373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            @Override
16383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            public Map<String, List<String>> getHeaders() throws IOException {
16393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller              return responseHeaders;
16403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            }
16413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
16423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            @Override
16433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            public InputStream getBody() throws IOException {
16443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller              return new ByteArrayInputStream(body);
16453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            }
16463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
16473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            @Override
16483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            public String getCipherSuite() {
16493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller              return cipherSuite;
16503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            }
16513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
16523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            @Override
16533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            public List<Certificate> getLocalCertificateChain() {
16543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller              return localCertificates == null ? null : Arrays.asList(localCertificates);
16553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            }
16563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
16573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            @Override
16583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            public List<Certificate> getServerCertificateChain() throws SSLPeerUnverifiedException {
16593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller              if (serverCertificates == null) {
16603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller                throw new SSLPeerUnverifiedException("Test implementation");
16613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller              }
16623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller              return Arrays.asList(serverCertificates);
16633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            }
16643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
16653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            @Override
16663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
16673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller              throw new UnsupportedOperationException();
16683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            }
16693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
16703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            @Override
16713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            public Principal getLocalPrincipal() {
16723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller              throw new UnsupportedOperationException();
16733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            }
16743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          };
16753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        } else {
16763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          return new CacheResponse() {
16773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            @Override
16783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            public Map<String, List<String>> getHeaders() throws IOException {
16793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller              return responseHeaders;
16803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            }
16813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
16823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            @Override
16833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            public InputStream getBody() throws IOException {
16843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller              return new ByteArrayInputStream(body);
16853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            }
16863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          };
16873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
16883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
16893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
16903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      public CacheRequest asCacheRequest() {
16913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return new CacheRequest() {
16923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          @Override
16933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          public OutputStream getBody() throws IOException {
16943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            return new ByteArrayOutputStream() {
16953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller              @Override
16963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller              public void close() throws IOException {
16973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller                super.close();
16983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller                body = toByteArray();
16993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller                cache.put(Entry.this.key(), Entry.this);
17003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller              }
17013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            };
17023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          }
17033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
17043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          @Override
17053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          public void abort() {
17063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            // No-op: close() puts the item in the cache, abort need not do anything.
17073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          }
17083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        };
17093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
17103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
17113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      private Key key() {
17123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return new Key(uri, cacheVariant);
17133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
17143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
17153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
17163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    private Map<Key, Entry> cache = new HashMap<Key, Entry>();
17173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
17183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override
17193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    public CacheResponse get(URI uri, String method, Map<String, List<String>> requestHeaders)
17203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        throws IOException {
17213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
17223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (!"GET".equals(method)) {
17233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return null;
17243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
17253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
17263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      String cacheVariant =
17273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          requestHeaders.containsKey(CACHE_VARIANT_HEADER)
17283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller              ? requestHeaders.get(CACHE_VARIANT_HEADER).get(0) : null;
17293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      Key key = new Key(uri, cacheVariant);
17303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      Entry entry = cache.get(key);
17313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (entry == null) {
17323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return null;
17333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
17343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return entry.asCacheResponse();
17353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
17363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
17373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override
17383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    public CacheRequest put(URI uri, URLConnection urlConnection) throws IOException {
17393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (!"GET".equals(((HttpURLConnection) urlConnection).getRequestMethod())) {
17403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return null;
17413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
17423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
17433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      Entry entry = new Entry(uri, urlConnection);
17443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return entry.asCacheRequest();
17453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
17463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
17477899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath}
1748