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.Handshake; 19 import com.squareup.okhttp.Headers; 20 import com.squareup.okhttp.MediaType; 21 import com.squareup.okhttp.Protocol; 22 import com.squareup.okhttp.Request; 23 import com.squareup.okhttp.RequestBody; 24 import com.squareup.okhttp.Response; 25 import com.squareup.okhttp.ResponseBody; 26 import com.squareup.okhttp.internal.Internal; 27 import com.squareup.okhttp.internal.Util; 28 import com.squareup.okhttp.mockwebserver.MockWebServer; 29 import java.io.ByteArrayInputStream; 30 import java.io.ByteArrayOutputStream; 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.net.CacheResponse; 34 import java.net.HttpURLConnection; 35 import java.net.SecureCacheResponse; 36 import java.net.URI; 37 import java.nio.charset.StandardCharsets; 38 import java.security.Principal; 39 import java.security.cert.Certificate; 40 import java.security.cert.CertificateException; 41 import java.security.cert.CertificateFactory; 42 import java.security.cert.X509Certificate; 43 import java.util.Arrays; 44 import java.util.Collections; 45 import java.util.HashMap; 46 import java.util.LinkedHashSet; 47 import java.util.List; 48 import java.util.Map; 49 import java.util.Set; 50 import javax.net.ssl.HttpsURLConnection; 51 import javax.net.ssl.SSLPeerUnverifiedException; 52 import okio.Buffer; 53 import okio.BufferedSource; 54 import org.junit.Before; 55 import org.junit.Rule; 56 import org.junit.Test; 57 58 import static org.junit.Assert.assertArrayEquals; 59 import static org.junit.Assert.assertEquals; 60 import static org.junit.Assert.assertFalse; 61 import static org.junit.Assert.assertNotNull; 62 import static org.junit.Assert.assertNull; 63 import static org.junit.Assert.assertTrue; 64 import static org.junit.Assert.fail; 65 66 public class JavaApiConverterTest { 67 68 // $ openssl req -x509 -nodes -days 36500 -subj '/CN=localhost' -config ./cert.cnf \ 69 // -newkey rsa:512 -out cert.pem 70 private static final X509Certificate LOCAL_CERT = certificate("" 71 + "-----BEGIN CERTIFICATE-----\n" 72 + "MIIBWDCCAQKgAwIBAgIJANS1EtICX2AZMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV\n" 73 + "BAMTCWxvY2FsaG9zdDAgFw0xMjAxMDIxOTA4NThaGA8yMTExMTIwOTE5MDg1OFow\n" 74 + "FDESMBAGA1UEAxMJbG9jYWxob3N0MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPpt\n" 75 + "atK8r4/hf4hSIs0os/BSlQLbRBaK9AfBReM4QdAklcQqe6CHsStKfI8pp0zs7Ptg\n" 76 + "PmMdpbttL0O7mUboBC8CAwEAAaM1MDMwMQYDVR0RBCowKIIVbG9jYWxob3N0Lmxv\n" 77 + "Y2FsZG9tYWlugglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcNAQEFBQADQQD0ntfL\n" 78 + "DCzOCv9Ma6Lv5o5jcYWVxvBSTsnt22hsJpWD1K7iY9lbkLwl0ivn73pG2evsAn9G\n" 79 + "X8YKH52fnHsCrhSD\n" 80 + "-----END CERTIFICATE-----"); 81 82 // openssl req -x509 -nodes -days 36500 -subj '/CN=*.0.0.1' -newkey rsa:512 -out cert.pem 83 private static final X509Certificate SERVER_CERT = certificate("" 84 + "-----BEGIN CERTIFICATE-----\n" 85 + "MIIBkjCCATygAwIBAgIJAMdemqOwd/BEMA0GCSqGSIb3DQEBBQUAMBIxEDAOBgNV\n" 86 + "BAMUByouMC4wLjEwIBcNMTAxMjIwMTY0NDI1WhgPMjExMDExMjYxNjQ0MjVaMBIx\n" 87 + "EDAOBgNVBAMUByouMC4wLjEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAqY8c9Qrt\n" 88 + "YPWCvb7lclI+aDHM6fgbJcHsS9Zg8nUOh5dWrS7AgeA25wyaokFl4plBbbHQe2j+\n" 89 + "cCjsRiJIcQo9HwIDAQABo3MwcTAdBgNVHQ4EFgQUJ436TZPJvwCBKklZZqIvt1Yt\n" 90 + "JjEwQgYDVR0jBDswOYAUJ436TZPJvwCBKklZZqIvt1YtJjGhFqQUMBIxEDAOBgNV\n" 91 + "BAMUByouMC4wLjGCCQDHXpqjsHfwRDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB\n" 92 + "BQUAA0EAk9i88xdjWoewqvE+iMC9tD2obMchgFDaHH0ogxxiRaIKeEly3g0uGxIt\n" 93 + "fl2WRY8hb4x+zRrwsFaLEpdEvqcjOQ==\n" 94 + "-----END CERTIFICATE-----"); 95 96 @Rule public MockWebServer server = new MockWebServer(); 97 98 @Before public void setUp() throws Exception { 99 Internal.initializeInstanceForTests(); 100 } 101 102 @Test public void createOkResponseForCacheGet() throws Exception { 103 final String statusLine = "HTTP/1.1 200 Fantastic"; 104 URI uri = new URI("http://foo/bar"); 105 Request request = new Request.Builder().url(uri.toURL()).build(); 106 CacheResponse cacheResponse = new CacheResponse() { 107 @Override public Map<String, List<String>> getHeaders() throws IOException { 108 Map<String, List<String>> headers = new HashMap<>(); 109 headers.put(null, Collections.singletonList(statusLine)); 110 headers.put("xyzzy", Arrays.asList("bar", "baz")); 111 return headers; 112 } 113 114 @Override public InputStream getBody() throws IOException { 115 return new ByteArrayInputStream("HelloWorld".getBytes(StandardCharsets.UTF_8)); 116 } 117 }; 118 119 Response response = JavaApiConverter.createOkResponseForCacheGet(request, cacheResponse); 120 Request cacheRequest = response.request(); 121 assertEquals(request.httpUrl(), cacheRequest.httpUrl()); 122 assertEquals(request.method(), cacheRequest.method()); 123 assertEquals(0, request.headers().size()); 124 125 assertEquals(Protocol.HTTP_1_1, response.protocol()); 126 assertEquals(200, response.code()); 127 assertEquals("Fantastic", response.message()); 128 Headers okResponseHeaders = response.headers(); 129 assertEquals("baz", okResponseHeaders.get("xyzzy")); 130 assertEquals("HelloWorld", response.body().string()); 131 assertNull(response.handshake()); 132 } 133 134 /** Test for https://code.google.com/p/android/issues/detail?id=160522 */ 135 @Test public void createOkResponseForCacheGet_withMissingStatusLine() throws Exception { 136 URI uri = new URI("http://foo/bar"); 137 Request request = new Request.Builder().url(uri.toURL()).build(); 138 CacheResponse cacheResponse = new CacheResponse() { 139 @Override public Map<String, List<String>> getHeaders() throws IOException { 140 Map<String, List<String>> headers = new HashMap<>(); 141 // Headers is deliberately missing an entry with a null key. 142 headers.put("xyzzy", Arrays.asList("bar", "baz")); 143 return headers; 144 } 145 146 @Override public InputStream getBody() throws IOException { 147 return null; // Should never be called 148 } 149 }; 150 151 try { 152 JavaApiConverter.createOkResponseForCacheGet(request, cacheResponse); 153 fail(); 154 } catch (IOException expected) { 155 } 156 } 157 158 @Test public void createOkResponseForCacheGet_secure() throws Exception { 159 final String statusLine = "HTTP/1.1 200 Fantastic"; 160 final Principal localPrincipal = LOCAL_CERT.getSubjectX500Principal(); 161 final List<Certificate> localCertificates = Arrays.<Certificate>asList(LOCAL_CERT); 162 final Principal serverPrincipal = SERVER_CERT.getSubjectX500Principal(); 163 final List<Certificate> serverCertificates = Arrays.<Certificate>asList(SERVER_CERT); 164 URI uri = new URI("https://foo/bar"); 165 Request request = new Request.Builder().url(uri.toURL()).build(); 166 SecureCacheResponse cacheResponse = new SecureCacheResponse() { 167 @Override public Map<String, List<String>> getHeaders() throws IOException { 168 Map<String, List<String>> headers = new HashMap<>(); 169 headers.put(null, Collections.singletonList(statusLine)); 170 headers.put("xyzzy", Arrays.asList("bar", "baz")); 171 return headers; 172 } 173 174 @Override public InputStream getBody() throws IOException { 175 return new ByteArrayInputStream("HelloWorld".getBytes(StandardCharsets.UTF_8)); 176 } 177 178 @Override public String getCipherSuite() { 179 return "SuperSecure"; 180 } 181 182 @Override public List<Certificate> getLocalCertificateChain() { 183 return localCertificates; 184 } 185 186 @Override public List<Certificate> getServerCertificateChain() throws SSLPeerUnverifiedException { 187 return serverCertificates; 188 } 189 190 @Override public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { 191 return serverPrincipal; 192 } 193 194 @Override public Principal getLocalPrincipal() { 195 return localPrincipal; 196 } 197 }; 198 199 Response response = JavaApiConverter.createOkResponseForCacheGet(request, cacheResponse); 200 Request cacheRequest = response.request(); 201 assertEquals(request.httpUrl(), cacheRequest.httpUrl()); 202 assertEquals(request.method(), cacheRequest.method()); 203 assertEquals(0, request.headers().size()); 204 205 assertEquals(Protocol.HTTP_1_1, response.protocol()); 206 assertEquals(200, response.code()); 207 assertEquals("Fantastic", response.message()); 208 Headers okResponseHeaders = response.headers(); 209 assertEquals("baz", okResponseHeaders.get("xyzzy")); 210 assertEquals("HelloWorld", response.body().string()); 211 212 Handshake handshake = response.handshake(); 213 assertNotNull(handshake); 214 assertNotNullAndEquals("SuperSecure", handshake.cipherSuite()); 215 assertEquals(localPrincipal, handshake.localPrincipal()); 216 assertEquals(serverPrincipal, handshake.peerPrincipal()); 217 assertEquals(serverCertificates, handshake.peerCertificates()); 218 assertEquals(localCertificates, handshake.localCertificates()); 219 } 220 221 @Test public void createOkRequest_nullRequestHeaders() throws Exception { 222 URI uri = new URI("http://foo/bar"); 223 224 Map<String,List<String>> javaRequestHeaders = null; 225 Request request = JavaApiConverter.createOkRequest(uri, "POST", javaRequestHeaders); 226 assertFalse(request.isHttps()); 227 assertEquals(uri, request.uri()); 228 Headers okRequestHeaders = request.headers(); 229 assertEquals(0, okRequestHeaders.size()); 230 assertEquals("POST", request.method()); 231 } 232 233 @Test public void createOkRequest_nonNullRequestHeaders() throws Exception { 234 URI uri = new URI("https://foo/bar"); 235 236 Map<String,List<String>> javaRequestHeaders = new HashMap<>(); 237 javaRequestHeaders.put("Foo", Arrays.asList("Bar")); 238 Request request = JavaApiConverter.createOkRequest(uri, "POST", javaRequestHeaders); 239 assertTrue(request.isHttps()); 240 assertEquals(uri, request.uri()); 241 Headers okRequestHeaders = request.headers(); 242 assertEquals(1, okRequestHeaders.size()); 243 assertEquals("Bar", okRequestHeaders.get("Foo")); 244 assertEquals("POST", request.method()); 245 } 246 247 // Older versions of OkHttp would store the "request line" as a header with a 248 // null key. To support the Android usecase where an old version of OkHttp uses 249 // a newer, Android-bundled, version of HttpResponseCache the null key must be 250 // explicitly ignored. 251 @Test public void createOkRequest_nullRequestHeaderKey() throws Exception { 252 URI uri = new URI("https://foo/bar"); 253 254 Map<String,List<String>> javaRequestHeaders = new HashMap<>(); 255 javaRequestHeaders.put(null, Arrays.asList("GET / HTTP 1.1")); 256 javaRequestHeaders.put("Foo", Arrays.asList("Bar")); 257 Request request = JavaApiConverter.createOkRequest(uri, "POST", javaRequestHeaders); 258 assertTrue(request.isHttps()); 259 assertEquals(uri, request.uri()); 260 Headers okRequestHeaders = request.headers(); 261 assertEquals(1, okRequestHeaders.size()); 262 assertEquals("Bar", okRequestHeaders.get("Foo")); 263 assertEquals("POST", request.method()); 264 } 265 266 @Test public void createJavaUrlConnection_requestChangesForbidden() throws Exception { 267 Response okResponse = createArbitraryOkResponse(); 268 HttpURLConnection httpUrlConnection = 269 JavaApiConverter.createJavaUrlConnectionForCachePut(okResponse); 270 // Check an arbitrary (not complete) set of methods that can be used to modify the 271 // request. 272 try { 273 httpUrlConnection.setRequestProperty("key", "value"); 274 fail(); 275 } catch (UnsupportedOperationException expected) { 276 } 277 try { 278 httpUrlConnection.setFixedLengthStreamingMode(1234); 279 fail(); 280 } catch (UnsupportedOperationException expected) { 281 } 282 try { 283 httpUrlConnection.setRequestMethod("PUT"); 284 fail(); 285 } catch (UnsupportedOperationException expected) { 286 } 287 try { 288 httpUrlConnection.getHeaderFields().put("key", Collections.singletonList("value")); 289 fail(); 290 } catch (UnsupportedOperationException expected) { 291 } 292 try { 293 httpUrlConnection.getOutputStream(); 294 fail(); 295 } catch (UnsupportedOperationException expected) { 296 } 297 } 298 299 @Test public void createJavaUrlConnection_connectionChangesForbidden() throws Exception { 300 Response okResponse = createArbitraryOkResponse(); 301 HttpURLConnection httpUrlConnection = 302 JavaApiConverter.createJavaUrlConnectionForCachePut(okResponse); 303 try { 304 httpUrlConnection.connect(); 305 fail(); 306 } catch (UnsupportedOperationException expected) { 307 } 308 try { 309 httpUrlConnection.disconnect(); 310 fail(); 311 } catch (UnsupportedOperationException expected) { 312 } 313 } 314 315 @Test public void createJavaUrlConnection_responseChangesForbidden() throws Exception { 316 Response okResponse = createArbitraryOkResponse(); 317 HttpURLConnection httpUrlConnection = 318 JavaApiConverter.createJavaUrlConnectionForCachePut(okResponse); 319 // Check an arbitrary (not complete) set of methods that can be used to access the response 320 // body. 321 try { 322 httpUrlConnection.getInputStream(); 323 fail(); 324 } catch (UnsupportedOperationException expected) { 325 } 326 try { 327 httpUrlConnection.getContent(); 328 fail(); 329 } catch (UnsupportedOperationException expected) { 330 } 331 try { 332 httpUrlConnection.setFixedLengthStreamingMode(1234); 333 fail(); 334 } catch (UnsupportedOperationException expected) { 335 } 336 try { 337 httpUrlConnection.setRequestMethod("PUT"); 338 fail(); 339 } catch (UnsupportedOperationException expected) { 340 } 341 try { 342 httpUrlConnection.getHeaderFields().put("key", Collections.singletonList("value")); 343 fail(); 344 } catch (UnsupportedOperationException expected) { 345 } 346 } 347 348 @Test public void createJavaUrlConnection_responseHeadersOk() throws Exception { 349 ResponseBody responseBody = createResponseBody("BodyText"); 350 Response okResponse = new Response.Builder() 351 .request(createArbitraryOkRequest()) 352 .protocol(Protocol.HTTP_1_1) 353 .code(200) 354 .message("Fantastic") 355 .addHeader("A", "c") 356 .addHeader("B", "d") 357 .addHeader("A", "e") 358 .addHeader("Content-Length", Long.toString(responseBody.contentLength())) 359 .body(responseBody) 360 .build(); 361 362 HttpURLConnection httpUrlConnection = 363 JavaApiConverter.createJavaUrlConnectionForCachePut(okResponse); 364 assertEquals(200, httpUrlConnection.getResponseCode()); 365 assertEquals("Fantastic", httpUrlConnection.getResponseMessage()); 366 assertEquals(responseBody.contentLength(), httpUrlConnection.getContentLength()); 367 368 // Check retrieval by string key. 369 assertEquals("HTTP/1.1 200 Fantastic", httpUrlConnection.getHeaderField(null)); 370 assertEquals("e", httpUrlConnection.getHeaderField("A")); 371 // The RI and OkHttp supports case-insensitive matching for this method. 372 assertEquals("e", httpUrlConnection.getHeaderField("a")); 373 374 // Check retrieval using a Map. 375 Map<String, List<String>> responseHeaders = httpUrlConnection.getHeaderFields(); 376 assertEquals(Arrays.asList("HTTP/1.1 200 Fantastic"), responseHeaders.get(null)); 377 assertEquals(newSet("c", "e"), newSet(responseHeaders.get("A"))); 378 // OkHttp supports case-insensitive matching here. The RI does not. 379 assertEquals(newSet("c", "e"), newSet(responseHeaders.get("a"))); 380 381 // Check the Map iterator contains the expected mappings. 382 assertHeadersContainsMapping(responseHeaders, null, "HTTP/1.1 200 Fantastic"); 383 assertHeadersContainsMapping(responseHeaders, "A", "c", "e"); 384 assertHeadersContainsMapping(responseHeaders, "B", "d"); 385 386 // Check immutability of the headers Map. 387 try { 388 responseHeaders.put("N", Arrays.asList("o")); 389 fail("Modified an unmodifiable view."); 390 } catch (UnsupportedOperationException expected) { 391 } 392 try { 393 responseHeaders.get("A").add("f"); 394 fail("Modified an unmodifiable view."); 395 } catch (UnsupportedOperationException expected) { 396 } 397 398 // Check retrieval of headers by index. 399 assertEquals(null, httpUrlConnection.getHeaderFieldKey(0)); 400 assertEquals("HTTP/1.1 200 Fantastic", httpUrlConnection.getHeaderField(0)); 401 // After header zero there may be additional entries provided at the beginning or end by the 402 // implementation. It's probably important that the relative ordering of the headers is 403 // preserved, particularly if there are multiple value for the same key. 404 int i = 1; 405 while (!httpUrlConnection.getHeaderFieldKey(i).equals("A")) { 406 i++; 407 } 408 // Check the ordering of the headers set by app code. 409 assertResponseHeaderAtIndex(httpUrlConnection, i++, "A", "c"); 410 assertResponseHeaderAtIndex(httpUrlConnection, i++, "B", "d"); 411 assertResponseHeaderAtIndex(httpUrlConnection, i++, "A", "e"); 412 // There may be some additional headers provided by the implementation. 413 while (httpUrlConnection.getHeaderField(i) != null) { 414 assertNotNull(httpUrlConnection.getHeaderFieldKey(i)); 415 i++; 416 } 417 // Confirm the correct behavior when the index is out-of-range. 418 assertNull(httpUrlConnection.getHeaderFieldKey(i)); 419 } 420 421 private static void assertResponseHeaderAtIndex(HttpURLConnection httpUrlConnection, 422 int headerIndex, String expectedKey, String expectedValue) { 423 assertEquals(expectedKey, httpUrlConnection.getHeaderFieldKey(headerIndex)); 424 assertEquals(expectedValue, httpUrlConnection.getHeaderField(headerIndex)); 425 } 426 427 private void assertHeadersContainsMapping(Map<String, List<String>> headers, String expectedKey, 428 String... expectedValues) { 429 assertTrue(headers.containsKey(expectedKey)); 430 assertEquals(newSet(expectedValues), newSet(headers.get(expectedKey))); 431 } 432 433 @Test public void createJavaUrlConnection_accessibleRequestInfo_GET() throws Exception { 434 Request okRequest = createArbitraryOkRequest().newBuilder() 435 .get() 436 .build(); 437 Response okResponse = createArbitraryOkResponse(okRequest); 438 HttpURLConnection httpUrlConnection = 439 JavaApiConverter.createJavaUrlConnectionForCachePut(okResponse); 440 441 assertEquals("GET", httpUrlConnection.getRequestMethod()); 442 assertTrue(httpUrlConnection.getDoInput()); 443 assertFalse(httpUrlConnection.getDoOutput()); 444 } 445 446 @Test public void createJavaUrlConnection_accessibleRequestInfo_POST() throws Exception { 447 Request okRequest = createArbitraryOkRequest().newBuilder() 448 .post(createRequestBody("PostBody")) 449 .build(); 450 Response okResponse = createArbitraryOkResponse(okRequest); 451 HttpURLConnection httpUrlConnection = 452 JavaApiConverter.createJavaUrlConnectionForCachePut(okResponse); 453 454 assertEquals("POST", httpUrlConnection.getRequestMethod()); 455 assertTrue(httpUrlConnection.getDoInput()); 456 assertTrue(httpUrlConnection.getDoOutput()); 457 } 458 459 @Test public void createJavaUrlConnection_https_extraHttpsMethods() throws Exception { 460 Request okRequest = createArbitraryOkRequest().newBuilder() 461 .get() 462 .url("https://secure/request") 463 .build(); 464 Handshake handshake = Handshake.get("SecureCipher", Arrays.<Certificate>asList(SERVER_CERT), 465 Arrays.<Certificate>asList(LOCAL_CERT)); 466 Response okResponse = createArbitraryOkResponse(okRequest).newBuilder() 467 .handshake(handshake) 468 .build(); 469 HttpsURLConnection httpsUrlConnection = 470 (HttpsURLConnection) JavaApiConverter.createJavaUrlConnectionForCachePut(okResponse); 471 472 assertEquals("SecureCipher", httpsUrlConnection.getCipherSuite()); 473 assertEquals(SERVER_CERT.getSubjectX500Principal(), httpsUrlConnection.getPeerPrincipal()); 474 assertArrayEquals(new Certificate[] { LOCAL_CERT }, httpsUrlConnection.getLocalCertificates()); 475 assertArrayEquals(new Certificate[] { SERVER_CERT }, 476 httpsUrlConnection.getServerCertificates()); 477 assertEquals(LOCAL_CERT.getSubjectX500Principal(), httpsUrlConnection.getLocalPrincipal()); 478 } 479 480 @Test public void createJavaUrlConnection_https_forbiddenFields() throws Exception { 481 Request okRequest = createArbitraryOkRequest().newBuilder() 482 .url("https://secure/request") 483 .build(); 484 Response okResponse = createArbitraryOkResponse(okRequest); 485 HttpsURLConnection httpsUrlConnection = 486 (HttpsURLConnection) JavaApiConverter.createJavaUrlConnectionForCachePut(okResponse); 487 488 try { 489 httpsUrlConnection.getHostnameVerifier(); 490 fail(); 491 } catch (UnsupportedOperationException expected) { 492 } 493 try { 494 httpsUrlConnection.getSSLSocketFactory(); 495 fail(); 496 } catch (UnsupportedOperationException expected) { 497 } 498 } 499 500 @Test public void createJavaCacheResponse_httpGet() throws Exception { 501 Request okRequest = 502 createArbitraryOkRequest().newBuilder() 503 .url("http://insecure/request") 504 .get() 505 .build(); 506 Response okResponse = createArbitraryOkResponse(okRequest).newBuilder() 507 .protocol(Protocol.HTTP_1_1) 508 .code(200) 509 .message("Fantastic") 510 .addHeader("key1", "value1_1") 511 .addHeader("key2", "value2") 512 .addHeader("key1", "value1_2") 513 .body(null) 514 .build(); 515 CacheResponse javaCacheResponse = JavaApiConverter.createJavaCacheResponse(okResponse); 516 assertFalse(javaCacheResponse instanceof SecureCacheResponse); 517 Map<String, List<String>> javaHeaders = javaCacheResponse.getHeaders(); 518 assertEquals(Arrays.asList("value1_1", "value1_2"), javaHeaders.get("key1")); 519 assertEquals(Arrays.asList("HTTP/1.1 200 Fantastic"), javaHeaders.get(null)); 520 assertNull(javaCacheResponse.getBody()); 521 } 522 523 @Test public void createJavaCacheResponse_httpPost() throws Exception { 524 Request okRequest = 525 createArbitraryOkRequest().newBuilder() 526 .url("http://insecure/request") 527 .post(createRequestBody("RequestBody")) 528 .build(); 529 ResponseBody responseBody = createResponseBody("ResponseBody"); 530 Response okResponse = createArbitraryOkResponse(okRequest).newBuilder() 531 .protocol(Protocol.HTTP_1_1) 532 .code(200) 533 .message("Fantastic") 534 .addHeader("key1", "value1_1") 535 .addHeader("key2", "value2") 536 .addHeader("key1", "value1_2") 537 .body(responseBody) 538 .build(); 539 CacheResponse javaCacheResponse = JavaApiConverter.createJavaCacheResponse(okResponse); 540 assertFalse(javaCacheResponse instanceof SecureCacheResponse); 541 Map<String, List<String>> javaHeaders = javaCacheResponse.getHeaders(); 542 assertEquals(Arrays.asList("value1_1", "value1_2"), javaHeaders.get("key1")); 543 assertEquals(Arrays.asList("HTTP/1.1 200 Fantastic"), javaHeaders.get(null)); 544 assertEquals("ResponseBody", readAll(javaCacheResponse.getBody())); 545 } 546 547 @Test public void createJavaCacheResponse_httpsPost() throws Exception { 548 Request okRequest = 549 createArbitraryOkRequest().newBuilder() 550 .url("https://secure/request") 551 .post(createRequestBody("RequestBody") ) 552 .build(); 553 ResponseBody responseBody = createResponseBody("ResponseBody"); 554 Handshake handshake = Handshake.get("SecureCipher", Arrays.<Certificate>asList(SERVER_CERT), 555 Arrays.<Certificate>asList(LOCAL_CERT)); 556 Response okResponse = createArbitraryOkResponse(okRequest).newBuilder() 557 .protocol(Protocol.HTTP_1_1) 558 .code(200) 559 .message("Fantastic") 560 .addHeader("key1", "value1_1") 561 .addHeader("key2", "value2") 562 .addHeader("key1", "value1_2") 563 .body(responseBody) 564 .handshake(handshake) 565 .build(); 566 SecureCacheResponse javaCacheResponse = 567 (SecureCacheResponse) JavaApiConverter.createJavaCacheResponse(okResponse); 568 Map<String, List<String>> javaHeaders = javaCacheResponse.getHeaders(); 569 assertEquals(Arrays.asList("value1_1", "value1_2"), javaHeaders.get("key1")); 570 assertEquals(Arrays.asList("HTTP/1.1 200 Fantastic"), javaHeaders.get(null)); 571 assertEquals("ResponseBody", readAll(javaCacheResponse.getBody())); 572 assertEquals(handshake.cipherSuite(), javaCacheResponse.getCipherSuite()); 573 assertEquals(handshake.localCertificates(), javaCacheResponse.getLocalCertificateChain()); 574 assertEquals(handshake.peerCertificates(), javaCacheResponse.getServerCertificateChain()); 575 assertEquals(handshake.localPrincipal(), javaCacheResponse.getLocalPrincipal()); 576 assertEquals(handshake.peerPrincipal(), javaCacheResponse.getPeerPrincipal()); 577 } 578 579 @Test public void extractJavaHeaders() throws Exception { 580 Request okRequest = createArbitraryOkRequest().newBuilder() 581 .addHeader("key1", "value1_1") 582 .addHeader("key2", "value2") 583 .addHeader("key1", "value1_2") 584 .build(); 585 Map<String, List<String>> javaHeaders = JavaApiConverter.extractJavaHeaders(okRequest); 586 587 assertEquals(Arrays.asList("value1_1", "value1_2"), javaHeaders.get("key1")); 588 assertEquals(Arrays.asList("value2"), javaHeaders.get("key2")); 589 } 590 591 @Test public void extractOkHeaders() { 592 Map<String, List<String>> javaResponseHeaders = new HashMap<>(); 593 javaResponseHeaders.put(null, Arrays.asList("StatusLine")); 594 javaResponseHeaders.put("key1", Arrays.asList("value1_1", "value1_2")); 595 javaResponseHeaders.put("key2", Arrays.asList("value2")); 596 597 Headers okHeaders = JavaApiConverter.extractOkHeaders(javaResponseHeaders); 598 assertEquals(3, okHeaders.size()); // null entry should be stripped out 599 assertEquals(Arrays.asList("value1_1", "value1_2"), okHeaders.values("key1")); 600 assertEquals(Arrays.asList("value2"), okHeaders.values("key2")); 601 } 602 603 @Test public void extractStatusLine() throws Exception { 604 Map<String, List<String>> javaResponseHeaders = new HashMap<>(); 605 javaResponseHeaders.put(null, Arrays.asList("StatusLine")); 606 javaResponseHeaders.put("key1", Arrays.asList("value1_1", "value1_2")); 607 javaResponseHeaders.put("key2", Arrays.asList("value2")); 608 assertEquals("StatusLine", JavaApiConverter.extractStatusLine(javaResponseHeaders)); 609 610 try { 611 JavaApiConverter.extractStatusLine(Collections.<String, List<String>>emptyMap()); 612 fail(); 613 } catch (IOException expected) { 614 } 615 } 616 617 private static <T> void assertNotNullAndEquals(T expected, T actual) { 618 assertNotNull(actual); 619 assertEquals(expected, actual); 620 } 621 622 private static X509Certificate certificate(String certificate) { 623 try { 624 return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate( 625 new ByteArrayInputStream(certificate.getBytes(Util.UTF_8))); 626 } catch (CertificateException e) { 627 fail(); 628 return null; 629 } 630 } 631 632 @SafeVarargs 633 private static <T> Set<T> newSet(T... elements) { 634 return newSet(Arrays.asList(elements)); 635 } 636 637 private static <T> Set<T> newSet(List<T> elements) { 638 return new LinkedHashSet<>(elements); 639 } 640 641 private static Request createArbitraryOkRequest() { 642 return new Request.Builder().url("http://arbitrary/url").build(); 643 } 644 645 private static Response createArbitraryOkResponse(Request request) { 646 return new Response.Builder() 647 .request(request) 648 .protocol(Protocol.HTTP_1_1) 649 .code(200) 650 .message("Arbitrary") 651 .build(); 652 } 653 654 private static Response createArbitraryOkResponse() { 655 return createArbitraryOkResponse(createArbitraryOkRequest()); 656 } 657 658 private static RequestBody createRequestBody(String bodyText) { 659 return RequestBody.create(MediaType.parse("text/plain"), bodyText); 660 } 661 662 private static ResponseBody createResponseBody(String bodyText) { 663 final Buffer source = new Buffer().writeUtf8(bodyText); 664 final long contentLength = source.size(); 665 return new ResponseBody() { 666 @Override public MediaType contentType() { 667 return MediaType.parse("text/plain; charset=utf-8"); 668 } 669 670 @Override public long contentLength() { 671 return contentLength; 672 } 673 674 @Override public BufferedSource source() { 675 return source; 676 } 677 }; 678 } 679 680 private String readAll(InputStream in) throws IOException { 681 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 682 int value; 683 while ((value = in.read()) != -1) { 684 buffer.write(value); 685 } 686 in.close(); 687 return buffer.toString("UTF-8"); 688 } 689 } 690