Home | History | Annotate | Download | only in huc
      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