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