JavaApiConverterTest.java revision 78092f38ebd93018ead53a87b53118dc829cbb8a
13c827367444ee418f129b2c238299f49d3264554Jarkko Poyry/* 23c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Copyright (C) 2014 Square, Inc. 33c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * 43c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Licensed under the Apache License, Version 2.0 (the "License"); 53c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * you may not use this file except in compliance with the License. 63c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * You may obtain a copy of the License at 73c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * 83c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * http://www.apache.org/licenses/LICENSE-2.0 93c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * 103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Unless required by applicable law or agreed to in writing, software 113c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * distributed under the License is distributed on an "AS IS" BASIS, 123c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * See the License for the specific language governing permissions and 143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * limitations under the License. 153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry */ 163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 173c827367444ee418f129b2c238299f49d3264554Jarkko Poyrypackage com.squareup.okhttp.internal.http; 183c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 193c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport com.squareup.okhttp.Handshake; 203c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport com.squareup.okhttp.Headers; 213c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport com.squareup.okhttp.MediaType; 223c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport com.squareup.okhttp.OkHttpClient; 233c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport com.squareup.okhttp.Request; 243c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport com.squareup.okhttp.Response; 253c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport com.squareup.okhttp.internal.SslContextBuilder; 263c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport com.squareup.okhttp.internal.Util; 273c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport com.squareup.okhttp.mockwebserver.MockResponse; 283c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport com.squareup.okhttp.mockwebserver.MockWebServer; 293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 303c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport org.junit.After; 313c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport org.junit.Before; 323c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport org.junit.Test; 333c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 343c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.io.ByteArrayInputStream; 353c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.io.ByteArrayOutputStream; 363c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.io.IOException; 373c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.io.InputStream; 383c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.net.CacheResponse; 393c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.net.HttpURLConnection; 403c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.net.SecureCacheResponse; 413c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.net.URI; 423c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.net.URL; 433c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.nio.charset.StandardCharsets; 443c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.security.Principal; 453c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.security.cert.Certificate; 463c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.security.cert.CertificateException; 473c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.security.cert.CertificateFactory; 483c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.security.cert.X509Certificate; 493c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.util.Arrays; 503c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.util.Collections; 513c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.util.HashMap; 523c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.util.LinkedHashSet; 533c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.util.List; 54import java.util.Map; 55import java.util.Set; 56import javax.net.ssl.HostnameVerifier; 57import javax.net.ssl.HttpsURLConnection; 58import javax.net.ssl.SSLContext; 59import javax.net.ssl.SSLPeerUnverifiedException; 60import javax.net.ssl.SSLSession; 61 62import static org.junit.Assert.assertArrayEquals; 63import static org.junit.Assert.assertEquals; 64import static org.junit.Assert.assertFalse; 65import static org.junit.Assert.assertNotNull; 66import static org.junit.Assert.assertNull; 67import static org.junit.Assert.assertSame; 68import static org.junit.Assert.assertTrue; 69import static org.junit.Assert.fail; 70 71/** 72 * Tests for {@link JavaApiConverter}. 73 */ 74public class JavaApiConverterTest { 75 76 // $ openssl req -x509 -nodes -days 36500 -subj '/CN=localhost' -config ./cert.cnf \ 77 // -newkey rsa:512 -out cert.pem 78 private static final X509Certificate LOCAL_CERT = certificate("" 79 + "-----BEGIN CERTIFICATE-----\n" 80 + "MIIBWDCCAQKgAwIBAgIJANS1EtICX2AZMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV\n" 81 + "BAMTCWxvY2FsaG9zdDAgFw0xMjAxMDIxOTA4NThaGA8yMTExMTIwOTE5MDg1OFow\n" 82 + "FDESMBAGA1UEAxMJbG9jYWxob3N0MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPpt\n" 83 + "atK8r4/hf4hSIs0os/BSlQLbRBaK9AfBReM4QdAklcQqe6CHsStKfI8pp0zs7Ptg\n" 84 + "PmMdpbttL0O7mUboBC8CAwEAAaM1MDMwMQYDVR0RBCowKIIVbG9jYWxob3N0Lmxv\n" 85 + "Y2FsZG9tYWlugglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcNAQEFBQADQQD0ntfL\n" 86 + "DCzOCv9Ma6Lv5o5jcYWVxvBSTsnt22hsJpWD1K7iY9lbkLwl0ivn73pG2evsAn9G\n" 87 + "X8YKH52fnHsCrhSD\n" 88 + "-----END CERTIFICATE-----"); 89 90 // openssl req -x509 -nodes -days 36500 -subj '/CN=*.0.0.1' -newkey rsa:512 -out cert.pem 91 private static final X509Certificate SERVER_CERT = certificate("" 92 + "-----BEGIN CERTIFICATE-----\n" 93 + "MIIBkjCCATygAwIBAgIJAMdemqOwd/BEMA0GCSqGSIb3DQEBBQUAMBIxEDAOBgNV\n" 94 + "BAMUByouMC4wLjEwIBcNMTAxMjIwMTY0NDI1WhgPMjExMDExMjYxNjQ0MjVaMBIx\n" 95 + "EDAOBgNVBAMUByouMC4wLjEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAqY8c9Qrt\n" 96 + "YPWCvb7lclI+aDHM6fgbJcHsS9Zg8nUOh5dWrS7AgeA25wyaokFl4plBbbHQe2j+\n" 97 + "cCjsRiJIcQo9HwIDAQABo3MwcTAdBgNVHQ4EFgQUJ436TZPJvwCBKklZZqIvt1Yt\n" 98 + "JjEwQgYDVR0jBDswOYAUJ436TZPJvwCBKklZZqIvt1YtJjGhFqQUMBIxEDAOBgNV\n" 99 + "BAMUByouMC4wLjGCCQDHXpqjsHfwRDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB\n" 100 + "BQUAA0EAk9i88xdjWoewqvE+iMC9tD2obMchgFDaHH0ogxxiRaIKeEly3g0uGxIt\n" 101 + "fl2WRY8hb4x+zRrwsFaLEpdEvqcjOQ==\n" 102 + "-----END CERTIFICATE-----"); 103 104 private static final SSLContext sslContext = SslContextBuilder.localhost(); 105 private static final HostnameVerifier NULL_HOSTNAME_VERIFIER = new HostnameVerifier() { 106 public boolean verify(String hostname, SSLSession session) { 107 return true; 108 } 109 }; 110 111 private MockWebServer server; 112 113 private OkHttpClient client; 114 115 private HttpURLConnection connection; 116 117 @Before public void setUp() throws Exception { 118 server = new MockWebServer(); 119 client = new OkHttpClient(); 120 } 121 122 @After public void tearDown() throws Exception { 123 if (connection != null) { 124 connection.disconnect(); 125 } 126 server.shutdown(); 127 } 128 129 @Test public void createOkResponse_fromOkHttpUrlConnection() throws Exception { 130 testCreateOkResponseInternal(new OkHttpURLConnectionFactory(client), false /* isSecure */); 131 } 132 133 @Test public void createOkResponse_fromJavaHttpUrlConnection() throws Exception { 134 testCreateOkResponseInternal(new JavaHttpURLConnectionFactory(), false /* isSecure */); 135 } 136 137 @Test public void createOkResponse_fromOkHttpsUrlConnection() throws Exception { 138 testCreateOkResponseInternal(new OkHttpURLConnectionFactory(client), true /* isSecure */); 139 } 140 141 @Test public void createOkResponse_fromJavaHttpsUrlConnection() throws Exception { 142 testCreateOkResponseInternal(new JavaHttpURLConnectionFactory(), true /* isSecure */); 143 } 144 145 private void testCreateOkResponseInternal(HttpURLConnectionFactory httpUrlConnectionFactory, 146 boolean isSecure) throws Exception { 147 String statusLine = "HTTP/1.1 200 Fantastic"; 148 String body = "Nothing happens"; 149 final URL serverUrl; 150 MockResponse mockResponse = new MockResponse() 151 .setStatus(statusLine) 152 .addHeader("xyzzy", "baz") 153 .setBody(body); 154 if (isSecure) { 155 serverUrl = configureHttpsServer( 156 mockResponse); 157 158 assertEquals("https", serverUrl.getProtocol()); 159 } else { 160 serverUrl = configureServer( 161 mockResponse); 162 assertEquals("http", serverUrl.getProtocol()); 163 } 164 165 connection = httpUrlConnectionFactory.open(serverUrl); 166 if (isSecure) { 167 HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) connection; 168 httpsUrlConnection.setSSLSocketFactory(sslContext.getSocketFactory()); 169 httpsUrlConnection.setHostnameVerifier(NULL_HOSTNAME_VERIFIER); 170 } 171 connection.setRequestProperty("snake", "bird"); 172 connection.connect(); 173 Response response = JavaApiConverter.createOkResponse(serverUrl.toURI(), connection); 174 175 // Check the response.request() 176 Request request = response.request(); 177 assertEquals(isSecure, request.isHttps()); 178 assertEquals(serverUrl.toURI(), request.uri()); 179 assertNull(request.body()); 180 Headers okRequestHeaders = request.headers(); 181 // In Java the request headers are unavailable for a connected HttpURLConnection. 182 assertEquals(0, okRequestHeaders.size()); 183 assertEquals("GET", request.method()); 184 185 // Check the response 186 assertEquals(statusLine, response.statusLine()); 187 Headers okResponseHeaders = response.headers(); 188 assertEquals("baz", okResponseHeaders.get("xyzzy")); 189 assertEquals(body, response.body().string()); 190 if (isSecure) { 191 Handshake handshake = response.handshake(); 192 assertNotNull(handshake); 193 HttpsURLConnection httpsURLConnection = (HttpsURLConnection) connection; 194 assertNotNullAndEquals(httpsURLConnection.getCipherSuite(), handshake.cipherSuite()); 195 assertEquals(httpsURLConnection.getLocalPrincipal(), handshake.localPrincipal()); 196 assertNotNullAndEquals(httpsURLConnection.getPeerPrincipal(), handshake.peerPrincipal()); 197 assertNotNull(httpsURLConnection.getServerCertificates()); 198 assertEquals(Arrays.asList(httpsURLConnection.getServerCertificates()), 199 handshake.peerCertificates()); 200 assertNull(httpsURLConnection.getLocalCertificates()); 201 } else { 202 assertNull(response.handshake()); 203 } 204 } 205 206 @Test public void createOkResponse_fromCacheResponse() throws Exception { 207 final String statusLine = "HTTP/1.1 200 Fantastic"; 208 URI uri = new URI("http://foo/bar"); 209 Request request = new Request.Builder().url(uri.toURL()).method("GET", null).build(); 210 CacheResponse cacheResponse = new CacheResponse() { 211 @Override 212 public Map<String, List<String>> getHeaders() throws IOException { 213 Map<String, List<String>> headers = new HashMap<String, List<String>>(); 214 headers.put(null, Collections.singletonList(statusLine)); 215 headers.put("xyzzy", Arrays.asList("bar", "baz")); 216 return headers; 217 } 218 219 @Override 220 public InputStream getBody() throws IOException { 221 return new ByteArrayInputStream("HelloWorld".getBytes(StandardCharsets.UTF_8)); 222 } 223 }; 224 225 Response response = JavaApiConverter.createOkResponse(request, cacheResponse); 226 assertSame(request, response.request()); 227 228 assertNotNullAndEquals(statusLine, response.statusLine()); 229 Headers okResponseHeaders = response.headers(); 230 assertEquals("baz", okResponseHeaders.get("xyzzy")); 231 assertEquals("HelloWorld", response.body().string()); 232 assertNull(response.handshake()); 233 } 234 235 @Test public void createOkResponse_fromSecureCacheResponse() throws Exception { 236 final String statusLine = "HTTP/1.1 200 Fantastic"; 237 final Principal localPrincipal = LOCAL_CERT.getSubjectX500Principal(); 238 final List<Certificate> localCertificates = Arrays.<Certificate>asList(LOCAL_CERT); 239 final Principal serverPrincipal = SERVER_CERT.getSubjectX500Principal(); 240 final List<Certificate> serverCertificates = Arrays.<Certificate>asList(SERVER_CERT); 241 URI uri = new URI("https://foo/bar"); 242 Request request = new Request.Builder().url(uri.toURL()).method("GET", null).build(); 243 SecureCacheResponse cacheResponse = new SecureCacheResponse() { 244 @Override 245 public Map<String, List<String>> getHeaders() throws IOException { 246 Map<String, List<String>> headers = new HashMap<String, List<String>>(); 247 headers.put(null, Collections.singletonList(statusLine)); 248 headers.put("xyzzy", Arrays.asList("bar", "baz")); 249 return headers; 250 } 251 252 @Override 253 public InputStream getBody() throws IOException { 254 return new ByteArrayInputStream("HelloWorld".getBytes(StandardCharsets.UTF_8)); 255 } 256 257 @Override 258 public String getCipherSuite() { 259 return "SuperSecure"; 260 } 261 262 @Override 263 public List<Certificate> getLocalCertificateChain() { 264 return localCertificates; 265 } 266 267 @Override 268 public List<Certificate> getServerCertificateChain() throws SSLPeerUnverifiedException { 269 return serverCertificates; 270 } 271 272 @Override 273 public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { 274 return serverPrincipal; 275 } 276 277 @Override 278 public Principal getLocalPrincipal() { 279 return localPrincipal; 280 } 281 }; 282 283 Response response = JavaApiConverter.createOkResponse(request, cacheResponse); 284 assertSame(request, response.request()); 285 286 assertNotNullAndEquals(statusLine, response.statusLine()); 287 Headers okResponseHeaders = response.headers(); 288 assertEquals("baz", okResponseHeaders.get("xyzzy")); 289 assertEquals("HelloWorld", response.body().string()); 290 291 Handshake handshake = response.handshake(); 292 assertNotNull(handshake); 293 assertNotNullAndEquals("SuperSecure", handshake.cipherSuite()); 294 assertEquals(localPrincipal, handshake.localPrincipal()); 295 assertEquals(serverPrincipal, handshake.peerPrincipal()); 296 assertEquals(serverCertificates, handshake.peerCertificates()); 297 assertEquals(localCertificates, handshake.localCertificates()); 298 } 299 300 @Test public void createOkRequest_nullRequestHeaders() throws Exception { 301 URI uri = new URI("http://foo/bar"); 302 303 Map<String,List<String>> javaRequestHeaders = null; 304 Request request = JavaApiConverter.createOkRequest(uri, "POST", javaRequestHeaders); 305 assertFalse(request.isHttps()); 306 assertEquals(uri, request.uri()); 307 assertNull(request.body()); 308 Headers okRequestHeaders = request.headers(); 309 assertEquals(0, okRequestHeaders.size()); 310 assertEquals("POST", request.method()); 311 } 312 313 @Test public void createOkRequest_nonNullRequestHeaders() throws Exception { 314 URI uri = new URI("https://foo/bar"); 315 316 Map<String,List<String>> javaRequestHeaders = new HashMap<String, List<String>>(); 317 javaRequestHeaders.put("Foo", Arrays.asList("Bar")); 318 Request request = JavaApiConverter.createOkRequest(uri, "POST", javaRequestHeaders); 319 assertTrue(request.isHttps()); 320 assertEquals(uri, request.uri()); 321 assertNull(request.body()); 322 Headers okRequestHeaders = request.headers(); 323 assertEquals(1, okRequestHeaders.size()); 324 assertEquals("Bar", okRequestHeaders.get("Foo")); 325 assertEquals("POST", request.method()); 326 } 327 328 // Older versions of OkHttp would store the "request line" as a header with a 329 // null key. To support the Android usecase where an old version of OkHttp uses 330 // a newer, Android-bundled, version of HttpResponseCache the null key must be 331 // explicitly ignored. 332 @Test public void createOkRequest_nullRequestHeaderKey() throws Exception { 333 URI uri = new URI("https://foo/bar"); 334 335 Map<String,List<String>> javaRequestHeaders = new HashMap<String, List<String>>(); 336 javaRequestHeaders.put(null, Arrays.asList("GET / HTTP 1.1")); 337 javaRequestHeaders.put("Foo", Arrays.asList("Bar")); 338 Request request = JavaApiConverter.createOkRequest(uri, "POST", javaRequestHeaders); 339 assertTrue(request.isHttps()); 340 assertEquals(uri, request.uri()); 341 assertNull(request.body()); 342 Headers okRequestHeaders = request.headers(); 343 assertEquals(1, okRequestHeaders.size()); 344 assertEquals("Bar", okRequestHeaders.get("Foo")); 345 assertEquals("POST", request.method()); 346 } 347 348 @Test public void createJavaUrlConnection_requestChangesForbidden() throws Exception { 349 Response okResponse = createArbitraryOkResponse(); 350 HttpURLConnection httpUrlConnection = JavaApiConverter.createJavaUrlConnection(okResponse); 351 // Check an arbitrary (not complete) set of methods that can be used to modify the 352 // request. 353 try { 354 httpUrlConnection.setRequestProperty("key", "value"); 355 fail(); 356 } catch (UnsupportedOperationException expected) { 357 } 358 try { 359 httpUrlConnection.setFixedLengthStreamingMode(1234); 360 fail(); 361 } catch (UnsupportedOperationException expected) { 362 } 363 try { 364 httpUrlConnection.setRequestMethod("PUT"); 365 fail(); 366 } catch (UnsupportedOperationException expected) { 367 } 368 try { 369 httpUrlConnection.getHeaderFields().put("key", Collections.singletonList("value")); 370 fail(); 371 } catch (UnsupportedOperationException expected) { 372 } 373 try { 374 httpUrlConnection.getOutputStream(); 375 fail(); 376 } catch (UnsupportedOperationException expected) { 377 } 378 } 379 380 @Test public void createJavaUrlConnection_connectionChangesForbidden() throws Exception { 381 Response okResponse = createArbitraryOkResponse(); 382 HttpURLConnection httpUrlConnection = JavaApiConverter.createJavaUrlConnection(okResponse); 383 try { 384 httpUrlConnection.connect(); 385 fail(); 386 } catch (UnsupportedOperationException expected) { 387 } 388 try { 389 httpUrlConnection.disconnect(); 390 fail(); 391 } catch (UnsupportedOperationException expected) { 392 } 393 } 394 395 @Test public void createJavaUrlConnection_responseChangesForbidden() throws Exception { 396 Response okResponse = createArbitraryOkResponse(); 397 HttpURLConnection httpUrlConnection = JavaApiConverter.createJavaUrlConnection(okResponse); 398 // Check an arbitrary (not complete) set of methods that can be used to access the response 399 // body. 400 try { 401 httpUrlConnection.getInputStream(); 402 fail(); 403 } catch (UnsupportedOperationException expected) { 404 } 405 try { 406 httpUrlConnection.getContent(); 407 fail(); 408 } catch (UnsupportedOperationException expected) { 409 } 410 try { 411 httpUrlConnection.setFixedLengthStreamingMode(1234); 412 fail(); 413 } catch (UnsupportedOperationException expected) { 414 } 415 try { 416 httpUrlConnection.setRequestMethod("PUT"); 417 fail(); 418 } catch (UnsupportedOperationException expected) { 419 } 420 try { 421 httpUrlConnection.getHeaderFields().put("key", Collections.singletonList("value")); 422 fail(); 423 } catch (UnsupportedOperationException expected) { 424 } 425 } 426 427 @Test public void createJavaUrlConnection_responseHeadersOk() throws Exception { 428 final String statusLine = "HTTP/1.1 200 Fantastic"; 429 Response.Body responseBody = 430 createResponseBody("text/plain", "BodyText".getBytes(StandardCharsets.UTF_8)); 431 Response okResponse = new Response.Builder() 432 .request(createArbitraryOkRequest()) 433 .statusLine(statusLine) 434 .addHeader("A", "c") 435 .addHeader("B", "d") 436 .addHeader("A", "e") 437 .addHeader("Content-Length", Long.toString(responseBody.contentLength())) 438 .body(responseBody) 439 .build(); 440 441 HttpURLConnection httpUrlConnection = JavaApiConverter.createJavaUrlConnection(okResponse); 442 assertEquals(200, httpUrlConnection.getResponseCode()); 443 assertEquals("Fantastic", httpUrlConnection.getResponseMessage()); 444 assertEquals(responseBody.contentLength(), httpUrlConnection.getContentLength()); 445 446 // Check retrieval by string key. 447 assertEquals(statusLine, httpUrlConnection.getHeaderField(null)); 448 assertEquals("e", httpUrlConnection.getHeaderField("A")); 449 // The RI and OkHttp supports case-insensitive matching for this method. 450 assertEquals("e", httpUrlConnection.getHeaderField("a")); 451 452 // Check retrieval using a Map. 453 Map<String, List<String>> responseHeaders = httpUrlConnection.getHeaderFields(); 454 assertEquals(Arrays.asList(statusLine), responseHeaders.get(null)); 455 assertEquals(newSet("c", "e"), newSet(responseHeaders.get("A"))); 456 // OkHttp supports case-insensitive matching here. The RI does not. 457 assertEquals(newSet("c", "e"), newSet(responseHeaders.get("a"))); 458 459 // Check the Map iterator contains the expected mappings. 460 assertHeadersContainsMapping(responseHeaders, null, statusLine); 461 assertHeadersContainsMapping(responseHeaders, "A", "c", "e"); 462 assertHeadersContainsMapping(responseHeaders, "B", "d"); 463 464 // Check immutability of the headers Map. 465 try { 466 responseHeaders.put("N", Arrays.asList("o")); 467 fail("Modified an unmodifiable view."); 468 } catch (UnsupportedOperationException expected) { 469 } 470 try { 471 responseHeaders.get("A").add("f"); 472 fail("Modified an unmodifiable view."); 473 } catch (UnsupportedOperationException expected) { 474 } 475 476 // Check retrieval of headers by index. 477 assertEquals(null, httpUrlConnection.getHeaderFieldKey(0)); 478 assertEquals(statusLine, httpUrlConnection.getHeaderField(0)); 479 // After header zero there may be additional entries provided at the beginning or end by the 480 // implementation. It's probably important that the relative ordering of the headers is 481 // preserved, particularly if there are multiple value for the same key. 482 int i = 1; 483 while (!httpUrlConnection.getHeaderFieldKey(i).equals("A")) { 484 i++; 485 } 486 // Check the ordering of the headers set by app code. 487 assertResponseHeaderAtIndex(httpUrlConnection, i++, "A", "c"); 488 assertResponseHeaderAtIndex(httpUrlConnection, i++, "B", "d"); 489 assertResponseHeaderAtIndex(httpUrlConnection, i++, "A", "e"); 490 // There may be some additional headers provided by the implementation. 491 while (httpUrlConnection.getHeaderField(i) != null) { 492 assertNotNull(httpUrlConnection.getHeaderFieldKey(i)); 493 i++; 494 } 495 // Confirm the correct behavior when the index is out-of-range. 496 assertNull(httpUrlConnection.getHeaderFieldKey(i)); 497 } 498 499 private static void assertResponseHeaderAtIndex(HttpURLConnection httpUrlConnection, 500 int headerIndex, String expectedKey, String expectedValue) { 501 assertEquals(expectedKey, httpUrlConnection.getHeaderFieldKey(headerIndex)); 502 assertEquals(expectedValue, httpUrlConnection.getHeaderField(headerIndex)); 503 } 504 505 private void assertHeadersContainsMapping(Map<String, List<String>> headers, String expectedKey, 506 String... expectedValues) { 507 assertTrue(headers.containsKey(expectedKey)); 508 assertEquals(newSet(expectedValues), newSet(headers.get(expectedKey))); 509 } 510 511 @Test public void createJavaUrlConnection_accessibleRequestInfo_GET() throws Exception { 512 Request okRequest = createArbitraryOkRequest().newBuilder() 513 .method("GET", null) 514 .build(); 515 Response okResponse = createArbitraryOkResponse(okRequest); 516 HttpURLConnection httpUrlConnection = JavaApiConverter.createJavaUrlConnection(okResponse); 517 518 assertEquals("GET", httpUrlConnection.getRequestMethod()); 519 assertTrue(httpUrlConnection.getDoInput()); 520 assertFalse(httpUrlConnection.getDoOutput()); 521 } 522 523 @Test public void createJavaUrlConnection_accessibleRequestInfo_POST() throws Exception { 524 Request okRequest = createArbitraryOkRequest().newBuilder() 525 .method("POST", createRequestBody("PostBody")) 526 .build(); 527 Response okResponse = createArbitraryOkResponse(okRequest); 528 HttpURLConnection httpUrlConnection = JavaApiConverter.createJavaUrlConnection(okResponse); 529 530 assertEquals("POST", httpUrlConnection.getRequestMethod()); 531 assertTrue(httpUrlConnection.getDoInput()); 532 assertTrue(httpUrlConnection.getDoOutput()); 533 } 534 535 @Test public void createJavaUrlConnection_https_extraHttpsMethods() throws Exception { 536 Request okRequest = createArbitraryOkRequest().newBuilder() 537 .method("GET", null) 538 .url("https://secure/request") 539 .build(); 540 Handshake handshake = Handshake.get("SecureCipher", Arrays.<Certificate>asList(SERVER_CERT), 541 Arrays.<Certificate>asList(LOCAL_CERT)); 542 Response okResponse = createArbitraryOkResponse(okRequest).newBuilder() 543 .handshake(handshake) 544 .build(); 545 HttpsURLConnection httpsUrlConnection = 546 (HttpsURLConnection) JavaApiConverter.createJavaUrlConnection(okResponse); 547 548 assertEquals("SecureCipher", httpsUrlConnection.getCipherSuite()); 549 assertEquals(SERVER_CERT.getSubjectX500Principal(), httpsUrlConnection.getPeerPrincipal()); 550 assertArrayEquals(new Certificate[] { LOCAL_CERT }, httpsUrlConnection.getLocalCertificates()); 551 assertArrayEquals(new Certificate[] { SERVER_CERT }, 552 httpsUrlConnection.getServerCertificates()); 553 assertEquals(LOCAL_CERT.getSubjectX500Principal(), httpsUrlConnection.getLocalPrincipal()); 554 } 555 556 @Test public void createJavaUrlConnection_https_forbiddenFields() throws Exception { 557 Request okRequest = createArbitraryOkRequest().newBuilder() 558 .url("https://secure/request") 559 .build(); 560 Response okResponse = createArbitraryOkResponse(okRequest); 561 HttpsURLConnection httpsUrlConnection = 562 (HttpsURLConnection) JavaApiConverter.createJavaUrlConnection(okResponse); 563 564 try { 565 httpsUrlConnection.getHostnameVerifier(); 566 fail(); 567 } catch (UnsupportedOperationException expected) { 568 } 569 try { 570 httpsUrlConnection.getSSLSocketFactory(); 571 fail(); 572 } catch (UnsupportedOperationException expected) { 573 } 574 } 575 576 @Test public void createJavaCacheResponse_httpGet() throws Exception { 577 Request okRequest = 578 createArbitraryOkRequest().newBuilder() 579 .url("http://insecure/request") 580 .method("GET", null) 581 .build(); 582 String statusLine = "HTTP/1.1 200 Fantastic"; 583 Response okResponse = createArbitraryOkResponse(okRequest).newBuilder() 584 .statusLine(statusLine) 585 .addHeader("key1", "value1_1") 586 .addHeader("key2", "value2") 587 .addHeader("key1", "value1_2") 588 .body(null) 589 .build(); 590 CacheResponse javaCacheResponse = JavaApiConverter.createJavaCacheResponse(okResponse); 591 assertFalse(javaCacheResponse instanceof SecureCacheResponse); 592 Map<String, List<String>> javaHeaders = javaCacheResponse.getHeaders(); 593 assertEquals(Arrays.asList("value1_1", "value1_2"), javaHeaders.get("key1")); 594 assertEquals(Arrays.asList(statusLine), javaHeaders.get(null)); 595 assertNull(javaCacheResponse.getBody()); 596 } 597 598 @Test public void createJavaCacheResponse_httpPost() throws Exception { 599 Request okRequest = 600 createArbitraryOkRequest().newBuilder() 601 .url("http://insecure/request") 602 .method("POST", createRequestBody("RequestBody") ) 603 .build(); 604 String statusLine = "HTTP/1.1 200 Fantastic"; 605 Response.Body responseBody = 606 createResponseBody("text/plain", "ResponseBody".getBytes(StandardCharsets.UTF_8)); 607 Response okResponse = createArbitraryOkResponse(okRequest).newBuilder() 608 .statusLine(statusLine) 609 .addHeader("key1", "value1_1") 610 .addHeader("key2", "value2") 611 .addHeader("key1", "value1_2") 612 .body(responseBody) 613 .build(); 614 CacheResponse javaCacheResponse = JavaApiConverter.createJavaCacheResponse(okResponse); 615 assertFalse(javaCacheResponse instanceof SecureCacheResponse); 616 Map<String, List<String>> javaHeaders = javaCacheResponse.getHeaders(); 617 assertEquals(Arrays.asList("value1_1", "value1_2"), javaHeaders.get("key1")); 618 assertEquals(Arrays.asList(statusLine), javaHeaders.get(null)); 619 assertArrayEquals(responseBody.bytes(), readAll(javaCacheResponse.getBody())); 620 } 621 622 @Test public void createJavaCacheResponse_httpsPost() throws Exception { 623 Request okRequest = 624 createArbitraryOkRequest().newBuilder() 625 .url("https://secure/request") 626 .method("POST", createRequestBody("RequestBody") ) 627 .build(); 628 String statusLine = "HTTP/1.1 200 Fantastic"; 629 Response.Body responseBody = 630 createResponseBody("text/plain", "ResponseBody".getBytes(StandardCharsets.UTF_8)); 631 Handshake handshake = Handshake.get("SecureCipher", Arrays.<Certificate>asList(SERVER_CERT), 632 Arrays.<Certificate>asList(LOCAL_CERT)); 633 Response okResponse = createArbitraryOkResponse(okRequest).newBuilder() 634 .statusLine(statusLine) 635 .addHeader("key1", "value1_1") 636 .addHeader("key2", "value2") 637 .addHeader("key1", "value1_2") 638 .body(responseBody) 639 .handshake(handshake) 640 .build(); 641 SecureCacheResponse javaCacheResponse = 642 (SecureCacheResponse) JavaApiConverter.createJavaCacheResponse(okResponse); 643 Map<String, List<String>> javaHeaders = javaCacheResponse.getHeaders(); 644 assertEquals(Arrays.asList("value1_1", "value1_2"), javaHeaders.get("key1")); 645 assertEquals(Arrays.asList(statusLine), javaHeaders.get(null)); 646 assertArrayEquals(responseBody.bytes(), readAll(javaCacheResponse.getBody())); 647 assertEquals(handshake.cipherSuite(), javaCacheResponse.getCipherSuite()); 648 assertEquals(handshake.localCertificates(), javaCacheResponse.getLocalCertificateChain()); 649 assertEquals(handshake.peerCertificates(), javaCacheResponse.getServerCertificateChain()); 650 assertEquals(handshake.localPrincipal(), javaCacheResponse.getLocalPrincipal()); 651 assertEquals(handshake.peerPrincipal(), javaCacheResponse.getPeerPrincipal()); 652 } 653 654 @Test public void extractJavaHeaders() throws Exception { 655 Request okRequest = createArbitraryOkRequest().newBuilder() 656 .addHeader("key1", "value1_1") 657 .addHeader("key2", "value2") 658 .addHeader("key1", "value1_2") 659 .build(); 660 Map<String, List<String>> javaHeaders = JavaApiConverter.extractJavaHeaders(okRequest); 661 662 assertEquals(Arrays.asList("value1_1", "value1_2"), javaHeaders.get("key1")); 663 assertEquals(Arrays.asList("value2"), javaHeaders.get("key2")); 664 } 665 666 @Test public void extractOkHeaders() { 667 Map<String, List<String>> javaResponseHeaders = new HashMap<String, List<String>>(); 668 javaResponseHeaders.put(null, Arrays.asList("StatusLine")); 669 javaResponseHeaders.put("key1", Arrays.asList("value1_1", "value1_2")); 670 javaResponseHeaders.put("key2", Arrays.asList("value2")); 671 672 Headers okHeaders = JavaApiConverter.extractOkHeaders(javaResponseHeaders); 673 assertEquals(3, okHeaders.size()); // null entry should be stripped out 674 assertEquals(Arrays.asList("value1_1", "value1_2"), okHeaders.values("key1")); 675 assertEquals(Arrays.asList("value2"), okHeaders.values("key2")); 676 } 677 678 @Test public void extractStatusLine() { 679 Map<String, List<String>> javaResponseHeaders = new HashMap<String, List<String>>(); 680 javaResponseHeaders.put(null, Arrays.asList("StatusLine")); 681 javaResponseHeaders.put("key1", Arrays.asList("value1_1", "value1_2")); 682 javaResponseHeaders.put("key2", Arrays.asList("value2")); 683 assertEquals("StatusLine", JavaApiConverter.extractStatusLine(javaResponseHeaders)); 684 685 assertNull(JavaApiConverter.extractStatusLine(Collections.<String, List<String>>emptyMap())); 686 } 687 688 private URL configureServer(MockResponse mockResponse) throws Exception { 689 server.enqueue(mockResponse); 690 server.play(); 691 return server.getUrl("/"); 692 } 693 694 private URL configureHttpsServer(MockResponse mockResponse) throws Exception { 695 server.useHttps(sslContext.getSocketFactory(), false /* tunnelProxy */); 696 server.enqueue(mockResponse); 697 server.play(); 698 return server.getUrl("/"); 699 } 700 701 private static <T> void assertNotNullAndEquals(T expected, T actual) { 702 assertNotNull(actual); 703 assertEquals(expected, actual); 704 } 705 706 private interface HttpURLConnectionFactory { 707 public HttpURLConnection open(URL serverUrl) throws IOException; 708 } 709 710 private static class OkHttpURLConnectionFactory implements HttpURLConnectionFactory { 711 protected final OkHttpClient client; 712 713 private OkHttpURLConnectionFactory(OkHttpClient client) { 714 this.client = client; 715 } 716 717 @Override 718 public HttpURLConnection open(URL serverUrl) { 719 return client.open(serverUrl); 720 } 721 } 722 723 private static class JavaHttpURLConnectionFactory implements HttpURLConnectionFactory { 724 @Override 725 public HttpURLConnection open(URL serverUrl) throws IOException { 726 return (HttpURLConnection) serverUrl.openConnection(); 727 } 728 } 729 730 private static X509Certificate certificate(String certificate) { 731 try { 732 return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate( 733 new ByteArrayInputStream(certificate.getBytes(Util.UTF_8))); 734 } catch (CertificateException e) { 735 fail(); 736 return null; 737 } 738 } 739 740 private static <T> Set<T> newSet(T... elements) { 741 return newSet(Arrays.asList(elements)); 742 } 743 744 private static <T> Set<T> newSet(List<T> elements) { 745 return new LinkedHashSet<T>(elements); 746 } 747 748 private static Request createArbitraryOkRequest() { 749 return new Request.Builder() 750 .url("http://arbitrary/url") 751 .method("GET", null) 752 .build(); 753 } 754 755 private static Response createArbitraryOkResponse(Request request) { 756 return new Response.Builder() 757 .request(request) 758 .statusLine("HTTP/1.1 200 Arbitrary") 759 .build(); 760 } 761 762 private static Response createArbitraryOkResponse() { 763 return createArbitraryOkResponse(createArbitraryOkRequest()); 764 } 765 766 private static Request.Body createRequestBody(String bodyText) { 767 return Request.Body.create(MediaType.parse("text/plain"), bodyText); 768 } 769 770 private static Response.Body createResponseBody(final String contentType, final byte[] bytes) { 771 return new Response.Body() { 772 773 @Override 774 public boolean ready() throws IOException { 775 return true; 776 } 777 778 @Override 779 public MediaType contentType() { 780 return MediaType.parse(contentType); 781 } 782 783 @Override 784 public long contentLength() { 785 return bytes.length; 786 } 787 788 @Override 789 public InputStream byteStream() { 790 return new ByteArrayInputStream(bytes); 791 } 792 }; 793 } 794 795 private byte[] readAll(InputStream in) throws IOException { 796 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 797 int value; 798 while ((value = in.read()) != -1) { 799 buffer.write(value); 800 } 801 in.close(); 802 return buffer.toByteArray(); 803 } 804 805} 806