1 /* 2 * Copyright (C) 2014 Square, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.squareup.okhttp.internal.huc; 17 18 import com.squareup.okhttp.AbstractResponseCache; 19 import com.squareup.okhttp.OkHttpClient; 20 import com.squareup.okhttp.OkUrlFactory; 21 import com.squareup.okhttp.internal.Internal; 22 import com.squareup.okhttp.internal.SslContextBuilder; 23 import com.squareup.okhttp.mockwebserver.MockResponse; 24 import com.squareup.okhttp.mockwebserver.MockWebServer; 25 import com.squareup.okhttp.testing.RecordingHostnameVerifier; 26 import java.io.IOException; 27 import java.net.CacheRequest; 28 import java.net.CacheResponse; 29 import java.net.HttpURLConnection; 30 import java.net.ResponseCache; 31 import java.net.URI; 32 import java.net.URL; 33 import java.net.URLConnection; 34 import java.nio.charset.StandardCharsets; 35 import java.util.Collections; 36 import java.util.List; 37 import java.util.Map; 38 import javax.net.ssl.HostnameVerifier; 39 import javax.net.ssl.HttpsURLConnection; 40 import javax.net.ssl.SSLContext; 41 import okio.Buffer; 42 import org.junit.After; 43 import org.junit.Before; 44 import org.junit.Test; 45 46 import static org.junit.Assert.assertArrayEquals; 47 import static org.junit.Assert.assertEquals; 48 import static org.junit.Assert.assertFalse; 49 import static org.junit.Assert.assertTrue; 50 51 /** 52 * A white-box test for {@link CacheAdapter}. See also: 53 * <ul> 54 * <li>{@link ResponseCacheTest} for black-box tests that check that {@link ResponseCache} 55 * classes are called correctly by OkHttp.</li> 56 * <li>{@link JavaApiConverterTest} for tests that check Java API classes / OkHttp conversion 57 * logic. </li> 58 * </ul> 59 */ 60 public class CacheAdapterTest { 61 private SSLContext sslContext = SslContextBuilder.localhost(); 62 private HostnameVerifier hostnameVerifier = new RecordingHostnameVerifier(); 63 private MockWebServer server; 64 private OkHttpClient client; 65 private HttpURLConnection connection; 66 67 @Before public void setUp() throws Exception { 68 server = new MockWebServer(); 69 client = new OkHttpClient(); 70 } 71 72 @After public void tearDown() throws Exception { 73 if (connection != null) { 74 connection.disconnect(); 75 } 76 server.shutdown(); 77 } 78 79 @Test public void get_httpGet() throws Exception { 80 final URL serverUrl = configureServer(new MockResponse()); 81 assertEquals("http", serverUrl.getProtocol()); 82 83 ResponseCache responseCache = new AbstractResponseCache() { 84 @Override 85 public CacheResponse get(URI uri, String method, Map<String, List<String>> headers) throws IOException { 86 assertEquals(toUri(serverUrl), uri); 87 assertEquals("GET", method); 88 assertTrue("Arbitrary standard header not present", headers.containsKey("User-Agent")); 89 assertEquals(Collections.singletonList("value1"), headers.get("key1")); 90 return null; 91 } 92 }; 93 Internal.instance.setCache(client, new CacheAdapter(responseCache)); 94 95 connection = new OkUrlFactory(client).open(serverUrl); 96 connection.setRequestProperty("key1", "value1"); 97 98 executeGet(connection); 99 } 100 101 @Test public void get_httpsGet() throws Exception { 102 final URL serverUrl = configureHttpsServer(new MockResponse()); 103 assertEquals("https", serverUrl.getProtocol()); 104 105 ResponseCache responseCache = new AbstractResponseCache() { 106 @Override public CacheResponse get(URI uri, String method, Map<String, List<String>> headers) 107 throws IOException { 108 assertEquals("https", uri.getScheme()); 109 assertEquals(toUri(serverUrl), uri); 110 assertEquals("GET", method); 111 assertTrue("Arbitrary standard header not present", headers.containsKey("User-Agent")); 112 assertEquals(Collections.singletonList("value1"), headers.get("key1")); 113 return null; 114 } 115 }; 116 Internal.instance.setCache(client, new CacheAdapter(responseCache)); 117 client.setSslSocketFactory(sslContext.getSocketFactory()); 118 client.setHostnameVerifier(hostnameVerifier); 119 120 connection = new OkUrlFactory(client).open(serverUrl); 121 connection.setRequestProperty("key1", "value1"); 122 123 executeGet(connection); 124 } 125 126 @Test public void put_httpGet() throws Exception { 127 final String statusLine = "HTTP/1.1 200 Fantastic"; 128 final byte[] response = "ResponseString".getBytes(StandardCharsets.UTF_8); 129 final URL serverUrl = configureServer( 130 new MockResponse() 131 .setStatus(statusLine) 132 .addHeader("A", "c") 133 .setBody(new Buffer().write(response))); 134 135 ResponseCache responseCache = new AbstractResponseCache() { 136 @Override public CacheRequest put(URI uri, URLConnection connection) throws IOException { 137 assertTrue(connection instanceof HttpURLConnection); 138 assertFalse(connection instanceof HttpsURLConnection); 139 140 assertEquals(response.length, connection.getContentLength()); 141 142 HttpURLConnection httpUrlConnection = (HttpURLConnection) connection; 143 assertEquals("GET", httpUrlConnection.getRequestMethod()); 144 assertTrue(httpUrlConnection.getDoInput()); 145 assertFalse(httpUrlConnection.getDoOutput()); 146 147 assertEquals("Fantastic", httpUrlConnection.getResponseMessage()); 148 assertEquals(toUri(serverUrl), uri); 149 assertEquals(serverUrl, connection.getURL()); 150 assertEquals("value", connection.getRequestProperty("key")); 151 152 // Check retrieval by string key. 153 assertEquals(statusLine, httpUrlConnection.getHeaderField(null)); 154 assertEquals("c", httpUrlConnection.getHeaderField("A")); 155 // The RI and OkHttp supports case-insensitive matching for this method. 156 assertEquals("c", httpUrlConnection.getHeaderField("a")); 157 return null; 158 } 159 }; 160 Internal.instance.setCache(client, new CacheAdapter(responseCache)); 161 162 connection = new OkUrlFactory(client).open(serverUrl); 163 connection.setRequestProperty("key", "value"); 164 executeGet(connection); 165 } 166 167 @Test public void put_httpPost() throws Exception { 168 final String statusLine = "HTTP/1.1 200 Fantastic"; 169 final URL serverUrl = configureServer( 170 new MockResponse() 171 .setStatus(statusLine) 172 .addHeader("A", "c")); 173 174 ResponseCache responseCache = new AbstractResponseCache() { 175 @Override public CacheRequest put(URI uri, URLConnection connection) throws IOException { 176 assertTrue(connection instanceof HttpURLConnection); 177 assertFalse(connection instanceof HttpsURLConnection); 178 179 assertEquals(0, connection.getContentLength()); 180 181 HttpURLConnection httpUrlConnection = (HttpURLConnection) connection; 182 assertEquals("POST", httpUrlConnection.getRequestMethod()); 183 assertTrue(httpUrlConnection.getDoInput()); 184 assertTrue(httpUrlConnection.getDoOutput()); 185 186 assertEquals("Fantastic", httpUrlConnection.getResponseMessage()); 187 assertEquals(toUri(serverUrl), uri); 188 assertEquals(serverUrl, connection.getURL()); 189 assertEquals("value", connection.getRequestProperty("key")); 190 191 // Check retrieval by string key. 192 assertEquals(statusLine, httpUrlConnection.getHeaderField(null)); 193 assertEquals("c", httpUrlConnection.getHeaderField("A")); 194 // The RI and OkHttp supports case-insensitive matching for this method. 195 assertEquals("c", httpUrlConnection.getHeaderField("a")); 196 return null; 197 } 198 }; 199 Internal.instance.setCache(client, new CacheAdapter(responseCache)); 200 201 connection = new OkUrlFactory(client).open(serverUrl); 202 203 executePost(connection); 204 } 205 206 @Test public void put_httpsGet() throws Exception { 207 final URL serverUrl = configureHttpsServer(new MockResponse()); 208 assertEquals("https", serverUrl.getProtocol()); 209 210 ResponseCache responseCache = new AbstractResponseCache() { 211 @Override public CacheRequest put(URI uri, URLConnection connection) throws IOException { 212 assertTrue(connection instanceof HttpsURLConnection); 213 assertEquals(toUri(serverUrl), uri); 214 assertEquals(serverUrl, connection.getURL()); 215 216 HttpsURLConnection cacheHttpsUrlConnection = (HttpsURLConnection) connection; 217 HttpsURLConnection realHttpsUrlConnection = (HttpsURLConnection) CacheAdapterTest.this.connection; 218 assertEquals(realHttpsUrlConnection.getCipherSuite(), 219 cacheHttpsUrlConnection.getCipherSuite()); 220 assertEquals(realHttpsUrlConnection.getPeerPrincipal(), 221 cacheHttpsUrlConnection.getPeerPrincipal()); 222 assertArrayEquals(realHttpsUrlConnection.getLocalCertificates(), 223 cacheHttpsUrlConnection.getLocalCertificates()); 224 assertArrayEquals(realHttpsUrlConnection.getServerCertificates(), 225 cacheHttpsUrlConnection.getServerCertificates()); 226 assertEquals(realHttpsUrlConnection.getLocalPrincipal(), 227 cacheHttpsUrlConnection.getLocalPrincipal()); 228 return null; 229 } 230 }; 231 Internal.instance.setCache(client, new CacheAdapter(responseCache)); 232 client.setSslSocketFactory(sslContext.getSocketFactory()); 233 client.setHostnameVerifier(hostnameVerifier); 234 235 connection = new OkUrlFactory(client).open(serverUrl); 236 executeGet(connection); 237 } 238 239 private void executeGet(HttpURLConnection connection) throws IOException { 240 connection.connect(); 241 connection.getHeaderFields(); 242 connection.disconnect(); 243 } 244 245 private void executePost(HttpURLConnection connection) throws IOException { 246 connection.setDoOutput(true); 247 connection.connect(); 248 connection.getOutputStream().write("Hello World".getBytes()); 249 connection.disconnect(); 250 } 251 252 private URL configureServer(MockResponse mockResponse) throws Exception { 253 server.enqueue(mockResponse); 254 server.start(); 255 return server.getUrl("/"); 256 } 257 258 private URL configureHttpsServer(MockResponse mockResponse) throws Exception { 259 server.useHttps(sslContext.getSocketFactory(), false /* tunnelProxy */); 260 server.enqueue(mockResponse); 261 server.start(); 262 return server.getUrl("/"); 263 } 264 } 265