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