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