UrlConnectionCacheTest.java revision faf49723fb689c626f69876e718c58018eff8ee7
17899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath/*
27899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * Copyright (C) 2011 The Android Open Source Project
37899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *
47899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * Licensed under the Apache License, Version 2.0 (the "License");
57899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * you may not use this file except in compliance with the License.
67899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * You may obtain a copy of the License at
77899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *
87899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *      http://www.apache.org/licenses/LICENSE-2.0
97899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *
107899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * Unless required by applicable law or agreed to in writing, software
117899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS,
127899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * See the License for the specific language governing permissions and
147899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * limitations under the License.
157899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath */
167899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
172231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonpackage com.squareup.okhttp.internal.http;
187899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
197899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport com.google.mockwebserver.MockResponse;
207899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport com.google.mockwebserver.MockWebServer;
217899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport com.google.mockwebserver.RecordedRequest;
22faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport com.squareup.okhttp.HttpResponseCache;
232231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.OkHttpClient;
24faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport com.squareup.okhttp.ResponseSource;
252231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.internal.SslContextBuilder;
267899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.BufferedReader;
277899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.ByteArrayOutputStream;
287899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.File;
297899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.FileNotFoundException;
307899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.IOException;
317899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.InputStream;
327899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.InputStreamReader;
337899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.OutputStream;
347899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.CacheRequest;
357899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.CacheResponse;
367899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.CookieHandler;
377899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.CookieManager;
387899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.HttpCookie;
397899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.HttpURLConnection;
402231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.InetAddress;
417899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.ResponseCache;
427899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.SecureCacheResponse;
437899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.URI;
447899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.URISyntaxException;
457899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.URL;
467899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.net.URLConnection;
472231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.UnknownHostException;
482231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.security.GeneralSecurityException;
492231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.security.Principal;
502231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.security.cert.Certificate;
517899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.text.DateFormat;
527899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.text.SimpleDateFormat;
537899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.ArrayList;
547899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.Arrays;
557899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.Collections;
567899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.Date;
577899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.Iterator;
587899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.List;
597899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.Locale;
607899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.Map;
617899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.TimeZone;
627899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.UUID;
637899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.concurrent.TimeUnit;
647899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.concurrent.atomic.AtomicInteger;
657899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.concurrent.atomic.AtomicReference;
667899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.util.zip.GZIPOutputStream;
672231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport javax.net.ssl.HostnameVerifier;
682231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport javax.net.ssl.HttpsURLConnection;
692231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport javax.net.ssl.SSLContext;
702231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport javax.net.ssl.SSLSession;
712231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport org.junit.After;
7254cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport org.junit.Before;
7354cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport org.junit.Test;
7454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
7554cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_END;
762231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.assertEquals;
772231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.assertFalse;
782231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.assertNotNull;
792231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.assertNull;
802231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.assertTrue;
812231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.fail;
827899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
8354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson/** Android's HttpResponseCacheTest. */
842231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonpublic final class HttpResponseCacheTest {
8554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static final HostnameVerifier NULL_HOSTNAME_VERIFIER = new HostnameVerifier() {
8654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public boolean verify(String s, SSLSession sslSession) {
8754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return true;
8854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
8954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  };
9054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private final OkHttpClient client = new OkHttpClient();
9154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private MockWebServer server = new MockWebServer();
9254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private HttpResponseCache cache;
9354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private final CookieManager cookieManager = new CookieManager();
9454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
9554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static final SSLContext sslContext;
9654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  static {
9754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
9854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      sslContext = new SslContextBuilder(InetAddress.getLocalHost().getHostName()).build();
9954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (GeneralSecurityException e) {
10054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new RuntimeException(e);
10154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (UnknownHostException e) {
10254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new RuntimeException(e);
10354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
10454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
10554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
10654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Before public void setUp() throws Exception {
10754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String tmp = System.getProperty("java.io.tmpdir");
10854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    File cacheDir = new File(tmp, "HttpCache-" + UUID.randomUUID());
10954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    cache = new HttpResponseCache(cacheDir, Integer.MAX_VALUE);
11054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    ResponseCache.setDefault(cache);
11154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    CookieHandler.setDefault(cookieManager);
11254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
11354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
11454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @After public void tearDown() throws Exception {
11554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.shutdown();
11654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    ResponseCache.setDefault(null);
117faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    cache.delete();
11854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    CookieHandler.setDefault(null);
11954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
12054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
12154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private HttpURLConnection openConnection(URL url) {
12254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return client.open(url);
12354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
12454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
12554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
12654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Test that response caching is consistent with the RI and the spec.
12754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4
12854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
12954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCachingByResponseCode() throws Exception {
13054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Test each documented HTTP/1.1 code, plus the first unused value in each range.
13154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
13254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
13354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // We can't test 100 because it's not really a response.
13454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // assertCached(false, 100);
13554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 101);
13654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 102);
13754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(true, 200);
13854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 201);
13954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 202);
14054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(true, 203);
14154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 204);
14254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 205);
14354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 206); // we don't cache partial responses
14454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 207);
14554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(true, 300);
14654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(true, 301);
14754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 302; i <= 308; ++i) {
14854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertCached(false, i);
14954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
15054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 400; i <= 406; ++i) {
15154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertCached(false, i);
15254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
15354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // (See test_responseCaching_407.)
15454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 408);
15554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(false, 409);
15654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // (See test_responseCaching_410.)
15754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 411; i <= 418; ++i) {
15854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertCached(false, i);
15954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
16054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 500; i <= 506; ++i) {
16154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertCached(false, i);
16254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
16354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
16454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
16554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
16654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Response code 407 should only come from proxy servers. Android's client
16754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * throws if it is sent by an origin server.
16854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
16954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void originServerSends407() throws Exception {
17054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(407));
17154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
17254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
17354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
17454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection conn = openConnection(url);
17554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
17654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      conn.getResponseCode();
17754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
17854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException expected) {
17954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
18054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
18154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
18254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCaching_410() throws Exception {
18354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // the HTTP spec permits caching 410s, but the RI doesn't.
18454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCached(true, 410);
18554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
18654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
18754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void assertCached(boolean shouldPut, int responseCode) throws Exception {
18854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server = new MockWebServer();
18954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response =
19054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
19154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
19254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setResponseCode(responseCode)
19354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setBody("ABCDE")
19454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("WWW-Authenticate: challenge");
19554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (responseCode == HttpURLConnection.HTTP_PROXY_AUTH) {
19654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      response.addHeader("Proxy-Authenticate: Basic realm=\"protected area\"");
19754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
19854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      response.addHeader("WWW-Authenticate: Basic realm=\"protected area\"");
19954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
20054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
20154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
20254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
20354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
20454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection conn = openConnection(url);
20554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(responseCode, conn.getResponseCode());
20654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
20754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // exhaust the content stream
20854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    readAscii(conn);
20954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
21054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    CacheResponse cached =
21154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        cache.get(url.toURI(), "GET", Collections.<String, List<String>>emptyMap());
21254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (shouldPut) {
21354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertNotNull(Integer.toString(responseCode), cached);
21454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      cached.getBody().close();
21554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
21654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertNull(Integer.toString(responseCode), cached);
21754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
21854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.shutdown(); // tearDown() isn't sufficient; this test starts multiple servers
21954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
22054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
22154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
22254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Test that we can interrogate the response when the cache is being
22354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * populated. http://code.google.com/p/android/issues/detail?id=7787
22454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
22554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCacheCallbackApis() throws Exception {
22654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    final String body = "ABCDE";
22754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    final AtomicInteger cacheCount = new AtomicInteger();
22854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
22954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
23054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().setStatus("HTTP/1.1 200 Fantastic").addHeader("fgh: ijk").setBody(body));
23154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
23254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
23354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    ResponseCache.setDefault(new ResponseCache() {
23454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      @Override public CacheResponse get(URI uri, String requestMethod,
23554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          Map<String, List<String>> requestHeaders) throws IOException {
23654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return null;
23754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
23854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      @Override public CacheRequest put(URI uri, URLConnection conn) throws IOException {
23954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        HttpURLConnection httpConnection = (HttpURLConnection) conn;
2402231db3e6bb54447a9b14cf004a6cb03c373651cjwilson        try {
24154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          httpConnection.getRequestProperties();
24254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          fail();
24354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        } catch (IllegalStateException expected) {
2447899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        }
2457899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        try {
24654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          httpConnection.addRequestProperty("K", "V");
24754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          fail();
24854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        } catch (IllegalStateException expected) {
2497899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        }
25054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        assertEquals("HTTP/1.1 200 Fantastic", httpConnection.getHeaderField(null));
25154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        assertEquals(Arrays.asList("HTTP/1.1 200 Fantastic"),
25254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            httpConnection.getHeaderFields().get(null));
25354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        assertEquals(200, httpConnection.getResponseCode());
25454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        assertEquals("Fantastic", httpConnection.getResponseMessage());
25554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        assertEquals(body.length(), httpConnection.getContentLength());
25654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        assertEquals("ijk", httpConnection.getHeaderField("fgh"));
2577899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        try {
25854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          httpConnection.getInputStream(); // the RI doesn't forbid this, but it should
25954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          fail();
2607899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        } catch (IOException expected) {
2617899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        }
26254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        cacheCount.incrementAndGet();
26354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return null;
26454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
26554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    });
26654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
26754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
26854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(url);
26954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(body, readAscii(connection));
27054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cacheCount.get());
27154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
27254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
27354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCachingAndInputStreamSkipWithFixedLength() throws IOException {
27454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testResponseCaching(TransferKind.FIXED_LENGTH);
27554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
27654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
27754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCachingAndInputStreamSkipWithChunkedEncoding() throws IOException {
27854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testResponseCaching(TransferKind.CHUNKED);
27954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
28054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
28154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCachingAndInputStreamSkipWithNoLengthHeaders() throws IOException {
28254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testResponseCaching(TransferKind.END_OF_STREAM);
28354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
28454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
28554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
28654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * HttpURLConnection.getInputStream().skip(long) causes ResponseCache corruption
28754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * http://code.google.com/p/android/issues/detail?id=8175
28854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
28954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testResponseCaching(TransferKind transferKind) throws IOException {
29054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response =
29154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
29254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
29354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setStatus("HTTP/1.1 200 Fantastic");
29454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transferKind.setBody(response, "I love puppies but hate spiders", 1);
29554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
29654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
29754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
29854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Make sure that calling skip() doesn't omit bytes from the cache.
29954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection urlConnection = openConnection(server.getUrl("/"));
30054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream in = urlConnection.getInputStream();
30154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("I love ", readAscii(urlConnection, "I love ".length()));
30254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    reliableSkip(in, "puppies but hate ".length());
30354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("spiders", readAscii(urlConnection, "spiders".length()));
30454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(-1, in.read());
30554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    in.close();
30654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getWriteSuccessCount());
30754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, cache.getWriteAbortCount());
30854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
30954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    urlConnection = openConnection(server.getUrl("/")); // cached!
31054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    in = urlConnection.getInputStream();
31154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("I love puppies but hate spiders",
31254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        readAscii(urlConnection, "I love puppies but hate spiders".length()));
31354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(200, urlConnection.getResponseCode());
31454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("Fantastic", urlConnection.getResponseMessage());
31554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
31654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(-1, in.read());
31754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    in.close();
31854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getWriteSuccessCount());
31954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, cache.getWriteAbortCount());
32054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, cache.getRequestCount());
32154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getHitCount());
32254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
32354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
32454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void secureResponseCaching() throws IOException {
32554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.useHttps(sslContext.getSocketFactory(), false);
32654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
32754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
32854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("ABC"));
32954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
33054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
33154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpsURLConnection connection = (HttpsURLConnection) client.open(server.getUrl("/"));
33254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setSSLSocketFactory(sslContext.getSocketFactory());
33354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
33454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(connection));
33554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
33654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // OpenJDK 6 fails on this line, complaining that the connection isn't open yet
33754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String suite = connection.getCipherSuite();
33854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<Certificate> localCerts = toListOrNull(connection.getLocalCertificates());
33954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<Certificate> serverCerts = toListOrNull(connection.getServerCertificates());
34054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Principal peerPrincipal = connection.getPeerPrincipal();
34154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Principal localPrincipal = connection.getLocalPrincipal();
34254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
34354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection = (HttpsURLConnection) client.open(server.getUrl("/")); // cached!
34454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setSSLSocketFactory(sslContext.getSocketFactory());
34554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
34654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(connection));
34754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
34854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, cache.getRequestCount());
34954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getNetworkCount());
35054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getHitCount());
35154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
35254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(suite, connection.getCipherSuite());
35354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(localCerts, toListOrNull(connection.getLocalCertificates()));
35454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(serverCerts, toListOrNull(connection.getServerCertificates()));
35554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(peerPrincipal, connection.getPeerPrincipal());
35654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(localPrincipal, connection.getLocalPrincipal());
35754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
35854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
35954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cacheReturnsInsecureResponseForSecureRequest() throws IOException {
36054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.useHttps(sslContext.getSocketFactory(), false);
36154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("ABC"));
36254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("DEF"));
36354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
36454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
36554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    ResponseCache.setDefault(new InsecureResponseCache());
36654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
36754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpsURLConnection connection1 = (HttpsURLConnection) client.open(server.getUrl("/"));
36854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.setSSLSocketFactory(sslContext.getSocketFactory());
36954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
37054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(connection1));
37154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
37254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Not cached!
37354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpsURLConnection connection2 = (HttpsURLConnection) client.open(server.getUrl("/"));
37454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.setSSLSocketFactory(sslContext.getSocketFactory());
37554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
37654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("DEF", readAscii(connection2));
37754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
37854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
37954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCachingAndRedirects() throws Exception {
38054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
38154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
38254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
38354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Location: /foo"));
38454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
38554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
38654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("ABC"));
38754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("DEF"));
38854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
38954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
39054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(server.getUrl("/"));
39154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(connection));
39254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
39354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection = openConnection(server.getUrl("/")); // cached!
39454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(connection));
39554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
39654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(4, cache.getRequestCount()); // 2 requests + 2 redirects
39754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, cache.getNetworkCount());
39854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, cache.getHitCount());
39954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
40054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
40154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void redirectToCachedResult() throws Exception {
40254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60").setBody("ABC"));
40354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
40454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Location: /foo"));
40554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("DEF"));
40654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
40754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
40854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(openConnection(server.getUrl("/foo"))));
40954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request1 = server.takeRequest();
41054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET /foo HTTP/1.1", request1.getRequestLine());
41154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, request1.getSequenceNumber());
41254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
41354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(openConnection(server.getUrl("/bar"))));
41454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request2 = server.takeRequest();
41554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET /bar HTTP/1.1", request2.getRequestLine());
41654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, request2.getSequenceNumber());
41754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
41854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // an unrelated request should reuse the pooled connection
41954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("DEF", readAscii(openConnection(server.getUrl("/baz"))));
42054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request3 = server.takeRequest();
42154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET /baz HTTP/1.1", request3.getRequestLine());
42254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, request3.getSequenceNumber());
42354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
42454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
42554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void secureResponseCachingAndRedirects() throws IOException {
42654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.useHttps(sslContext.getSocketFactory(), false);
42754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
42854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
42954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
43054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Location: /foo"));
43154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
43254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
43354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("ABC"));
43454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("DEF"));
43554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
43654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
43754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpsURLConnection connection1 = (HttpsURLConnection) client.open(server.getUrl("/"));
43854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.setSSLSocketFactory(sslContext.getSocketFactory());
43954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
44054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(connection1));
44154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
44254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Cached!
44354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpsURLConnection connection2 = (HttpsURLConnection) client.open(server.getUrl("/"));
44454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.setSSLSocketFactory(sslContext.getSocketFactory());
44554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
44654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(connection2));
44754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
44854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(4, cache.getRequestCount()); // 2 direct + 2 redirect = 4
44954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, cache.getHitCount());
45054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
45154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
45254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCacheRequestHeaders() throws IOException, URISyntaxException {
45354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("ABC"));
45454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
45554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
45654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    final AtomicReference<Map<String, List<String>>> requestHeadersRef =
45754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new AtomicReference<Map<String, List<String>>>();
45854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    ResponseCache.setDefault(new ResponseCache() {
45954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      @Override public CacheResponse get(URI uri, String requestMethod,
46054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          Map<String, List<String>> requestHeaders) throws IOException {
46154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        requestHeadersRef.set(requestHeaders);
46254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return null;
46354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
46454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      @Override public CacheRequest put(URI uri, URLConnection conn) throws IOException {
46554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return null;
46654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
46754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    });
46854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
46954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
47054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection urlConnection = openConnection(url);
47154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    urlConnection.addRequestProperty("A", "android");
47254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    readAscii(urlConnection);
47354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(Arrays.asList("android"), requestHeadersRef.get().get("A"));
47454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
47554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
47654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void serverDisconnectsPrematurelyWithContentLengthHeader() throws IOException {
47754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testServerPrematureDisconnect(TransferKind.FIXED_LENGTH);
47854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
47954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
48054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void serverDisconnectsPrematurelyWithChunkedEncoding() throws IOException {
48154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testServerPrematureDisconnect(TransferKind.CHUNKED);
48254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
48354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
48454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void serverDisconnectsPrematurelyWithNoLengthHeaders() throws IOException {
48554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Intentionally empty. This case doesn't make sense because there's no
48654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // such thing as a premature disconnect when the disconnect itself
48754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // indicates the end of the data stream.
48854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
48954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
49054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testServerPrematureDisconnect(TransferKind transferKind) throws IOException {
49154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response = new MockResponse();
49254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transferKind.setBody(response, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 16);
49354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(truncateViolently(response, 16));
49454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("Request #2"));
49554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
49654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
49754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    BufferedReader reader = new BufferedReader(
49854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new InputStreamReader(openConnection(server.getUrl("/")).getInputStream()));
49954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABCDE", reader.readLine());
50054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
50154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      reader.readLine();
50254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail("This implementation silently ignored a truncated HTTP body.");
50354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException expected) {
50454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } finally {
50554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      reader.close();
50654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
50754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
50854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getWriteAbortCount());
50954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, cache.getWriteSuccessCount());
51054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
51154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("Request #2", readAscii(connection));
51254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getWriteAbortCount());
51354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getWriteSuccessCount());
51454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
51554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
51654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientPrematureDisconnectWithContentLengthHeader() throws IOException {
51754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testClientPrematureDisconnect(TransferKind.FIXED_LENGTH);
51854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
51954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
52054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientPrematureDisconnectWithChunkedEncoding() throws IOException {
52154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testClientPrematureDisconnect(TransferKind.CHUNKED);
52254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
52354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
52454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientPrematureDisconnectWithNoLengthHeaders() throws IOException {
52554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testClientPrematureDisconnect(TransferKind.END_OF_STREAM);
52654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
52754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
52854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testClientPrematureDisconnect(TransferKind transferKind) throws IOException {
52954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Setting a low transfer speed ensures that stream discarding will time out.
53054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response = new MockResponse().setBytesPerSecond(6);
53154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transferKind.setBody(response, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 1024);
53254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
53354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("Request #2"));
53454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
53554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
53654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
53754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream in = connection.getInputStream();
53854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABCDE", readAscii(connection, 5));
53954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    in.close();
54054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
54154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      in.read();
54254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail("Expected an IOException because the stream is closed.");
54354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException expected) {
54454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
54554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
54654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getWriteAbortCount());
54754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, cache.getWriteSuccessCount());
54854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection = openConnection(server.getUrl("/"));
54954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("Request #2", readAscii(connection));
55054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getWriteAbortCount());
55154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getWriteSuccessCount());
55254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
55354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
55454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void defaultExpirationDateFullyCachedForLessThan24Hours() throws Exception {
55554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //      last modified: 105 seconds ago
55654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //             served:   5 seconds ago
55754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //   default lifetime: (105 - 5) / 10 = 10 seconds
55854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //            expires:  10 seconds from served date = 5 seconds from now
55954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
56054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-105, TimeUnit.SECONDS))
56154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Date: " + formatDate(-5, TimeUnit.SECONDS))
56254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setBody("A"));
56354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
56454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
56554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
56654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
56754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(url);
56854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
56954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNull(connection.getHeaderField("Warning"));
57054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
57154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
57254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void defaultExpirationDateConditionallyCached() throws Exception {
57354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //      last modified: 115 seconds ago
57454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //             served:  15 seconds ago
57554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //   default lifetime: (115 - 15) / 10 = 10 seconds
57654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //            expires:  10 seconds from served date = 5 seconds ago
57754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-115, TimeUnit.SECONDS);
57854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest = assertConditionallyCached(
57954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + lastModifiedDate)
58054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Date: " + formatDate(-15, TimeUnit.SECONDS)));
58154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = conditionalRequest.getHeaders();
58254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
58354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
58454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
58554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void defaultExpirationDateFullyCachedForMoreThan24Hours() throws Exception {
58654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //      last modified: 105 days ago
58754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //             served:   5 days ago
58854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //   default lifetime: (105 - 5) / 10 = 10 days
58954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    //            expires:  10 days from served date = 5 days from now
59054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-105, TimeUnit.DAYS))
59154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(-5, TimeUnit.DAYS))
59254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
59354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
59454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
59554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
59654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
59754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
59854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("113 HttpURLConnection \"Heuristic expiration\"",
59954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        connection.getHeaderField("Warning"));
60054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
60154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
60254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void noDefaultExpirationForUrlsWithQueryString() throws Exception {
60354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
60454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-105, TimeUnit.SECONDS))
60554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Date: " + formatDate(-5, TimeUnit.SECONDS))
60654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setBody("A"));
60754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
60854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
60954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
61054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/?foo=bar");
61154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
61254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(url)));
61354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
61454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
61554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void expirationDateInThePastWithLastModifiedHeader() throws Exception {
61654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
61754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest = assertConditionallyCached(
61854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + lastModifiedDate)
61954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
62054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = conditionalRequest.getHeaders();
62154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
62254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
62354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
62454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void expirationDateInThePastWithNoLastModifiedHeader() throws Exception {
62554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
62654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
62754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
62854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void expirationDateInTheFuture() throws Exception {
62954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(new MockResponse().addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
63054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
63154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
63254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgePreferredWithMaxAgeAndExpires() throws Exception {
63354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(new MockResponse().addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
63454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS))
63554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=60"));
63654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
63754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
63854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgeInThePastWithDateAndLastModifiedHeaders() throws Exception {
63954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
64054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest = assertConditionallyCached(
64154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Date: " + formatDate(-120, TimeUnit.SECONDS))
64254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Last-Modified: " + lastModifiedDate)
64354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: max-age=60"));
64454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = conditionalRequest.getHeaders();
64554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
64654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
64754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
64854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgeInThePastWithDateHeaderButNoLastModifiedHeader() throws Exception {
64954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Chrome interprets max-age relative to the local clock. Both our cache
65054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // and Firefox both use the earlier of the local and server's clock.
65154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Date: " + formatDate(-120, TimeUnit.SECONDS))
65254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=60"));
65354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
65454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
65554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgeInTheFutureWithDateHeader() throws Exception {
65654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(new MockResponse().addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
65754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=60"));
65854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
65954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
66054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgeInTheFutureWithNoDateHeader() throws Exception {
66154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(new MockResponse().addHeader("Cache-Control: max-age=60"));
66254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
66354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
66454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgeWithLastModifiedButNoServedDate() throws Exception {
66554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(
66654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
66754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: max-age=60"));
66854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
66954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
67054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgeInTheFutureWithDateAndLastModifiedHeaders() throws Exception {
67154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(
67254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
67354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
67454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: max-age=60"));
67554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
67654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
67754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgePreferredOverLowerSharedMaxAge() throws Exception {
67854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(new MockResponse().addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
67954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: s-maxage=60")
68054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=180"));
68154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
68254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
68354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void maxAgePreferredOverHigherMaxAge() throws Exception {
68454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
68554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: s-maxage=180")
68654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=60"));
68754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
68854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
68954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMethodOptionsIsNotCached() throws Exception {
69054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRequestMethod("OPTIONS", false);
69154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
69254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
69354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMethodGetIsCached() throws Exception {
69454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRequestMethod("GET", true);
69554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
69654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
69754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMethodHeadIsNotCached() throws Exception {
69854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // We could support this but choose not to for implementation simplicity
69954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRequestMethod("HEAD", false);
70054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
70154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
70254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMethodPostIsNotCached() throws Exception {
70354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // We could support this but choose not to for implementation simplicity
70454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRequestMethod("POST", false);
70554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
70654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
70754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMethodPutIsNotCached() throws Exception {
70854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRequestMethod("PUT", false);
70954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
71054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
71154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMethodDeleteIsNotCached() throws Exception {
71254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRequestMethod("DELETE", false);
71354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
71454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
71554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMethodTraceIsNotCached() throws Exception {
71654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRequestMethod("TRACE", false);
71754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
71854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
71954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testRequestMethod(String requestMethod, boolean expectCached) throws Exception {
72054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // 1. seed the cache (potentially)
72154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // 2. expect a cache hit or miss
72254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
72354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("X-Response-ID: 1"));
72454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("X-Response-ID: 2"));
72554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
72654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
72754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
72854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
72954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection request1 = openConnection(url);
73054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    request1.setRequestMethod(requestMethod);
73154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    addRequestBodyIfNecessary(requestMethod, request1);
73254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("1", request1.getHeaderField("X-Response-ID"));
73354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
73454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection request2 = openConnection(url);
73554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (expectCached) {
73654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertEquals("1", request1.getHeaderField("X-Response-ID"));
73754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
73854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertEquals("2", request2.getHeaderField("X-Response-ID"));
73954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
74054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
74154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
74254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void postInvalidatesCache() throws Exception {
74354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testMethodInvalidates("POST");
74454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
74554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
74654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void putInvalidatesCache() throws Exception {
74754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testMethodInvalidates("PUT");
74854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
74954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
75054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void deleteMethodInvalidatesCache() throws Exception {
75154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testMethodInvalidates("DELETE");
75254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
75354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
75454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testMethodInvalidates(String requestMethod) throws Exception {
75554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // 1. seed the cache
75654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // 2. invalidate it
75754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // 3. expect a cache miss
75854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
75954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().setBody("A").addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
76054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
76154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("C"));
76254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
76354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
76454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
76554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
76654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
76754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
76854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection invalidate = openConnection(url);
76954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    invalidate.setRequestMethod(requestMethod);
77054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    addRequestBodyIfNecessary(requestMethod, invalidate);
77154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(invalidate));
77254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
77354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("C", readAscii(openConnection(url)));
77454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
77554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
77654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void etag() throws Exception {
77754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest =
77854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        assertConditionallyCached(new MockResponse().addHeader("ETag: v1"));
77954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(conditionalRequest.getHeaders().contains("If-None-Match: v1"));
78054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
78154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
78254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void etagAndExpirationDateInThePast() throws Exception {
78354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
78454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest = assertConditionallyCached(
78554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("ETag: v1")
78654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Last-Modified: " + lastModifiedDate)
78754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
78854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = conditionalRequest.getHeaders();
78954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-None-Match: v1"));
79054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
79154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
79254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
79354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void etagAndExpirationDateInTheFuture() throws Exception {
79454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFullyCached(new MockResponse().addHeader("ETag: v1")
79554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
79654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
79754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
79854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
79954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cacheControlNoCache() throws Exception {
80054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Cache-Control: no-cache"));
80154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
80254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
80354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cacheControlNoCacheAndExpirationDateInTheFuture() throws Exception {
80454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
80554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest = assertConditionallyCached(
80654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + lastModifiedDate)
80754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
80854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: no-cache"));
80954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = conditionalRequest.getHeaders();
81054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
81154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
81254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
81354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void pragmaNoCache() throws Exception {
81454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Pragma: no-cache"));
81554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
81654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
81754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void pragmaNoCacheAndExpirationDateInTheFuture() throws Exception {
81854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-2, TimeUnit.HOURS);
81954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest conditionalRequest = assertConditionallyCached(
82054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + lastModifiedDate)
82154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
82254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Pragma: no-cache"));
82354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = conditionalRequest.getHeaders();
82454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + lastModifiedDate));
82554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
82654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
82754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cacheControlNoStore() throws Exception {
82854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Cache-Control: no-store"));
82954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
83054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
83154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cacheControlNoStoreAndExpirationDateInTheFuture() throws Exception {
83254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
83354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
83454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: no-store"));
83554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
83654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
83754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void partialRangeResponsesDoNotCorruptCache() throws Exception {
83854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // 1. request a range
83954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // 2. request a full document, expecting a cache miss
84054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("AA")
84154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_PARTIAL)
84254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
84354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Content-Range: bytes 1000-1001/2000"));
84454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("BB"));
84554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
84654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
84754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
84854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
84954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection range = openConnection(url);
85054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    range.addRequestProperty("Range", "bytes=1000-1001");
85154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("AA", readAscii(range));
85254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
85354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("BB", readAscii(openConnection(url)));
85454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
85554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
85654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void serverReturnsDocumentOlderThanCache() throws Exception {
85754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
85854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
85954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
86054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B")
86154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-4, TimeUnit.HOURS)));
86254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
86354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
86454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
86554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
86654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
86754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
86854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
86954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
87054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void nonIdentityEncodingAndConditionalCache() throws Exception {
87154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNonIdentityEncodingCached(
87254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
87354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(-1, TimeUnit.HOURS)));
87454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
87554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
87654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void nonIdentityEncodingAndFullCache() throws Exception {
87754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNonIdentityEncodingCached(
87854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
87954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
88054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
88154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
88254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void assertNonIdentityEncodingCached(MockResponse response) throws Exception {
88354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
88454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        response.setBody(gzip("ABCABCABC".getBytes("UTF-8"))).addHeader("Content-Encoding: gzip"));
88554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
88654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
88754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
88854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABCABCABC", readAscii(openConnection(server.getUrl("/"))));
88954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABCABCABC", readAscii(openConnection(server.getUrl("/"))));
89054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
89154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
89254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void expiresDateBeforeModifiedDate() throws Exception {
89354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertConditionallyCached(
89454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
89554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Expires: " + formatDate(-2, TimeUnit.HOURS)));
89654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
89754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
89854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMaxAge() throws IOException {
89954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
90054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-2, TimeUnit.HOURS))
90154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(-1, TimeUnit.MINUTES))
90254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)));
90354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
90454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
90554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
90654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
90754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
90854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
90954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "max-age=30");
91054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection));
91154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
91254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
91354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMinFresh() throws IOException {
91454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
91554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=60")
91654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
91754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
91854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
91954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
92054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
92154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
92254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
92354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "min-fresh=120");
92454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection));
92554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
92654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
92754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMaxStale() throws IOException {
92854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
92954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=120")
93054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(-4, TimeUnit.MINUTES)));
93154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
93254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
93354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
93454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
93554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
93654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
93754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "max-stale=180");
93854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
93954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("110 HttpURLConnection \"Response is stale\"",
94054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        connection.getHeaderField("Warning"));
94154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
94254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
94354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestMaxStaleNotHonoredWithMustRevalidate() throws IOException {
94454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
94554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=120, must-revalidate")
94654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(-4, TimeUnit.MINUTES)));
94754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
94854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
94954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
95054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
95154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
95254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
95354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "max-stale=180");
95454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection));
95554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
95654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
95754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestOnlyIfCachedWithNoResponseCached() throws IOException {
95854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // (no responses enqueued)
95954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
96054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
96154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(server.getUrl("/"));
96254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "only-if-cached");
96354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertGatewayTimeout(connection);
96454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
96554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
96654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestOnlyIfCachedWithFullResponseCached() throws IOException {
96754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
96854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=30")
96954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
97054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
97154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
97254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
97354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
97454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "only-if-cached");
975faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(connection));
97654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
97754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
97854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestOnlyIfCachedWithConditionalResponseCached() throws IOException {
97954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A")
98054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=30")
98154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(-1, TimeUnit.MINUTES)));
98254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
98354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
98454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
98554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(server.getUrl("/"));
98654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "only-if-cached");
98754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertGatewayTimeout(connection);
98854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
98954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
99054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestOnlyIfCachedWithUnhelpfulResponseCached() throws IOException {
99154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A"));
99254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
99354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
99454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
99554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(server.getUrl("/"));
99654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Cache-Control", "only-if-cached");
99754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertGatewayTimeout(connection);
99854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
99954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
100054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestCacheControlNoCache() throws Exception {
100154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
100254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
100354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
100454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: max-age=60")
100554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setBody("A"));
100654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
100754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
100854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
100954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
101054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
101154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(url);
101254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setRequestProperty("Cache-Control", "no-cache");
101354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection));
101454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
101554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
101654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestPragmaNoCache() throws Exception {
101754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
101854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-120, TimeUnit.SECONDS))
101954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Date: " + formatDate(0, TimeUnit.SECONDS))
102054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: max-age=60")
102154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setBody("A"));
102254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
102354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
102454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
102554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
102654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
102754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(url);
102854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setRequestProperty("Pragma", "no-cache");
102954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection));
103054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
103154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
103254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientSuppliedIfModifiedSinceWithCachedResult() throws Exception {
103354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response =
103454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("ETag: v3").addHeader("Cache-Control: max-age=0");
103554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String ifModifiedSinceDate = formatDate(-24, TimeUnit.HOURS);
103654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request =
103754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        assertClientSuppliedCondition(response, "If-Modified-Since", ifModifiedSinceDate);
103854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = request.getHeaders();
103954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-Modified-Since: " + ifModifiedSinceDate));
104054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFalse(headers.contains("If-None-Match: v3"));
104154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
104254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
104354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientSuppliedIfNoneMatchSinceWithCachedResult() throws Exception {
104454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String lastModifiedDate = formatDate(-3, TimeUnit.MINUTES);
104554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response = new MockResponse().addHeader("Last-Modified: " + lastModifiedDate)
104654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(-2, TimeUnit.MINUTES))
104754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0");
104854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = assertClientSuppliedCondition(response, "If-None-Match", "v1");
104954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = request.getHeaders();
105054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(headers.contains("If-None-Match: v1"));
105154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFalse(headers.contains("If-Modified-Since: " + lastModifiedDate));
105254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
105354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
105454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private RecordedRequest assertClientSuppliedCondition(MockResponse seed, String conditionName,
105554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      String conditionValue) throws Exception {
105654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(seed.setBody("A"));
105754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
105854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
105954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
106054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
106154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
106254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
106354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(url);
106454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty(conditionName, conditionValue);
106554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, connection.getResponseCode());
106654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("", readAscii(connection));
106754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
106854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.takeRequest(); // seed
106954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return server.takeRequest();
107054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
107154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
107254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void setIfModifiedSince() throws Exception {
107354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Date since = new Date();
107454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A"));
107554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
107654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
107754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
107854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(url);
107954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setIfModifiedSince(since.getTime());
108054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
108154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = server.takeRequest();
108254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(request.getHeaders().contains("If-Modified-Since: " + formatDate(since)));
108354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
108454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
108554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientSuppliedConditionWithoutCachedResult() throws Exception {
108654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
108754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
108854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
108954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection = openConnection(server.getUrl("/"));
109054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String clientIfModifiedSince = formatDate(-24, TimeUnit.HOURS);
109154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("If-Modified-Since", clientIfModifiedSince);
109254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, connection.getResponseCode());
109354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("", readAscii(connection));
109454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
109554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
109654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void authorizationRequestHeaderPreventsCaching() throws Exception {
109754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
109854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Last-Modified: " + formatDate(-2, TimeUnit.MINUTES))
109954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .addHeader("Cache-Control: max-age=60")
110054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            .setBody("A"));
110154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
110254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
110354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
110454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
110554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(url);
110654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Authorization", "password");
110754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
110854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(url)));
110954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
111054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
111154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void authorizationResponseCachedWithSMaxAge() throws Exception {
111254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertAuthorizationRequestFullyCached(
111354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Cache-Control: s-maxage=60"));
111454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
111554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
111654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void authorizationResponseCachedWithPublic() throws Exception {
111754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertAuthorizationRequestFullyCached(new MockResponse().addHeader("Cache-Control: public"));
111854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
111954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
112054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void authorizationResponseCachedWithMustRevalidate() throws Exception {
112154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertAuthorizationRequestFullyCached(
112254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Cache-Control: must-revalidate"));
112354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
112454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
112554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public void assertAuthorizationRequestFullyCached(MockResponse response) throws Exception {
112654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response.addHeader("Cache-Control: max-age=60").setBody("A"));
112754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
112854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
112954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
113054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
113154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(url);
113254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Authorization", "password");
113354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
113454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
113554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
113654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
113754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void contentLocationDoesNotPopulateCache() throws Exception {
113854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
113954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Content-Location: /bar")
114054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
114154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
114254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
114354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
114454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/foo"))));
114554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(server.getUrl("/bar"))));
114654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
114754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
114854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void useCachesFalseDoesNotWriteToCache() throws Exception {
114954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
115054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Cache-Control: max-age=60").setBody("A").setBody("A"));
115154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
115254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
115354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
115454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
115554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setUseCaches(false);
115654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection));
115754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(server.getUrl("/"))));
115854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
115954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
116054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void useCachesFalseDoesNotReadFromCache() throws Exception {
116154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
116254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().addHeader("Cache-Control: max-age=60").setBody("A").setBody("A"));
116354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
116454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
116554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
116654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
116754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = openConnection(server.getUrl("/"));
116854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setUseCaches(false);
116954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection));
117054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
117154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
117254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void defaultUseCachesSetsInitialValueOnly() throws Exception {
117354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = new URL("http://localhost/");
117454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection c1 = openConnection(url);
117554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection c2 = openConnection(url);
117654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(c1.getDefaultUseCaches());
117754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    c1.setDefaultUseCaches(false);
117854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
117954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertTrue(c1.getUseCaches());
118054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertTrue(c2.getUseCaches());
118154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      URLConnection c3 = openConnection(url);
118254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertFalse(c3.getUseCaches());
118354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } finally {
118454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      c1.setDefaultUseCaches(true);
118554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
118654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
118754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
118854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectionIsReturnedToPoolAfterConditionalSuccess() throws Exception {
118954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
119054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
119154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
119254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
119354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
119454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
119554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
119654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/a"))));
119754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/a"))));
119854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(server.getUrl("/b"))));
119954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
120054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, server.takeRequest().getSequenceNumber());
120154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, server.takeRequest().getSequenceNumber());
120254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, server.takeRequest().getSequenceNumber());
120354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
120454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
120554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void statisticsConditionalCacheMiss() throws Exception {
120654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
120754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
120854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
120954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
121054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("C"));
121154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
121254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
121354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
121454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getRequestCount());
121554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getNetworkCount());
121654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, cache.getHitCount());
121754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(server.getUrl("/"))));
121854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("C", readAscii(openConnection(server.getUrl("/"))));
121954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(3, cache.getRequestCount());
122054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(3, cache.getNetworkCount());
122154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, cache.getHitCount());
122254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
122354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
122454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void statisticsConditionalCacheHit() throws Exception {
122554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
122654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
122754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
122854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
122954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
123054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
123154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
123254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
123354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getRequestCount());
123454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getNetworkCount());
123554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, cache.getHitCount());
123654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
123754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
123854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(3, cache.getRequestCount());
123954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(3, cache.getNetworkCount());
124054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, cache.getHitCount());
124154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
124254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
124354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void statisticsFullCacheHit() throws Exception {
124454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60").setBody("A"));
124554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
124654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
124754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
124854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getRequestCount());
124954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getNetworkCount());
125054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, cache.getHitCount());
125154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
125254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
125354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(3, cache.getRequestCount());
125454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, cache.getNetworkCount());
125554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, cache.getHitCount());
125654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
125754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
125854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyMatchesChangedRequestHeaderField() throws Exception {
125954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
126054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Accept-Language")
126154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
126254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
126354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
126454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
126554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
126654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection frConnection = openConnection(url);
126754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    frConnection.addRequestProperty("Accept-Language", "fr-CA");
126854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(frConnection));
126954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
127054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection enConnection = openConnection(url);
127154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    enConnection.addRequestProperty("Accept-Language", "en-US");
127254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(enConnection));
127354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
127454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
127554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyMatchesUnchangedRequestHeaderField() throws Exception {
127654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
127754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Accept-Language")
127854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
127954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
128054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
128154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
128254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
128354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(url);
128454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Language", "fr-CA");
128554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
128654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(url);
128754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("Accept-Language", "fr-CA");
128854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
128954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
129054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
129154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyMatchesAbsentRequestHeaderField() throws Exception {
129254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
129354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Foo")
129454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
129554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
129654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
129754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
129854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
129954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
130054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
130154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
130254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyMatchesAddedRequestHeaderField() throws Exception {
130354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
130454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Foo")
130554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
130654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
130754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
130854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
130954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
131054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection fooConnection = openConnection(server.getUrl("/"));
131154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    fooConnection.addRequestProperty("Foo", "bar");
131254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(fooConnection));
131354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
131454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
131554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyMatchesRemovedRequestHeaderField() throws Exception {
131654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
131754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Foo")
131854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
131954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
132054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
132154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
132254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection fooConnection = openConnection(server.getUrl("/"));
132354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    fooConnection.addRequestProperty("Foo", "bar");
132454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(fooConnection));
132554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(server.getUrl("/"))));
132654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
132754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
132854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyFieldsAreCaseInsensitive() throws Exception {
132954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
133054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: ACCEPT-LANGUAGE")
133154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
133254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
133354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
133454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
133554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
133654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(url);
133754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Language", "fr-CA");
133854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
133954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(url);
134054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("accept-language", "fr-CA");
134154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
134254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
134354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
134454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyMultipleFieldsWithMatch() throws Exception {
134554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
134654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Accept-Language, Accept-Charset")
134754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Accept-Encoding")
134854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
134954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
135054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
135154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
135254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
135354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(url);
135454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Language", "fr-CA");
135554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Charset", "UTF-8");
135654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Encoding", "identity");
135754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
135854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(url);
135954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("Accept-Language", "fr-CA");
136054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("Accept-Charset", "UTF-8");
136154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("Accept-Encoding", "identity");
136254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
136354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
136454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
136554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyMultipleFieldsWithNoMatch() throws Exception {
136654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
136754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Accept-Language, Accept-Charset")
136854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Accept-Encoding")
136954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
137054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
137154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
137254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
137354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
137454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection frConnection = openConnection(url);
137554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    frConnection.addRequestProperty("Accept-Language", "fr-CA");
137654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    frConnection.addRequestProperty("Accept-Charset", "UTF-8");
137754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    frConnection.addRequestProperty("Accept-Encoding", "identity");
137854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(frConnection));
137954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection enConnection = openConnection(url);
138054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    enConnection.addRequestProperty("Accept-Language", "en-CA");
138154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    enConnection.addRequestProperty("Accept-Charset", "UTF-8");
138254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    enConnection.addRequestProperty("Accept-Encoding", "identity");
138354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(enConnection));
138454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
138554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
138654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyMultipleFieldValuesWithMatch() throws Exception {
138754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
138854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Accept-Language")
138954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
139054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
139154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
139254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
139354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
139454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(url);
139554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Language", "fr-CA, fr-FR");
139654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Language", "en-US");
139754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
139854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
139954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(url);
140054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("Accept-Language", "fr-CA, fr-FR");
140154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("Accept-Language", "en-US");
140254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
140354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
140454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
140554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyMultipleFieldValuesWithNoMatch() throws Exception {
140654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
140754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Accept-Language")
140854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
140954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
141054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
141154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
141254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
141354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(url);
141454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Language", "fr-CA, fr-FR");
141554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Language", "en-US");
141654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
141754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
141854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(url);
141954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("Accept-Language", "fr-CA");
142054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("Accept-Language", "en-US");
142154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection2));
142254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
142354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
142454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyAsterisk() throws Exception {
142554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
142654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: *")
142754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
142854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
142954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
143054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
143154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
143254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(server.getUrl("/"))));
143354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
143454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
143554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void varyAndHttps() throws Exception {
143654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.useHttps(sslContext.getSocketFactory(), false);
143754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=60")
143854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Vary: Accept-Language")
143954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
144054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
144154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
144254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
144354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
144454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpsURLConnection connection1 = (HttpsURLConnection) client.open(url);
144554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.setSSLSocketFactory(sslContext.getSocketFactory());
144654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
144754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Language", "en-US");
144854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
144954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
145054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpsURLConnection connection2 = (HttpsURLConnection) client.open(url);
145154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.setSSLSocketFactory(sslContext.getSocketFactory());
145254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.setHostnameVerifier(NULL_HOSTNAME_VERIFIER);
145354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.addRequestProperty("Accept-Language", "en-US");
145454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
145554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
145654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
145754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cachePlusCookies() throws Exception {
145854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader(
145954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        "Set-Cookie: a=FIRST; domain=" + server.getCookieDomain() + ";")
146054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
146154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
146254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
146354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader(
146454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        "Set-Cookie: a=SECOND; domain=" + server.getCookieDomain() + ";")
146554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
146654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
146754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
146854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
146954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
147054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCookies(url, "a=FIRST");
147154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
147254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertCookies(url, "a=SECOND");
147354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
147454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
147554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getHeadersReturnsNetworkEndToEndHeaders() throws Exception {
147654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Allow: GET, HEAD")
147754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
147854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
147954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
148054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Allow: GET, HEAD, PUT")
148154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
148254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
148354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
148454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(server.getUrl("/"));
148554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
148654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET, HEAD", connection1.getHeaderField("Allow"));
148754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
148854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(server.getUrl("/"));
148954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
149054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET, HEAD, PUT", connection2.getHeaderField("Allow"));
149154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
149254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
149354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getHeadersReturnsCachedHopByHopHeaders() throws Exception {
149454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Transfer-Encoding: identity")
149554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
149654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
149754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
149854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Transfer-Encoding: none")
149954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
150054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
150154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
150254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(server.getUrl("/"));
150354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
150454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("identity", connection1.getHeaderField("Transfer-Encoding"));
150554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
150654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(server.getUrl("/"));
150754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
150854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("identity", connection2.getHeaderField("Transfer-Encoding"));
150954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
151054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
151154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getHeadersDeletesCached100LevelWarnings() throws Exception {
151254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Warning: 199 test danger")
151354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
151454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
151554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
151654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
151754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
151854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
151954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(server.getUrl("/"));
152054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
152154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("199 test danger", connection1.getHeaderField("Warning"));
152254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
152354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(server.getUrl("/"));
152454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
152554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(null, connection2.getHeaderField("Warning"));
152654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
152754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
152854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getHeadersRetainsCached200LevelWarnings() throws Exception {
152954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Warning: 299 test danger")
153054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
153154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
153254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
153354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
153454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
153554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
153654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection1 = openConnection(server.getUrl("/"));
153754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
153854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("299 test danger", connection1.getHeaderField("Warning"));
153954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
154054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection2 = openConnection(server.getUrl("/"));
154154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
154254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("299 test danger", connection2.getHeaderField("Warning"));
154354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
154454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
154554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public void assertCookies(URL url, String... expectedCookies) throws Exception {
154654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> actualCookies = new ArrayList<String>();
154754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (HttpCookie cookie : cookieManager.getCookieStore().get(url.toURI())) {
154854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      actualCookies.add(cookie.toString());
154954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
155054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(Arrays.asList(expectedCookies), actualCookies);
155154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
155254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
155354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cachePlusRange() throws Exception {
155454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNotCached(new MockResponse().setResponseCode(HttpURLConnection.HTTP_PARTIAL)
155554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
155654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Content-Range: bytes 100-100/200")
155754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=60"));
155854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
155954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
156054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void conditionalHitUpdatesCache() throws Exception {
156154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Last-Modified: " + formatDate(0, TimeUnit.SECONDS))
156254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Cache-Control: max-age=0")
156354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("A"));
156454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Cache-Control: max-age=30")
156554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Allow: GET, HEAD")
156654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
156754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
156854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
156954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
157054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // cache miss; seed the cache
157154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection1 = openConnection(server.getUrl("/a"));
157254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
157354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(null, connection1.getHeaderField("Allow"));
157454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
157554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // conditional cache hit; update the cache
157654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection2 = openConnection(server.getUrl("/a"));
157754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_OK, connection2.getResponseCode());
157854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
157954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET, HEAD", connection2.getHeaderField("Allow"));
158054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
158154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // full cache hit
158254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection3 = openConnection(server.getUrl("/a"));
158354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection3));
158454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET, HEAD", connection3.getHeaderField("Allow"));
158554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
158654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, server.getRequestCount());
158754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
158854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1589faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void responseSourceHeaderCached() throws IOException {
1590faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("A")
1591faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Cache-Control: max-age=30")
1592faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
1593faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.play();
1594faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1595faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
1596faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    URLConnection connection = openConnection(server.getUrl("/"));
1597faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    connection.addRequestProperty("Cache-Control", "only-if-cached");
1598faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(connection));
1599faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1600faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    String source = connection.getHeaderField(ResponseHeaders.RESPONSE_SOURCE);
1601faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals(ResponseSource.CACHE.toString() + " 200", source);
1602faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
1603faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1604faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void responseSourceHeaderConditionalCacheFetched() throws IOException {
1605faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("A")
1606faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Cache-Control: max-age=30")
1607faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Date: " + formatDate(-31, TimeUnit.MINUTES)));
1608faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("B")
1609faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Cache-Control: max-age=30")
1610faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
1611faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.play();
1612faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1613faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
1614faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    HttpURLConnection connection = openConnection(server.getUrl("/"));
1615faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("B", readAscii(connection));
1616faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1617faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    String source = connection.getHeaderField(ResponseHeaders.RESPONSE_SOURCE);
1618faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals(ResponseSource.CONDITIONAL_CACHE.toString() + " 200", source);
1619faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
1620faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1621faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void responseSourceHeaderConditionalCacheNotFetched() throws IOException {
1622faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("A")
1623faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Cache-Control: max-age=0")
1624faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Date: " + formatDate(0, TimeUnit.MINUTES)));
1625faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setResponseCode(304));
1626faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.play();
1627faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1628faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(openConnection(server.getUrl("/"))));
1629faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    HttpURLConnection connection = openConnection(server.getUrl("/"));
1630faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(connection));
1631faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1632faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    String source = connection.getHeaderField(ResponseHeaders.RESPONSE_SOURCE);
1633faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals(ResponseSource.CONDITIONAL_CACHE.toString() + " 304", source);
1634faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
1635faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1636faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void responseSourceHeaderFetched() throws IOException {
1637faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("A"));
1638faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.play();
1639faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1640faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    URLConnection connection = openConnection(server.getUrl("/"));
1641faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", readAscii(connection));
1642faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1643faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    String source = connection.getHeaderField(ResponseHeaders.RESPONSE_SOURCE);
1644faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals(ResponseSource.NETWORK.toString() + " 200", source);
1645faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
1646faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
1647faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void emptyResponseHeaderNameFromCacheIsLenient() throws Exception {
1648faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse()
1649faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Cache-Control: max-age=120")
1650faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader(": A")
1651faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .setBody("body"));
1652faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.play();
1653faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    HttpURLConnection connection = client.open(server.getUrl("/"));
1654faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("A", connection.getHeaderField(""));
1655faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
1656faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
165754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
165854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @param delta the offset from the current date to use. Negative
165954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * values yield dates in the past; positive values yield dates in the
166054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * future.
166154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
166254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private String formatDate(long delta, TimeUnit timeUnit) {
166354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return formatDate(new Date(System.currentTimeMillis() + timeUnit.toMillis(delta)));
166454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
166554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
166654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private String formatDate(Date date) {
166754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    DateFormat rfc1123 = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
166854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    rfc1123.setTimeZone(TimeZone.getTimeZone("UTC"));
166954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return rfc1123.format(date);
167054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
167154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
167254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void addRequestBodyIfNecessary(String requestMethod, HttpURLConnection invalidate)
167354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throws IOException {
167454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (requestMethod.equals("POST") || requestMethod.equals("PUT")) {
167554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      invalidate.setDoOutput(true);
167654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      OutputStream requestBody = invalidate.getOutputStream();
167754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      requestBody.write('x');
167854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      requestBody.close();
167954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
168054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
168154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
168254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void assertNotCached(MockResponse response) throws Exception {
168354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response.setBody("A"));
168454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("B"));
168554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
168654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
168754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
168854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
168954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(openConnection(url)));
169054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
169154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
169254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** @return the request with the conditional get headers. */
169354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private RecordedRequest assertConditionallyCached(MockResponse response) throws Exception {
169454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // scenario 1: condition succeeds
169554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response.setBody("A").setStatus("HTTP/1.1 200 A-OK"));
169654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
169754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
169854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // scenario 2: condition fails
169954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response.setBody("B").setStatus("HTTP/1.1 200 B-OK"));
170054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setStatus("HTTP/1.1 200 C-OK").setBody("C"));
170154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
170254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
170354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
170454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL valid = server.getUrl("/valid");
170554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection1 = openConnection(valid);
170654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection1));
170754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_OK, connection1.getResponseCode());
170854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A-OK", connection1.getResponseMessage());
170954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection2 = openConnection(valid);
171054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection2));
171154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_OK, connection2.getResponseCode());
171254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A-OK", connection2.getResponseMessage());
171354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
171454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL invalid = server.getUrl("/invalid");
171554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection3 = openConnection(invalid);
171654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B", readAscii(connection3));
171754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_OK, connection3.getResponseCode());
171854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("B-OK", connection3.getResponseMessage());
171954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection4 = openConnection(invalid);
172054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("C", readAscii(connection4));
172154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(HttpURLConnection.HTTP_OK, connection4.getResponseCode());
172254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("C-OK", connection4.getResponseMessage());
172354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
172454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.takeRequest(); // regular get
172554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return server.takeRequest(); // conditional get
172654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
172754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
172854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void assertFullyCached(MockResponse response) throws Exception {
172954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response.setBody("A"));
173054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response.setBody("B"));
173154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.play();
173254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
173354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
173454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
173554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(openConnection(url)));
173654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
173754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
173854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
173954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Shortens the body of {@code response} but not the corresponding headers.
174054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Only useful to test how clients respond to the premature conclusion of
174154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * the HTTP body.
174254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
174354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private MockResponse truncateViolently(MockResponse response, int numBytesToKeep) {
174454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    response.setSocketPolicy(DISCONNECT_AT_END);
174554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> headers = new ArrayList<String>(response.getHeaders());
174654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    response.setBody(Arrays.copyOfRange(response.getBody(), 0, numBytesToKeep));
174754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    response.getHeaders().clear();
174854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    response.getHeaders().addAll(headers);
174954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return response;
175054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
175154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
175254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
175354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Reads {@code count} characters from the stream. If the stream is
175454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * exhausted before {@code count} characters can be read, the remaining
175554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * characters are returned and the stream is closed.
175654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
175754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private String readAscii(URLConnection connection, int count) throws IOException {
175854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection httpConnection = (HttpURLConnection) connection;
175954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream in = httpConnection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST
176054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        ? connection.getInputStream() : httpConnection.getErrorStream();
176154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    StringBuilder result = new StringBuilder();
176254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 0; i < count; i++) {
176354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      int value = in.read();
176454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (value == -1) {
17657899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        in.close();
176654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        break;
176754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
176854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      result.append((char) value);
176954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
177054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return result.toString();
177154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
177254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
177354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private String readAscii(URLConnection connection) throws IOException {
177454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return readAscii(connection, Integer.MAX_VALUE);
177554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
177654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
177754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void reliableSkip(InputStream in, int length) throws IOException {
177854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    while (length > 0) {
177954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      length -= in.skip(length);
178054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
178154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
178254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
178354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void assertGatewayTimeout(HttpURLConnection connection) throws IOException {
178454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
178554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.getInputStream();
178654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
178754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (FileNotFoundException expected) {
178854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
178954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(504, connection.getResponseCode());
179054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(-1, connection.getErrorStream().read());
179154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
179254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
179354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  enum TransferKind {
179454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    CHUNKED() {
179554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      @Override void setBody(MockResponse response, byte[] content, int chunkSize)
179654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          throws IOException {
179754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        response.setChunkedBody(content, chunkSize);
179854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
179954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    },
180054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    FIXED_LENGTH() {
180154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      @Override void setBody(MockResponse response, byte[] content, int chunkSize) {
180254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        response.setBody(content);
180354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
180454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    },
180554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    END_OF_STREAM() {
180654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      @Override void setBody(MockResponse response, byte[] content, int chunkSize) {
180754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        response.setBody(content);
18087899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        response.setSocketPolicy(DISCONNECT_AT_END);
180954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        for (Iterator<String> h = response.getHeaders().iterator(); h.hasNext(); ) {
181054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          if (h.next().startsWith("Content-Length:")) {
181154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            h.remove();
181254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            break;
181354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          }
18147899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        }
181554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
181654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    };
18177899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
181854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    abstract void setBody(MockResponse response, byte[] content, int chunkSize) throws IOException;
181954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
182054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    void setBody(MockResponse response, String content, int chunkSize) throws IOException {
182154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      setBody(response, content.getBytes("UTF-8"), chunkSize);
182254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
182354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
182454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
182554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private <T> List<T> toListOrNull(T[] arrayOrNull) {
182654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return arrayOrNull != null ? Arrays.asList(arrayOrNull) : null;
182754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
182854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
182954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Returns a gzipped copy of {@code bytes}. */
183054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public byte[] gzip(byte[] bytes) throws IOException {
183154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
183254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    OutputStream gzippedOut = new GZIPOutputStream(bytesOut);
183354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    gzippedOut.write(bytes);
183454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    gzippedOut.close();
183554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return bytesOut.toByteArray();
183654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
183754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
183854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private class InsecureResponseCache extends ResponseCache {
183954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public CacheRequest put(URI uri, URLConnection connection) throws IOException {
184054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return cache.put(uri, connection);
184154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
184254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
184354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public CacheResponse get(URI uri, String requestMethod,
184454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        Map<String, List<String>> requestHeaders) throws IOException {
184554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      final CacheResponse response = cache.get(uri, requestMethod, requestHeaders);
184654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (response instanceof SecureCacheResponse) {
184754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return new CacheResponse() {
184854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          @Override public InputStream getBody() throws IOException {
184954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            return response.getBody();
185054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          }
185154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          @Override public Map<String, List<String>> getHeaders() throws IOException {
185254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            return response.getHeaders();
185354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          }
18547899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        };
185554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
185654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return response;
18577899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
185854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
18597899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath}
1860