Home | History | Annotate | Download | only in okhttp
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      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 
     17 package com.squareup.okhttp;
     18 
     19 import com.squareup.okhttp.internal.Internal;
     20 import com.squareup.okhttp.internal.Platform;
     21 import com.squareup.okhttp.internal.RecordingAuthenticator;
     22 import com.squareup.okhttp.internal.RecordingOkAuthenticator;
     23 import com.squareup.okhttp.internal.SingleInetAddressDns;
     24 import com.squareup.okhttp.internal.SslContextBuilder;
     25 import com.squareup.okhttp.internal.Util;
     26 import com.squareup.okhttp.internal.Version;
     27 import com.squareup.okhttp.mockwebserver.MockResponse;
     28 import com.squareup.okhttp.mockwebserver.MockWebServer;
     29 import com.squareup.okhttp.mockwebserver.RecordedRequest;
     30 import com.squareup.okhttp.mockwebserver.SocketPolicy;
     31 import com.squareup.okhttp.testing.RecordingHostnameVerifier;
     32 import java.io.IOException;
     33 import java.io.InputStream;
     34 import java.io.OutputStream;
     35 import java.net.Authenticator;
     36 import java.net.ConnectException;
     37 import java.net.HttpRetryException;
     38 import java.net.HttpURLConnection;
     39 import java.net.InetAddress;
     40 import java.net.ProtocolException;
     41 import java.net.Proxy;
     42 import java.net.ProxySelector;
     43 import java.net.ServerSocket;
     44 import java.net.Socket;
     45 import java.net.SocketAddress;
     46 import java.net.SocketTimeoutException;
     47 import java.net.URI;
     48 import java.net.URL;
     49 import java.net.URLConnection;
     50 import java.net.UnknownHostException;
     51 import java.security.SecureRandom;
     52 import java.security.cert.CertificateException;
     53 import java.security.cert.X509Certificate;
     54 import java.util.ArrayList;
     55 import java.util.Arrays;
     56 import java.util.Collections;
     57 import java.util.EnumSet;
     58 import java.util.LinkedHashSet;
     59 import java.util.List;
     60 import java.util.Map;
     61 import java.util.Random;
     62 import java.util.Set;
     63 import java.util.concurrent.TimeUnit;
     64 import java.util.zip.GZIPInputStream;
     65 import javax.net.ServerSocketFactory;
     66 import javax.net.SocketFactory;
     67 import javax.net.ssl.HttpsURLConnection;
     68 import javax.net.ssl.SSLContext;
     69 import javax.net.ssl.SSLException;
     70 import javax.net.ssl.SSLHandshakeException;
     71 import javax.net.ssl.SSLSocketFactory;
     72 import javax.net.ssl.TrustManager;
     73 import javax.net.ssl.X509TrustManager;
     74 import okio.Buffer;
     75 import okio.BufferedSink;
     76 import okio.GzipSink;
     77 import okio.Okio;
     78 import org.junit.After;
     79 import org.junit.Before;
     80 import org.junit.Ignore;
     81 import org.junit.Rule;
     82 import org.junit.Test;
     83 import org.junit.rules.TemporaryFolder;
     84 
     85 import static com.squareup.okhttp.internal.Util.UTF_8;
     86 import static com.squareup.okhttp.internal.http.OkHeaders.SELECTED_PROTOCOL;
     87 import static com.squareup.okhttp.internal.http.StatusLine.HTTP_PERM_REDIRECT;
     88 import static com.squareup.okhttp.internal.http.StatusLine.HTTP_TEMP_REDIRECT;
     89 import static com.squareup.okhttp.mockwebserver.SocketPolicy.DISCONNECT_AT_END;
     90 import static com.squareup.okhttp.mockwebserver.SocketPolicy.DISCONNECT_AT_START;
     91 import static com.squareup.okhttp.mockwebserver.SocketPolicy.SHUTDOWN_INPUT_AT_END;
     92 import static com.squareup.okhttp.mockwebserver.SocketPolicy.SHUTDOWN_OUTPUT_AT_END;
     93 import static java.util.concurrent.TimeUnit.MILLISECONDS;
     94 import static java.util.concurrent.TimeUnit.NANOSECONDS;
     95 import static org.junit.Assert.assertEquals;
     96 import static org.junit.Assert.assertFalse;
     97 import static org.junit.Assert.assertNotNull;
     98 import static org.junit.Assert.assertNull;
     99 import static org.junit.Assert.assertTrue;
    100 import static org.junit.Assert.fail;
    101 
    102 /** Android's URLConnectionTest. */
    103 public final class URLConnectionTest {
    104   @Rule public final MockWebServer server = new MockWebServer();
    105   @Rule public final MockWebServer server2 = new MockWebServer();
    106   @Rule public final TemporaryFolder tempDir = new TemporaryFolder();
    107 
    108   private SSLContext sslContext = SslContextBuilder.localhost();
    109   private OkUrlFactory client;
    110   private HttpURLConnection connection;
    111   private Cache cache;
    112 
    113   @Before public void setUp() throws Exception {
    114     server.setProtocolNegotiationEnabled(false);
    115     client = new OkUrlFactory(new OkHttpClient());
    116   }
    117 
    118   @After public void tearDown() throws Exception {
    119     Authenticator.setDefault(null);
    120     System.clearProperty("proxyHost");
    121     System.clearProperty("proxyPort");
    122     System.clearProperty("http.agent");
    123     System.clearProperty("http.proxyHost");
    124     System.clearProperty("http.proxyPort");
    125     System.clearProperty("https.proxyHost");
    126     System.clearProperty("https.proxyPort");
    127     if (cache != null) {
    128       cache.delete();
    129     }
    130   }
    131 
    132   @Test public void requestHeaders() throws IOException, InterruptedException {
    133     server.enqueue(new MockResponse());
    134 
    135     connection = client.open(server.getUrl("/"));
    136     connection.addRequestProperty("D", "e");
    137     connection.addRequestProperty("D", "f");
    138     assertEquals("f", connection.getRequestProperty("D"));
    139     assertEquals("f", connection.getRequestProperty("d"));
    140     Map<String, List<String>> requestHeaders = connection.getRequestProperties();
    141     assertEquals(newSet("e", "f"), new LinkedHashSet<>(requestHeaders.get("D")));
    142     assertEquals(newSet("e", "f"), new LinkedHashSet<>(requestHeaders.get("d")));
    143     try {
    144       requestHeaders.put("G", Arrays.asList("h"));
    145       fail("Modified an unmodifiable view.");
    146     } catch (UnsupportedOperationException expected) {
    147     }
    148     try {
    149       requestHeaders.get("D").add("i");
    150       fail("Modified an unmodifiable view.");
    151     } catch (UnsupportedOperationException expected) {
    152     }
    153     try {
    154       connection.setRequestProperty(null, "j");
    155       fail();
    156     } catch (NullPointerException expected) {
    157     }
    158     try {
    159       connection.addRequestProperty(null, "k");
    160       fail();
    161     } catch (NullPointerException expected) {
    162     }
    163     connection.setRequestProperty("NullValue", null);
    164     assertNull(connection.getRequestProperty("NullValue"));
    165     connection.addRequestProperty("AnotherNullValue", null);
    166     assertNull(connection.getRequestProperty("AnotherNullValue"));
    167 
    168     connection.getResponseCode();
    169     RecordedRequest request = server.takeRequest();
    170     assertEquals(Arrays.asList("e", "f"), request.getHeaders().values("D"));
    171     assertNull(request.getHeader("NullValue"));
    172     assertNull(request.getHeader("AnotherNullValue"));
    173     assertNull(request.getHeader("G"));
    174     assertNull(request.getHeader("null"));
    175 
    176     try {
    177       connection.addRequestProperty("N", "o");
    178       fail("Set header after connect");
    179     } catch (IllegalStateException expected) {
    180     }
    181     try {
    182       connection.setRequestProperty("P", "q");
    183       fail("Set header after connect");
    184     } catch (IllegalStateException expected) {
    185     }
    186     try {
    187       connection.getRequestProperties();
    188       fail();
    189     } catch (IllegalStateException expected) {
    190     }
    191   }
    192 
    193   @Test public void getRequestPropertyReturnsLastValue() throws Exception {
    194     connection = client.open(server.getUrl("/"));
    195     connection.addRequestProperty("A", "value1");
    196     connection.addRequestProperty("A", "value2");
    197     assertEquals("value2", connection.getRequestProperty("A"));
    198   }
    199 
    200   @Test public void responseHeaders() throws IOException, InterruptedException {
    201     server.enqueue(new MockResponse().setStatus("HTTP/1.0 200 Fantastic")
    202         .addHeader("A: c")
    203         .addHeader("B: d")
    204         .addHeader("A: e")
    205         .setChunkedBody("ABCDE\nFGHIJ\nKLMNO\nPQR", 8));
    206 
    207     connection = client.open(server.getUrl("/"));
    208     assertEquals(200, connection.getResponseCode());
    209     assertEquals("Fantastic", connection.getResponseMessage());
    210     assertEquals("HTTP/1.0 200 Fantastic", connection.getHeaderField(null));
    211     Map<String, List<String>> responseHeaders = connection.getHeaderFields();
    212     assertEquals(Arrays.asList("HTTP/1.0 200 Fantastic"), responseHeaders.get(null));
    213     assertEquals(newSet("c", "e"), new LinkedHashSet<>(responseHeaders.get("A")));
    214     assertEquals(newSet("c", "e"), new LinkedHashSet<>(responseHeaders.get("a")));
    215     try {
    216       responseHeaders.put("N", Arrays.asList("o"));
    217       fail("Modified an unmodifiable view.");
    218     } catch (UnsupportedOperationException expected) {
    219     }
    220     try {
    221       responseHeaders.get("A").add("f");
    222       fail("Modified an unmodifiable view.");
    223     } catch (UnsupportedOperationException expected) {
    224     }
    225     assertEquals("A", connection.getHeaderFieldKey(0));
    226     assertEquals("c", connection.getHeaderField(0));
    227     assertEquals("B", connection.getHeaderFieldKey(1));
    228     assertEquals("d", connection.getHeaderField(1));
    229     assertEquals("A", connection.getHeaderFieldKey(2));
    230     assertEquals("e", connection.getHeaderField(2));
    231     connection.getInputStream().close();
    232   }
    233 
    234   @Test public void serverSendsInvalidResponseHeaders() throws Exception {
    235     server.enqueue(new MockResponse().setStatus("HTP/1.1 200 OK"));
    236 
    237     connection = client.open(server.getUrl("/"));
    238     try {
    239       connection.getResponseCode();
    240       fail();
    241     } catch (IOException expected) {
    242     }
    243   }
    244 
    245   @Test public void serverSendsInvalidCodeTooLarge() throws Exception {
    246     server.enqueue(new MockResponse().setStatus("HTTP/1.1 2147483648 OK"));
    247 
    248     connection = client.open(server.getUrl("/"));
    249     try {
    250       connection.getResponseCode();
    251       fail();
    252     } catch (IOException expected) {
    253     }
    254   }
    255 
    256   @Test public void serverSendsInvalidCodeNotANumber() throws Exception {
    257     server.enqueue(new MockResponse().setStatus("HTTP/1.1 00a OK"));
    258 
    259     connection = client.open(server.getUrl("/"));
    260     try {
    261       connection.getResponseCode();
    262       fail();
    263     } catch (IOException expected) {
    264     }
    265   }
    266 
    267   @Test public void serverSendsUnnecessaryWhitespace() throws Exception {
    268     server.enqueue(new MockResponse().setStatus(" HTTP/1.1 2147483648 OK"));
    269 
    270     connection = client.open(server.getUrl("/"));
    271     try {
    272       connection.getResponseCode();
    273       fail();
    274     } catch (IOException expected) {
    275     }
    276   }
    277 
    278   @Test public void connectRetriesUntilConnectedOrFailed() throws Exception {
    279     URL url = server.getUrl("/foo");
    280     server.shutdown();
    281 
    282     connection = client.open(url);
    283     try {
    284       connection.connect();
    285       fail();
    286     } catch (IOException expected) {
    287     }
    288   }
    289 
    290   @Test public void requestBodySurvivesRetriesWithFixedLength() throws Exception {
    291     testRequestBodySurvivesRetries(TransferKind.FIXED_LENGTH);
    292   }
    293 
    294   @Test public void requestBodySurvivesRetriesWithChunkedStreaming() throws Exception {
    295     testRequestBodySurvivesRetries(TransferKind.CHUNKED);
    296   }
    297 
    298   @Test public void requestBodySurvivesRetriesWithBufferedBody() throws Exception {
    299     testRequestBodySurvivesRetries(TransferKind.END_OF_STREAM);
    300   }
    301 
    302   private void testRequestBodySurvivesRetries(TransferKind transferKind) throws Exception {
    303     server.enqueue(new MockResponse().setBody("abc"));
    304 
    305     // Use a misconfigured proxy to guarantee that the request is retried.
    306     FakeProxySelector proxySelector = new FakeProxySelector();
    307     proxySelector.proxies.add(server2.toProxyAddress());
    308     client.client().setProxySelector(proxySelector);
    309     server2.shutdown();
    310 
    311     connection = client.open(server.getUrl("/def"));
    312     connection.setDoOutput(true);
    313     transferKind.setForRequest(connection, 4);
    314     connection.getOutputStream().write("body".getBytes("UTF-8"));
    315     assertContent("abc", connection);
    316 
    317     assertEquals("body", server.takeRequest().getBody().readUtf8());
    318   }
    319 
    320   @Test public void getErrorStreamOnSuccessfulRequest() throws Exception {
    321     server.enqueue(new MockResponse().setBody("A"));
    322     connection = client.open(server.getUrl("/"));
    323     assertNull(connection.getErrorStream());
    324     connection.getInputStream().close();
    325   }
    326 
    327   @Test public void getErrorStreamOnUnsuccessfulRequest() throws Exception {
    328     server.enqueue(new MockResponse().setResponseCode(404).setBody("A"));
    329     connection = client.open(server.getUrl("/"));
    330     assertEquals("A", readAscii(connection.getErrorStream(), Integer.MAX_VALUE));
    331   }
    332 
    333   // Check that if we don't read to the end of a response, the next request on the
    334   // recycled connection doesn't get the unread tail of the first request's response.
    335   // http://code.google.com/p/android/issues/detail?id=2939
    336   @Test public void bug2939() throws Exception {
    337     MockResponse response = new MockResponse().setChunkedBody("ABCDE\nFGHIJ\nKLMNO\nPQR", 8);
    338 
    339     server.enqueue(response);
    340     server.enqueue(response);
    341 
    342     HttpURLConnection c1 = client.open(server.getUrl("/"));
    343     assertContent("ABCDE", c1, 5);
    344     HttpURLConnection c2 = client.open(server.getUrl("/"));
    345     assertContent("ABCDE", c2, 5);
    346 
    347     c1.getInputStream().close();
    348     c2.getInputStream().close();
    349   }
    350 
    351   // Check that we recognize a few basic mime types by extension.
    352   // http://code.google.com/p/android/issues/detail?id=10100
    353   @Test public void bug10100() throws Exception {
    354     assertEquals("image/jpeg", URLConnection.guessContentTypeFromName("someFile.jpg"));
    355     assertEquals("application/pdf", URLConnection.guessContentTypeFromName("stuff.pdf"));
    356   }
    357 
    358   @Test public void connectionsArePooled() throws Exception {
    359     MockResponse response = new MockResponse().setBody("ABCDEFGHIJKLMNOPQR");
    360 
    361     server.enqueue(response);
    362     server.enqueue(response);
    363     server.enqueue(response);
    364 
    365     assertContent("ABCDEFGHIJKLMNOPQR", client.open(server.getUrl("/foo")));
    366     assertEquals(0, server.takeRequest().getSequenceNumber());
    367     assertContent("ABCDEFGHIJKLMNOPQR", client.open(server.getUrl("/bar?baz=quux")));
    368     assertEquals(1, server.takeRequest().getSequenceNumber());
    369     assertContent("ABCDEFGHIJKLMNOPQR", client.open(server.getUrl("/z")));
    370     assertEquals(2, server.takeRequest().getSequenceNumber());
    371   }
    372 
    373   @Test public void chunkedConnectionsArePooled() throws Exception {
    374     MockResponse response = new MockResponse().setChunkedBody("ABCDEFGHIJKLMNOPQR", 5);
    375 
    376     server.enqueue(response);
    377     server.enqueue(response);
    378     server.enqueue(response);
    379 
    380     assertContent("ABCDEFGHIJKLMNOPQR", client.open(server.getUrl("/foo")));
    381     assertEquals(0, server.takeRequest().getSequenceNumber());
    382     assertContent("ABCDEFGHIJKLMNOPQR", client.open(server.getUrl("/bar?baz=quux")));
    383     assertEquals(1, server.takeRequest().getSequenceNumber());
    384     assertContent("ABCDEFGHIJKLMNOPQR", client.open(server.getUrl("/z")));
    385     assertEquals(2, server.takeRequest().getSequenceNumber());
    386   }
    387 
    388   @Test public void serverClosesSocket() throws Exception {
    389     testServerClosesOutput(DISCONNECT_AT_END);
    390   }
    391 
    392   @Test public void serverShutdownInput() throws Exception {
    393     testServerClosesOutput(SHUTDOWN_INPUT_AT_END);
    394   }
    395 
    396   @Test public void serverShutdownOutput() throws Exception {
    397     testServerClosesOutput(SHUTDOWN_OUTPUT_AT_END);
    398   }
    399 
    400   @Test public void invalidHost() throws Exception {
    401     // Note that 1234.1.1.1 is an invalid host in a URI, but URL isn't as strict.
    402     URL url = new URL("http://1234.1.1.1/index.html");
    403     HttpURLConnection connection = client.open(url);
    404     try {
    405       connection.connect();
    406       fail();
    407     } catch (UnknownHostException expected) {
    408     }
    409   }
    410 
    411   private void testServerClosesOutput(SocketPolicy socketPolicy) throws Exception {
    412     server.enqueue(new MockResponse().setBody("This connection won't pool properly")
    413         .setSocketPolicy(socketPolicy));
    414     MockResponse responseAfter = new MockResponse().setBody("This comes after a busted connection");
    415     server.enqueue(responseAfter);
    416     server.enqueue(responseAfter); // Enqueue 2x because the broken connection may be reused.
    417 
    418     HttpURLConnection connection1 = client.open(server.getUrl("/a"));
    419     connection1.setReadTimeout(100);
    420     assertContent("This connection won't pool properly", connection1);
    421     assertEquals(0, server.takeRequest().getSequenceNumber());
    422     HttpURLConnection connection2 = client.open(server.getUrl("/b"));
    423     connection2.setReadTimeout(100);
    424     assertContent("This comes after a busted connection", connection2);
    425 
    426     // Check that a fresh connection was created, either immediately or after attempting reuse.
    427     RecordedRequest requestAfter = server.takeRequest();
    428     if (server.getRequestCount() == 3) {
    429       requestAfter = server.takeRequest(); // The failure consumed a response.
    430     }
    431     // sequence number 0 means the HTTP socket connection was not reused
    432     assertEquals(0, requestAfter.getSequenceNumber());
    433   }
    434 
    435   enum WriteKind {BYTE_BY_BYTE, SMALL_BUFFERS, LARGE_BUFFERS}
    436 
    437   @Test public void chunkedUpload_byteByByte() throws Exception {
    438     doUpload(TransferKind.CHUNKED, WriteKind.BYTE_BY_BYTE);
    439   }
    440 
    441   @Test public void chunkedUpload_smallBuffers() throws Exception {
    442     doUpload(TransferKind.CHUNKED, WriteKind.SMALL_BUFFERS);
    443   }
    444 
    445   @Test public void chunkedUpload_largeBuffers() throws Exception {
    446     doUpload(TransferKind.CHUNKED, WriteKind.LARGE_BUFFERS);
    447   }
    448 
    449   @Test public void fixedLengthUpload_byteByByte() throws Exception {
    450     doUpload(TransferKind.FIXED_LENGTH, WriteKind.BYTE_BY_BYTE);
    451   }
    452 
    453   @Test public void fixedLengthUpload_smallBuffers() throws Exception {
    454     doUpload(TransferKind.FIXED_LENGTH, WriteKind.SMALL_BUFFERS);
    455   }
    456 
    457   @Test public void fixedLengthUpload_largeBuffers() throws Exception {
    458     doUpload(TransferKind.FIXED_LENGTH, WriteKind.LARGE_BUFFERS);
    459   }
    460 
    461   private void doUpload(TransferKind uploadKind, WriteKind writeKind) throws Exception {
    462     int n = 512 * 1024;
    463     server.setBodyLimit(0);
    464     server.enqueue(new MockResponse());
    465 
    466     HttpURLConnection conn = client.open(server.getUrl("/"));
    467     conn.setDoOutput(true);
    468     conn.setRequestMethod("POST");
    469     if (uploadKind == TransferKind.CHUNKED) {
    470       conn.setChunkedStreamingMode(-1);
    471     } else {
    472       conn.setFixedLengthStreamingMode(n);
    473     }
    474     OutputStream out = conn.getOutputStream();
    475     if (writeKind == WriteKind.BYTE_BY_BYTE) {
    476       for (int i = 0; i < n; ++i) {
    477         out.write('x');
    478       }
    479     } else {
    480       byte[] buf = new byte[writeKind == WriteKind.SMALL_BUFFERS ? 256 : 64 * 1024];
    481       Arrays.fill(buf, (byte) 'x');
    482       for (int i = 0; i < n; i += buf.length) {
    483         out.write(buf, 0, Math.min(buf.length, n - i));
    484       }
    485     }
    486     out.close();
    487     assertEquals(200, conn.getResponseCode());
    488     RecordedRequest request = server.takeRequest();
    489     assertEquals(n, request.getBodySize());
    490     if (uploadKind == TransferKind.CHUNKED) {
    491       assertTrue(request.getChunkSizes().size() > 0);
    492     } else {
    493       assertTrue(request.getChunkSizes().isEmpty());
    494     }
    495   }
    496 
    497   @Test public void getResponseCodeNoResponseBody() throws Exception {
    498     server.enqueue(new MockResponse().addHeader("abc: def"));
    499 
    500     URL url = server.getUrl("/");
    501     HttpURLConnection conn = client.open(url);
    502     conn.setDoInput(false);
    503     assertEquals("def", conn.getHeaderField("abc"));
    504     assertEquals(200, conn.getResponseCode());
    505     try {
    506       conn.getInputStream();
    507       fail();
    508     } catch (ProtocolException expected) {
    509     }
    510   }
    511 
    512   @Test public void connectViaHttps() throws Exception {
    513     server.useHttps(sslContext.getSocketFactory(), false);
    514     server.enqueue(new MockResponse().setBody("this response comes via HTTPS"));
    515 
    516     client.client().setSslSocketFactory(sslContext.getSocketFactory());
    517     client.client().setHostnameVerifier(new RecordingHostnameVerifier());
    518     connection = client.open(server.getUrl("/foo"));
    519 
    520     assertContent("this response comes via HTTPS", connection);
    521 
    522     RecordedRequest request = server.takeRequest();
    523     assertEquals("GET /foo HTTP/1.1", request.getRequestLine());
    524   }
    525 
    526   @Test public void inspectHandshakeThroughoutRequestLifecycle() throws Exception {
    527     server.useHttps(sslContext.getSocketFactory(), false);
    528     server.enqueue(new MockResponse());
    529 
    530     client.client().setSslSocketFactory(sslContext.getSocketFactory());
    531     client.client().setHostnameVerifier(new RecordingHostnameVerifier());
    532 
    533     HttpsURLConnection httpsConnection = (HttpsURLConnection) client.open(server.getUrl("/foo"));
    534 
    535     // Prior to calling connect(), getting the cipher suite is forbidden.
    536     try {
    537       httpsConnection.getCipherSuite();
    538       fail();
    539     } catch (IllegalStateException expected) {
    540     }
    541 
    542     // Calling connect establishes a handshake...
    543     httpsConnection.connect();
    544     assertNotNull(httpsConnection.getCipherSuite());
    545 
    546     // ...which remains after we read the response body...
    547     assertContent("", httpsConnection);
    548     assertNotNull(httpsConnection.getCipherSuite());
    549 
    550     // ...and after we disconnect.
    551     httpsConnection.disconnect();
    552     assertNotNull(httpsConnection.getCipherSuite());
    553   }
    554 
    555   @Test public void connectViaHttpsReusingConnections() throws IOException, InterruptedException {
    556     server.useHttps(sslContext.getSocketFactory(), false);
    557     server.enqueue(new MockResponse().setBody("this response comes via HTTPS"));
    558     server.enqueue(new MockResponse().setBody("another response via HTTPS"));
    559 
    560     // The pool will only reuse sockets if the SSL socket factories are the same.
    561     SSLSocketFactory clientSocketFactory = sslContext.getSocketFactory();
    562     RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier();
    563 
    564     client.client().setSslSocketFactory(clientSocketFactory);
    565     client.client().setHostnameVerifier(hostnameVerifier);
    566     connection = client.open(server.getUrl("/"));
    567     assertContent("this response comes via HTTPS", connection);
    568 
    569     connection = client.open(server.getUrl("/"));
    570     assertContent("another response via HTTPS", connection);
    571 
    572     assertEquals(0, server.takeRequest().getSequenceNumber());
    573     assertEquals(1, server.takeRequest().getSequenceNumber());
    574   }
    575 
    576   @Test public void connectViaHttpsReusingConnectionsDifferentFactories()
    577       throws IOException, InterruptedException {
    578     server.useHttps(sslContext.getSocketFactory(), false);
    579     server.enqueue(new MockResponse().setBody("this response comes via HTTPS"));
    580     server.enqueue(new MockResponse().setBody("another response via HTTPS"));
    581 
    582     // install a custom SSL socket factory so the server can be authorized
    583     client.client().setSslSocketFactory(sslContext.getSocketFactory());
    584     client.client().setHostnameVerifier(new RecordingHostnameVerifier());
    585     HttpURLConnection connection1 = client.open(server.getUrl("/"));
    586     assertContent("this response comes via HTTPS", connection1);
    587 
    588     client.client().setSslSocketFactory(null);
    589     HttpURLConnection connection2 = client.open(server.getUrl("/"));
    590     try {
    591       readAscii(connection2.getInputStream(), Integer.MAX_VALUE);
    592       fail("without an SSL socket factory, the connection should fail");
    593     } catch (SSLException expected) {
    594     }
    595   }
    596 
    597   @Test public void connectViaHttpsWithSSLFallback() throws Exception {
    598     server.useHttps(sslContext.getSocketFactory(), false);
    599     server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE));
    600     server.enqueue(new MockResponse().setBody("this response comes via SSL"));
    601 
    602     suppressTlsFallbackScsv(client.client());
    603     client.client().setHostnameVerifier(new RecordingHostnameVerifier());
    604     connection = client.open(server.getUrl("/foo"));
    605 
    606     assertContent("this response comes via SSL", connection);
    607 
    608     RecordedRequest request = server.takeRequest();
    609     assertEquals("GET /foo HTTP/1.1", request.getRequestLine());
    610     assertEquals(TlsVersion.TLS_1_0, request.getTlsVersion());
    611   }
    612 
    613   @Test public void connectViaHttpsWithSSLFallbackFailuresRecorded() throws Exception {
    614     server.useHttps(sslContext.getSocketFactory(), false);
    615     server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE));
    616     server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE));
    617 
    618     suppressTlsFallbackScsv(client.client());
    619     client.client().setDns(new SingleInetAddressDns());
    620 
    621     client.client().setHostnameVerifier(new RecordingHostnameVerifier());
    622     connection = client.open(server.getUrl("/foo"));
    623 
    624     try {
    625       connection.getResponseCode();
    626       fail();
    627     } catch (IOException expected) {
    628       assertEquals(1, expected.getSuppressed().length);
    629     }
    630   }
    631 
    632   /**
    633    * When a pooled connection fails, don't blame the route. Otherwise pooled
    634    * connection failures can cause unnecessary SSL fallbacks.
    635    *
    636    * https://github.com/square/okhttp/issues/515
    637    */
    638   @Test public void sslFallbackNotUsedWhenRecycledConnectionFails() throws Exception {
    639     server.useHttps(sslContext.getSocketFactory(), false);
    640     server.enqueue(new MockResponse()
    641         .setBody("abc")
    642         .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END));
    643     server.enqueue(new MockResponse().setBody("def"));
    644 
    645     suppressTlsFallbackScsv(client.client());
    646     client.client().setHostnameVerifier(new RecordingHostnameVerifier());
    647 
    648     assertContent("abc", client.open(server.getUrl("/")));
    649     assertContent("def", client.open(server.getUrl("/")));
    650 
    651     Set<TlsVersion> tlsVersions =
    652         EnumSet.of(TlsVersion.TLS_1_0, TlsVersion.TLS_1_2); // v1.2 on OpenJDK 8.
    653 
    654     RecordedRequest request1 = server.takeRequest();
    655     assertTrue(tlsVersions.contains(request1.getTlsVersion()));
    656 
    657     RecordedRequest request2 = server.takeRequest();
    658     assertTrue(tlsVersions.contains(request2.getTlsVersion()));
    659   }
    660 
    661   /**
    662    * Verify that we don't retry connections on certificate verification errors.
    663    *
    664    * http://code.google.com/p/android/issues/detail?id=13178
    665    */
    666   @Test public void connectViaHttpsToUntrustedServer() throws IOException, InterruptedException {
    667     server.useHttps(sslContext.getSocketFactory(), false);
    668     server.enqueue(new MockResponse()); // unused
    669 
    670     connection = client.open(server.getUrl("/foo"));
    671     try {
    672       connection.getInputStream();
    673       fail();
    674     } catch (SSLHandshakeException expected) {
    675       assertTrue(expected.getCause() instanceof CertificateException);
    676     }
    677     assertEquals(0, server.getRequestCount());
    678   }
    679 
    680   @Test public void connectViaProxyUsingProxyArg() throws Exception {
    681     testConnectViaProxy(ProxyConfig.CREATE_ARG);
    682   }
    683 
    684   @Test public void connectViaProxyUsingProxySystemProperty() throws Exception {
    685     testConnectViaProxy(ProxyConfig.PROXY_SYSTEM_PROPERTY);
    686   }
    687 
    688   @Test public void connectViaProxyUsingHttpProxySystemProperty() throws Exception {
    689     testConnectViaProxy(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY);
    690   }
    691 
    692   private void testConnectViaProxy(ProxyConfig proxyConfig) throws Exception {
    693     MockResponse mockResponse = new MockResponse().setBody("this response comes via a proxy");
    694     server.enqueue(mockResponse);
    695 
    696     URL url = new URL("http://android.com/foo");
    697     connection = proxyConfig.connect(server, client, url);
    698     assertContent("this response comes via a proxy", connection);
    699     assertTrue(connection.usingProxy());
    700 
    701     RecordedRequest request = server.takeRequest();
    702     assertEquals("GET http://android.com/foo HTTP/1.1", request.getRequestLine());
    703     assertEquals("android.com", request.getHeader("Host"));
    704   }
    705 
    706   @Test public void contentDisagreesWithContentLengthHeaderBodyTooLong() throws IOException {
    707     server.enqueue(new MockResponse().setBody("abc\r\nYOU SHOULD NOT SEE THIS")
    708         .clearHeaders()
    709         .addHeader("Content-Length: 3"));
    710     assertContent("abc", client.open(server.getUrl("/")));
    711   }
    712 
    713   @Test public void contentDisagreesWithContentLengthHeaderBodyTooShort() throws IOException {
    714     server.enqueue(new MockResponse().setBody("abc")
    715         .setHeader("Content-Length", "5")
    716         .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END));
    717     try {
    718       readAscii(client.open(server.getUrl("/")).getInputStream(), 5);
    719       fail();
    720     } catch (ProtocolException expected) {
    721     }
    722   }
    723 
    724   public void testConnectViaSocketFactory(boolean useHttps) throws IOException {
    725     SocketFactory uselessSocketFactory = new SocketFactory() {
    726       public Socket createSocket() { throw new IllegalArgumentException("useless"); }
    727       public Socket createSocket(InetAddress host, int port) { return null; }
    728       public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
    729           int localPort) { return null; }
    730       public Socket createSocket(String host, int port) { return null; }
    731       public Socket createSocket(String host, int port, InetAddress localHost, int localPort) {
    732         return null;
    733       }
    734     };
    735 
    736     if (useHttps) {
    737       server.useHttps(sslContext.getSocketFactory(), false);
    738       client.client().setSslSocketFactory(sslContext.getSocketFactory());
    739       client.client().setHostnameVerifier(new RecordingHostnameVerifier());
    740     }
    741 
    742     server.enqueue(new MockResponse().setStatus("HTTP/1.1 200 OK"));
    743 
    744     client.client().setSocketFactory(uselessSocketFactory);
    745     connection = client.open(server.getUrl("/"));
    746     try {
    747       connection.getResponseCode();
    748       fail();
    749     } catch (IllegalArgumentException expected) {
    750     }
    751 
    752     client.client().setSocketFactory(SocketFactory.getDefault());
    753     connection = client.open(server.getUrl("/"));
    754     assertEquals(200, connection.getResponseCode());
    755   }
    756 
    757   @Test public void connectHttpViaSocketFactory() throws Exception {
    758     testConnectViaSocketFactory(false);
    759   }
    760 
    761   @Test public void connectHttpsViaSocketFactory() throws Exception {
    762     testConnectViaSocketFactory(true);
    763   }
    764 
    765   @Test public void contentDisagreesWithChunkedHeaderBodyTooLong() throws IOException {
    766     MockResponse mockResponse = new MockResponse();
    767     mockResponse.setChunkedBody("abc", 3);
    768     Buffer buffer = mockResponse.getBody();
    769     buffer.writeUtf8("\r\nYOU SHOULD NOT SEE THIS");
    770     mockResponse.setBody(buffer);
    771     mockResponse.clearHeaders();
    772     mockResponse.addHeader("Transfer-encoding: chunked");
    773 
    774     server.enqueue(mockResponse);
    775 
    776     assertContent("abc", client.open(server.getUrl("/")));
    777   }
    778 
    779   @Test public void contentDisagreesWithChunkedHeaderBodyTooShort() throws IOException {
    780     MockResponse mockResponse = new MockResponse();
    781     mockResponse.setChunkedBody("abcde", 5);
    782 
    783     Buffer truncatedBody = new Buffer();
    784     Buffer fullBody = mockResponse.getBody();
    785     truncatedBody.write(fullBody, fullBody.indexOf((byte) 'e'));
    786     mockResponse.setBody(truncatedBody);
    787 
    788     mockResponse.clearHeaders();
    789     mockResponse.addHeader("Transfer-encoding: chunked");
    790     mockResponse.setSocketPolicy(SocketPolicy.DISCONNECT_AT_END);
    791 
    792     server.enqueue(mockResponse);
    793 
    794     try {
    795       readAscii(client.open(server.getUrl("/")).getInputStream(), 5);
    796       fail();
    797     } catch (ProtocolException expected) {
    798     }
    799   }
    800 
    801   @Test public void connectViaHttpProxyToHttpsUsingProxyArgWithNoProxy() throws Exception {
    802     testConnectViaDirectProxyToHttps(ProxyConfig.NO_PROXY);
    803   }
    804 
    805   @Test public void connectViaHttpProxyToHttpsUsingHttpProxySystemProperty() throws Exception {
    806     // https should not use http proxy
    807     testConnectViaDirectProxyToHttps(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY);
    808   }
    809 
    810   private void testConnectViaDirectProxyToHttps(ProxyConfig proxyConfig) throws Exception {
    811     server.useHttps(sslContext.getSocketFactory(), false);
    812     server.enqueue(new MockResponse().setBody("this response comes via HTTPS"));
    813 
    814     URL url = server.getUrl("/foo");
    815     client.client().setSslSocketFactory(sslContext.getSocketFactory());
    816     client.client().setHostnameVerifier(new RecordingHostnameVerifier());
    817     connection = proxyConfig.connect(server, client, url);
    818 
    819     assertContent("this response comes via HTTPS", connection);
    820 
    821     RecordedRequest request = server.takeRequest();
    822     assertEquals("GET /foo HTTP/1.1", request.getRequestLine());
    823   }
    824 
    825   @Test public void connectViaHttpProxyToHttpsUsingProxyArg() throws Exception {
    826     testConnectViaHttpProxyToHttps(ProxyConfig.CREATE_ARG);
    827   }
    828 
    829   /**
    830    * We weren't honoring all of the appropriate proxy system properties when
    831    * connecting via HTTPS. http://b/3097518
    832    */
    833   @Test public void connectViaHttpProxyToHttpsUsingProxySystemProperty() throws Exception {
    834     testConnectViaHttpProxyToHttps(ProxyConfig.PROXY_SYSTEM_PROPERTY);
    835   }
    836 
    837   @Test public void connectViaHttpProxyToHttpsUsingHttpsProxySystemProperty() throws Exception {
    838     testConnectViaHttpProxyToHttps(ProxyConfig.HTTPS_PROXY_SYSTEM_PROPERTY);
    839   }
    840 
    841   /**
    842    * We were verifying the wrong hostname when connecting to an HTTPS site
    843    * through a proxy. http://b/3097277
    844    */
    845   private void testConnectViaHttpProxyToHttps(ProxyConfig proxyConfig) throws Exception {
    846     RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier();
    847 
    848     server.useHttps(sslContext.getSocketFactory(), true);
    849     server.enqueue(
    850         new MockResponse().setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END).clearHeaders());
    851     server.enqueue(new MockResponse().setBody("this response comes via a secure proxy"));
    852 
    853     URL url = new URL("https://android.com/foo");
    854     client.client().setSslSocketFactory(sslContext.getSocketFactory());
    855     client.client().setHostnameVerifier(hostnameVerifier);
    856     connection = proxyConfig.connect(server, client, url);
    857 
    858     assertContent("this response comes via a secure proxy", connection);
    859 
    860     RecordedRequest connect = server.takeRequest();
    861     assertEquals("Connect line failure on proxy", "CONNECT android.com:443 HTTP/1.1",
    862         connect.getRequestLine());
    863     assertEquals("android.com:443", connect.getHeader("Host"));
    864 
    865     RecordedRequest get = server.takeRequest();
    866     assertEquals("GET /foo HTTP/1.1", get.getRequestLine());
    867     assertEquals("android.com", get.getHeader("Host"));
    868     assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls);
    869   }
    870 
    871   /** Tolerate bad https proxy response when using HttpResponseCache. Android bug 6754912. */
    872   @Test public void connectViaHttpProxyToHttpsUsingBadProxyAndHttpResponseCache() throws Exception {
    873     initResponseCache();
    874 
    875     server.useHttps(sslContext.getSocketFactory(), true);
    876     // The inclusion of a body in the response to a CONNECT is key to reproducing b/6754912.
    877     MockResponse badProxyResponse = new MockResponse()
    878         .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END)
    879         .setBody("bogus proxy connect response content");
    880     server.enqueue(badProxyResponse);
    881     server.enqueue(new MockResponse().setBody("response"));
    882 
    883     // Configure a single IP address for the host and a single configuration, so we only need one
    884     // failure to fail permanently.
    885     client.client().setDns(new SingleInetAddressDns());
    886     client.client().setSslSocketFactory(sslContext.getSocketFactory());
    887     client.client().setConnectionSpecs(Util.immutableList(ConnectionSpec.MODERN_TLS));
    888     client.client().setHostnameVerifier(new RecordingHostnameVerifier());
    889     client.client().setProxy(server.toProxyAddress());
    890 
    891     URL url = new URL("https://android.com/foo");
    892     connection = client.open(url);
    893     assertContent("response", connection);
    894 
    895     RecordedRequest connect = server.takeRequest();
    896     assertEquals("CONNECT android.com:443 HTTP/1.1", connect.getRequestLine());
    897     assertEquals("android.com:443", connect.getHeader("Host"));
    898   }
    899 
    900   private void initResponseCache() throws IOException {
    901     cache = new Cache(tempDir.getRoot(), Integer.MAX_VALUE);
    902     client.client().setCache(cache);
    903   }
    904 
    905   /** Test which headers are sent unencrypted to the HTTP proxy. */
    906   @Test public void proxyConnectIncludesProxyHeadersOnly()
    907       throws IOException, InterruptedException {
    908     RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier();
    909 
    910     server.useHttps(sslContext.getSocketFactory(), true);
    911     server.enqueue(
    912         new MockResponse().setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END).clearHeaders());
    913     server.enqueue(new MockResponse().setBody("encrypted response from the origin server"));
    914 
    915     client.client().setProxy(server.toProxyAddress());
    916 
    917     URL url = new URL("https://android.com/foo");
    918     client.client().setSslSocketFactory(sslContext.getSocketFactory());
    919     client.client().setHostnameVerifier(hostnameVerifier);
    920     connection = client.open(url);
    921     connection.addRequestProperty("Private", "Secret");
    922     connection.addRequestProperty("Proxy-Authorization", "bar");
    923     connection.addRequestProperty("User-Agent", "baz");
    924     assertContent("encrypted response from the origin server", connection);
    925 
    926     RecordedRequest connect = server.takeRequest();
    927     assertNull(connect.getHeader("Private"));
    928     assertNull(connect.getHeader("Proxy-Authorization"));
    929     assertEquals(Version.userAgent(), connect.getHeader("User-Agent"));
    930     assertEquals("android.com:443", connect.getHeader("Host"));
    931     assertEquals("Keep-Alive", connect.getHeader("Proxy-Connection"));
    932 
    933     RecordedRequest get = server.takeRequest();
    934     assertEquals("Secret", get.getHeader("Private"));
    935     assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls);
    936   }
    937 
    938   @Test public void proxyAuthenticateOnConnect() throws Exception {
    939     Authenticator.setDefault(new RecordingAuthenticator());
    940     server.useHttps(sslContext.getSocketFactory(), true);
    941     server.enqueue(new MockResponse().setResponseCode(407)
    942         .addHeader("Proxy-Authenticate: Basic realm=\"localhost\""));
    943     server.enqueue(
    944         new MockResponse().setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END).clearHeaders());
    945     server.enqueue(new MockResponse().setBody("A"));
    946 
    947     client.client().setProxy(server.toProxyAddress());
    948 
    949     URL url = new URL("https://android.com/foo");
    950     client.client().setSslSocketFactory(sslContext.getSocketFactory());
    951     client.client().setHostnameVerifier(new RecordingHostnameVerifier());
    952     connection = client.open(url);
    953     assertContent("A", connection);
    954 
    955     RecordedRequest connect1 = server.takeRequest();
    956     assertEquals("CONNECT android.com:443 HTTP/1.1", connect1.getRequestLine());
    957     assertNull(connect1.getHeader("Proxy-Authorization"));
    958 
    959     RecordedRequest connect2 = server.takeRequest();
    960     assertEquals("CONNECT android.com:443 HTTP/1.1", connect2.getRequestLine());
    961     assertEquals("Basic " + RecordingAuthenticator.BASE_64_CREDENTIALS,
    962         connect2.getHeader("Proxy-Authorization"));
    963 
    964     RecordedRequest get = server.takeRequest();
    965     assertEquals("GET /foo HTTP/1.1", get.getRequestLine());
    966     assertNull(get.getHeader("Proxy-Authorization"));
    967   }
    968 
    969   // Don't disconnect after building a tunnel with CONNECT
    970   // http://code.google.com/p/android/issues/detail?id=37221
    971   @Test public void proxyWithConnectionClose() throws IOException {
    972     server.useHttps(sslContext.getSocketFactory(), true);
    973     server.enqueue(
    974         new MockResponse().setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END).clearHeaders());
    975     server.enqueue(new MockResponse().setBody("this response comes via a proxy"));
    976 
    977     client.client().setProxy(server.toProxyAddress());
    978 
    979     URL url = new URL("https://android.com/foo");
    980     client.client().setSslSocketFactory(sslContext.getSocketFactory());
    981     client.client().setHostnameVerifier(new RecordingHostnameVerifier());
    982     connection = client.open(url);
    983     connection.setRequestProperty("Connection", "close");
    984 
    985     assertContent("this response comes via a proxy", connection);
    986   }
    987 
    988   @Test public void proxyWithConnectionReuse() throws IOException {
    989     SSLSocketFactory socketFactory = sslContext.getSocketFactory();
    990     RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier();
    991 
    992     server.useHttps(socketFactory, true);
    993     server.enqueue(
    994         new MockResponse().setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END).clearHeaders());
    995     server.enqueue(new MockResponse().setBody("response 1"));
    996     server.enqueue(new MockResponse().setBody("response 2"));
    997 
    998     client.client().setProxy(server.toProxyAddress());
    999 
   1000     URL url = new URL("https://android.com/foo");
   1001     client.client().setSslSocketFactory(socketFactory);
   1002     client.client().setHostnameVerifier(hostnameVerifier);
   1003     assertContent("response 1", client.open(url));
   1004     assertContent("response 2", client.open(url));
   1005   }
   1006 
   1007   @Test public void disconnectedConnection() throws IOException {
   1008     server.enqueue(new MockResponse()
   1009         .throttleBody(2, 100, TimeUnit.MILLISECONDS)
   1010         .setBody("ABCD"));
   1011 
   1012     connection = client.open(server.getUrl("/"));
   1013     InputStream in = connection.getInputStream();
   1014     assertEquals('A', (char) in.read());
   1015     connection.disconnect();
   1016     try {
   1017       // Reading 'B' may succeed if it's buffered.
   1018       in.read();
   1019 
   1020       // But 'C' shouldn't be buffered (the response is throttled) and this should fail.
   1021       in.read();
   1022       fail("Expected a connection closed exception");
   1023     } catch (IOException expected) {
   1024     }
   1025     in.close();
   1026   }
   1027 
   1028   @Test public void disconnectBeforeConnect() throws IOException {
   1029     server.enqueue(new MockResponse().setBody("A"));
   1030 
   1031     connection = client.open(server.getUrl("/"));
   1032     connection.disconnect();
   1033     assertContent("A", connection);
   1034     assertEquals(200, connection.getResponseCode());
   1035   }
   1036 
   1037   @SuppressWarnings("deprecation") @Test public void defaultRequestProperty() throws Exception {
   1038     URLConnection.setDefaultRequestProperty("X-testSetDefaultRequestProperty", "A");
   1039     assertNull(URLConnection.getDefaultRequestProperty("X-setDefaultRequestProperty"));
   1040   }
   1041 
   1042   /**
   1043    * Reads {@code count} characters from the stream. If the stream is
   1044    * exhausted before {@code count} characters can be read, the remaining
   1045    * characters are returned and the stream is closed.
   1046    */
   1047   private String readAscii(InputStream in, int count) throws IOException {
   1048     StringBuilder result = new StringBuilder();
   1049     for (int i = 0; i < count; i++) {
   1050       int value = in.read();
   1051       if (value == -1) {
   1052         in.close();
   1053         break;
   1054       }
   1055       result.append((char) value);
   1056     }
   1057     return result.toString();
   1058   }
   1059 
   1060   @Test public void markAndResetWithContentLengthHeader() throws IOException {
   1061     testMarkAndReset(TransferKind.FIXED_LENGTH);
   1062   }
   1063 
   1064   @Test public void markAndResetWithChunkedEncoding() throws IOException {
   1065     testMarkAndReset(TransferKind.CHUNKED);
   1066   }
   1067 
   1068   @Test public void markAndResetWithNoLengthHeaders() throws IOException {
   1069     testMarkAndReset(TransferKind.END_OF_STREAM);
   1070   }
   1071 
   1072   private void testMarkAndReset(TransferKind transferKind) throws IOException {
   1073     MockResponse response = new MockResponse();
   1074     transferKind.setBody(response, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1024);
   1075     server.enqueue(response);
   1076     server.enqueue(response);
   1077 
   1078     InputStream in = client.open(server.getUrl("/")).getInputStream();
   1079     assertFalse("This implementation claims to support mark().", in.markSupported());
   1080     in.mark(5);
   1081     assertEquals("ABCDE", readAscii(in, 5));
   1082     try {
   1083       in.reset();
   1084       fail();
   1085     } catch (IOException expected) {
   1086     }
   1087     assertEquals("FGHIJKLMNOPQRSTUVWXYZ", readAscii(in, Integer.MAX_VALUE));
   1088     in.close();
   1089     assertContent("ABCDEFGHIJKLMNOPQRSTUVWXYZ", client.open(server.getUrl("/")));
   1090   }
   1091 
   1092   /**
   1093    * We've had a bug where we forget the HTTP response when we see response
   1094    * code 401. This causes a new HTTP request to be issued for every call into
   1095    * the URLConnection.
   1096    */
   1097   @Test public void unauthorizedResponseHandling() throws IOException {
   1098     MockResponse response = new MockResponse().addHeader("WWW-Authenticate: challenge")
   1099         .setResponseCode(401) // UNAUTHORIZED
   1100         .setBody("Unauthorized");
   1101     server.enqueue(response);
   1102     server.enqueue(response);
   1103     server.enqueue(response);
   1104 
   1105     URL url = server.getUrl("/");
   1106     HttpURLConnection conn = client.open(url);
   1107 
   1108     assertEquals(401, conn.getResponseCode());
   1109     assertEquals(401, conn.getResponseCode());
   1110     assertEquals(401, conn.getResponseCode());
   1111     assertEquals(1, server.getRequestCount());
   1112     conn.getErrorStream().close();
   1113   }
   1114 
   1115   @Test public void nonHexChunkSize() throws IOException {
   1116     server.enqueue(new MockResponse().setBody("5\r\nABCDE\r\nG\r\nFGHIJKLMNOPQRSTU\r\n0\r\n\r\n")
   1117         .clearHeaders()
   1118         .addHeader("Transfer-encoding: chunked"));
   1119 
   1120     URLConnection connection = client.open(server.getUrl("/"));
   1121     try {
   1122       readAscii(connection.getInputStream(), Integer.MAX_VALUE);
   1123       fail();
   1124     } catch (IOException e) {
   1125     }
   1126     connection.getInputStream().close();
   1127   }
   1128 
   1129   @Test public void malformedChunkSize() throws IOException {
   1130     server.enqueue(new MockResponse().setBody("5:x\r\nABCDE\r\n0\r\n\r\n")
   1131         .clearHeaders()
   1132         .addHeader("Transfer-encoding: chunked"));
   1133 
   1134     URLConnection connection = client.open(server.getUrl("/"));
   1135     try {
   1136       readAscii(connection.getInputStream(), Integer.MAX_VALUE);
   1137       fail();
   1138     } catch (IOException e) {
   1139     } finally {
   1140       connection.getInputStream().close();
   1141     }
   1142   }
   1143 
   1144   @Test public void extensionAfterChunkSize() throws IOException {
   1145     server.enqueue(new MockResponse().setBody("5;x\r\nABCDE\r\n0\r\n\r\n")
   1146         .clearHeaders()
   1147         .addHeader("Transfer-encoding: chunked"));
   1148 
   1149     HttpURLConnection connection = client.open(server.getUrl("/"));
   1150     assertContent("ABCDE", connection);
   1151   }
   1152 
   1153   @Test public void missingChunkBody() throws IOException {
   1154     server.enqueue(new MockResponse().setBody("5")
   1155         .clearHeaders()
   1156         .addHeader("Transfer-encoding: chunked")
   1157         .setSocketPolicy(DISCONNECT_AT_END));
   1158 
   1159     URLConnection connection = client.open(server.getUrl("/"));
   1160     try {
   1161       readAscii(connection.getInputStream(), Integer.MAX_VALUE);
   1162       fail();
   1163     } catch (IOException e) {
   1164     } finally {
   1165       connection.getInputStream().close();
   1166     }
   1167   }
   1168 
   1169   /**
   1170    * This test checks whether connections are gzipped by default. This
   1171    * behavior in not required by the API, so a failure of this test does not
   1172    * imply a bug in the implementation.
   1173    */
   1174   @Test public void gzipEncodingEnabledByDefault() throws IOException, InterruptedException {
   1175     server.enqueue(new MockResponse()
   1176         .setBody(gzip("ABCABCABC"))
   1177         .addHeader("Content-Encoding: gzip"));
   1178 
   1179     URLConnection connection = client.open(server.getUrl("/"));
   1180     assertEquals("ABCABCABC", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   1181     assertNull(connection.getContentEncoding());
   1182     assertEquals(-1, connection.getContentLength());
   1183 
   1184     RecordedRequest request = server.takeRequest();
   1185     assertEquals("gzip", request.getHeader("Accept-Encoding"));
   1186   }
   1187 
   1188   @Test public void clientConfiguredGzipContentEncoding() throws Exception {
   1189     Buffer bodyBytes = gzip("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
   1190     server.enqueue(new MockResponse()
   1191         .setBody(bodyBytes)
   1192         .addHeader("Content-Encoding: gzip"));
   1193 
   1194     URLConnection connection = client.open(server.getUrl("/"));
   1195     connection.addRequestProperty("Accept-Encoding", "gzip");
   1196     InputStream gunzippedIn = new GZIPInputStream(connection.getInputStream());
   1197     assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZ", readAscii(gunzippedIn, Integer.MAX_VALUE));
   1198     assertEquals(bodyBytes.size(), connection.getContentLength());
   1199 
   1200     RecordedRequest request = server.takeRequest();
   1201     assertEquals("gzip", request.getHeader("Accept-Encoding"));
   1202   }
   1203 
   1204   @Test public void gzipAndConnectionReuseWithFixedLength() throws Exception {
   1205     testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.FIXED_LENGTH, false);
   1206   }
   1207 
   1208   @Test public void gzipAndConnectionReuseWithChunkedEncoding() throws Exception {
   1209     testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.CHUNKED, false);
   1210   }
   1211 
   1212   @Test public void gzipAndConnectionReuseWithFixedLengthAndTls() throws Exception {
   1213     testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.FIXED_LENGTH, true);
   1214   }
   1215 
   1216   @Test public void gzipAndConnectionReuseWithChunkedEncodingAndTls() throws Exception {
   1217     testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.CHUNKED, true);
   1218   }
   1219 
   1220   @Test public void clientConfiguredCustomContentEncoding() throws Exception {
   1221     server.enqueue(new MockResponse().setBody("ABCDE").addHeader("Content-Encoding: custom"));
   1222 
   1223     URLConnection connection = client.open(server.getUrl("/"));
   1224     connection.addRequestProperty("Accept-Encoding", "custom");
   1225     assertEquals("ABCDE", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   1226 
   1227     RecordedRequest request = server.takeRequest();
   1228     assertEquals("custom", request.getHeader("Accept-Encoding"));
   1229   }
   1230 
   1231   /**
   1232    * Test a bug where gzip input streams weren't exhausting the input stream,
   1233    * which corrupted the request that followed or prevented connection reuse.
   1234    * http://code.google.com/p/android/issues/detail?id=7059
   1235    * http://code.google.com/p/android/issues/detail?id=38817
   1236    */
   1237   private void testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind transferKind,
   1238       boolean tls) throws Exception {
   1239     if (tls) {
   1240       SSLSocketFactory socketFactory = sslContext.getSocketFactory();
   1241       RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier();
   1242       server.useHttps(socketFactory, false);
   1243       client.client().setSslSocketFactory(socketFactory);
   1244       client.client().setHostnameVerifier(hostnameVerifier);
   1245     }
   1246 
   1247     MockResponse responseOne = new MockResponse();
   1248     responseOne.addHeader("Content-Encoding: gzip");
   1249     transferKind.setBody(responseOne, gzip("one (gzipped)"), 5);
   1250     server.enqueue(responseOne);
   1251     MockResponse responseTwo = new MockResponse();
   1252     transferKind.setBody(responseTwo, "two (identity)", 5);
   1253     server.enqueue(responseTwo);
   1254 
   1255     HttpURLConnection connection1 = client.open(server.getUrl("/"));
   1256     connection1.addRequestProperty("Accept-Encoding", "gzip");
   1257     InputStream gunzippedIn = new GZIPInputStream(connection1.getInputStream());
   1258     assertEquals("one (gzipped)", readAscii(gunzippedIn, Integer.MAX_VALUE));
   1259     assertEquals(0, server.takeRequest().getSequenceNumber());
   1260 
   1261     HttpURLConnection connection2 = client.open(server.getUrl("/"));
   1262     assertEquals("two (identity)", readAscii(connection2.getInputStream(), Integer.MAX_VALUE));
   1263     assertEquals(1, server.takeRequest().getSequenceNumber());
   1264   }
   1265 
   1266   @Test public void transparentGzipWorksAfterExceptionRecovery() throws Exception {
   1267     server.enqueue(new MockResponse()
   1268         .setBody("a")
   1269         .setSocketPolicy(SHUTDOWN_INPUT_AT_END));
   1270     server.enqueue(new MockResponse()
   1271         .addHeader("Content-Encoding: gzip")
   1272         .setBody(gzip("b")));
   1273 
   1274     // Seed the pool with a bad connection.
   1275     assertContent("a", client.open(server.getUrl("/")));
   1276 
   1277     // This connection will need to be recovered. When it is, transparent gzip should still work!
   1278     assertContent("b", client.open(server.getUrl("/")));
   1279 
   1280     assertEquals(0, server.takeRequest().getSequenceNumber());
   1281     assertEquals(0, server.takeRequest().getSequenceNumber()); // Connection is not pooled.
   1282   }
   1283 
   1284   @Test public void endOfStreamResponseIsNotPooled() throws Exception {
   1285     server.enqueue(new MockResponse()
   1286         .setBody("{}")
   1287         .clearHeaders()
   1288         .setSocketPolicy(DISCONNECT_AT_END));
   1289 
   1290     ConnectionPool pool = ConnectionPool.getDefault();
   1291     pool.evictAll();
   1292     client.client().setConnectionPool(pool);
   1293 
   1294     HttpURLConnection connection = client.open(server.getUrl("/"));
   1295     assertContent("{}", connection);
   1296     assertEquals(0, client.client().getConnectionPool().getIdleConnectionCount());
   1297   }
   1298 
   1299   @Test public void earlyDisconnectDoesntHarmPoolingWithChunkedEncoding() throws Exception {
   1300     testEarlyDisconnectDoesntHarmPooling(TransferKind.CHUNKED);
   1301   }
   1302 
   1303   @Test public void earlyDisconnectDoesntHarmPoolingWithFixedLengthEncoding() throws Exception {
   1304     testEarlyDisconnectDoesntHarmPooling(TransferKind.FIXED_LENGTH);
   1305   }
   1306 
   1307   private void testEarlyDisconnectDoesntHarmPooling(TransferKind transferKind) throws Exception {
   1308     MockResponse response1 = new MockResponse();
   1309     transferKind.setBody(response1, "ABCDEFGHIJK", 1024);
   1310     server.enqueue(response1);
   1311 
   1312     MockResponse response2 = new MockResponse();
   1313     transferKind.setBody(response2, "LMNOPQRSTUV", 1024);
   1314     server.enqueue(response2);
   1315 
   1316     HttpURLConnection connection1 = client.open(server.getUrl("/"));
   1317     InputStream in1 = connection1.getInputStream();
   1318     assertEquals("ABCDE", readAscii(in1, 5));
   1319     in1.close();
   1320     connection1.disconnect();
   1321 
   1322     HttpURLConnection connection2 = client.open(server.getUrl("/"));
   1323     InputStream in2 = connection2.getInputStream();
   1324     assertEquals("LMNOP", readAscii(in2, 5));
   1325     in2.close();
   1326     connection2.disconnect();
   1327 
   1328     assertEquals(0, server.takeRequest().getSequenceNumber());
   1329     assertEquals(1, server.takeRequest().getSequenceNumber()); // Connection is pooled!
   1330   }
   1331 
   1332   @Test public void streamDiscardingIsTimely() throws Exception {
   1333     // This response takes at least a full second to serve: 10,000 bytes served 100 bytes at a time.
   1334     server.enqueue(new MockResponse()
   1335         .setBody(new Buffer().write(new byte[10000]))
   1336         .throttleBody(100, 10, MILLISECONDS));
   1337     server.enqueue(new MockResponse().setBody("A"));
   1338 
   1339     long startNanos = System.nanoTime();
   1340     URLConnection connection1 = client.open(server.getUrl("/"));
   1341     InputStream in = connection1.getInputStream();
   1342     in.close();
   1343     long elapsedNanos = System.nanoTime() - startNanos;
   1344     long elapsedMillis = NANOSECONDS.toMillis(elapsedNanos);
   1345 
   1346     // If we're working correctly, this should be greater than 100ms, but less than double that.
   1347     // Previously we had a bug where we would download the entire response body as long as no
   1348     // individual read took longer than 100ms.
   1349     assertTrue(String.format("Time to close: %sms", elapsedMillis), elapsedMillis < 500);
   1350 
   1351     // Do another request to confirm that the discarded connection was not pooled.
   1352     assertContent("A", client.open(server.getUrl("/")));
   1353 
   1354     assertEquals(0, server.takeRequest().getSequenceNumber());
   1355     assertEquals(0, server.takeRequest().getSequenceNumber()); // Connection is not pooled.
   1356   }
   1357 
   1358   @Test public void setChunkedStreamingMode() throws IOException, InterruptedException {
   1359     server.enqueue(new MockResponse());
   1360 
   1361     String body = "ABCDEFGHIJKLMNOPQ";
   1362     connection = client.open(server.getUrl("/"));
   1363     connection.setChunkedStreamingMode(0); // OkHttp does not honor specific chunk sizes.
   1364     connection.setDoOutput(true);
   1365     OutputStream outputStream = connection.getOutputStream();
   1366     outputStream.write(body.getBytes("US-ASCII"));
   1367     assertEquals(200, connection.getResponseCode());
   1368     connection.getInputStream().close();
   1369 
   1370     RecordedRequest request = server.takeRequest();
   1371     assertEquals(body, request.getBody().readUtf8());
   1372     assertEquals(Arrays.asList(body.length()), request.getChunkSizes());
   1373   }
   1374 
   1375   @Test public void authenticateWithFixedLengthStreaming() throws Exception {
   1376     testAuthenticateWithStreamingPost(StreamingMode.FIXED_LENGTH);
   1377   }
   1378 
   1379   @Test public void authenticateWithChunkedStreaming() throws Exception {
   1380     testAuthenticateWithStreamingPost(StreamingMode.CHUNKED);
   1381   }
   1382 
   1383   private void testAuthenticateWithStreamingPost(StreamingMode streamingMode) throws Exception {
   1384     MockResponse pleaseAuthenticate = new MockResponse().setResponseCode(401)
   1385         .addHeader("WWW-Authenticate: Basic realm=\"protected area\"")
   1386         .setBody("Please authenticate.");
   1387     server.enqueue(pleaseAuthenticate);
   1388 
   1389     Authenticator.setDefault(new RecordingAuthenticator());
   1390     connection = client.open(server.getUrl("/"));
   1391     connection.setDoOutput(true);
   1392     byte[] requestBody = { 'A', 'B', 'C', 'D' };
   1393     if (streamingMode == StreamingMode.FIXED_LENGTH) {
   1394       connection.setFixedLengthStreamingMode(requestBody.length);
   1395     } else if (streamingMode == StreamingMode.CHUNKED) {
   1396       connection.setChunkedStreamingMode(0);
   1397     }
   1398     OutputStream outputStream = connection.getOutputStream();
   1399     outputStream.write(requestBody);
   1400     outputStream.close();
   1401     try {
   1402       connection.getInputStream();
   1403       fail();
   1404     } catch (HttpRetryException expected) {
   1405     }
   1406 
   1407     // no authorization header for the request...
   1408     RecordedRequest request = server.takeRequest();
   1409     assertNull(request.getHeader("Authorization"));
   1410     assertEquals("ABCD", request.getBody().readUtf8());
   1411   }
   1412 
   1413   @Test public void postBodyRetransmittedAfterAuthorizationFail() throws Exception {
   1414     postBodyRetransmittedAfterAuthorizationFail("abc");
   1415   }
   1416 
   1417   @Test public void postBodyRetransmittedAfterAuthorizationFail_SPDY_3() throws Exception {
   1418     enableProtocol(Protocol.SPDY_3);
   1419     postBodyRetransmittedAfterAuthorizationFail("abc");
   1420   }
   1421 
   1422   @Test public void postBodyRetransmittedAfterAuthorizationFail_HTTP_2() throws Exception {
   1423     enableProtocol(Protocol.HTTP_2);
   1424     postBodyRetransmittedAfterAuthorizationFail("abc");
   1425   }
   1426 
   1427   /** Don't explode when resending an empty post. https://github.com/square/okhttp/issues/1131 */
   1428   @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail() throws Exception {
   1429     postBodyRetransmittedAfterAuthorizationFail("");
   1430   }
   1431 
   1432   @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail_SPDY_3() throws Exception {
   1433     enableProtocol(Protocol.SPDY_3);
   1434     postBodyRetransmittedAfterAuthorizationFail("");
   1435   }
   1436 
   1437   @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail_HTTP_2() throws Exception {
   1438     enableProtocol(Protocol.HTTP_2);
   1439     postBodyRetransmittedAfterAuthorizationFail("");
   1440   }
   1441 
   1442   private void postBodyRetransmittedAfterAuthorizationFail(String body) throws Exception {
   1443     server.enqueue(new MockResponse().setResponseCode(401));
   1444     server.enqueue(new MockResponse());
   1445 
   1446     String credential = Credentials.basic("jesse", "secret");
   1447     client.client().setAuthenticator(new RecordingOkAuthenticator(credential));
   1448 
   1449     connection = client.open(server.getUrl("/"));
   1450     connection.setDoOutput(true);
   1451     OutputStream outputStream = connection.getOutputStream();
   1452     outputStream.write(body.getBytes("UTF-8"));
   1453     outputStream.close();
   1454     assertEquals(200, connection.getResponseCode());
   1455 
   1456     RecordedRequest recordedRequest1 = server.takeRequest();
   1457     assertEquals("POST", recordedRequest1.getMethod());
   1458     assertEquals(body, recordedRequest1.getBody().readUtf8());
   1459     assertNull(recordedRequest1.getHeader("Authorization"));
   1460 
   1461     RecordedRequest recordedRequest2 = server.takeRequest();
   1462     assertEquals("POST", recordedRequest2.getMethod());
   1463     assertEquals(body, recordedRequest2.getBody().readUtf8());
   1464     assertEquals(credential, recordedRequest2.getHeader("Authorization"));
   1465   }
   1466 
   1467   @Test public void nonStandardAuthenticationScheme() throws Exception {
   1468     List<String> calls = authCallsForHeader("WWW-Authenticate: Foo");
   1469     assertEquals(Collections.<String>emptyList(), calls);
   1470   }
   1471 
   1472   @Test public void nonStandardAuthenticationSchemeWithRealm() throws Exception {
   1473     List<String> calls = authCallsForHeader("WWW-Authenticate: Foo realm=\"Bar\"");
   1474     assertEquals(0, calls.size());
   1475   }
   1476 
   1477   // Digest auth is currently unsupported. Test that digest requests should fail reasonably.
   1478   // http://code.google.com/p/android/issues/detail?id=11140
   1479   @Test public void digestAuthentication() throws Exception {
   1480     List<String> calls = authCallsForHeader("WWW-Authenticate: Digest "
   1481         + "realm=\"testrealm (at) host.com\", qop=\"auth,auth-int\", "
   1482         + "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", "
   1483         + "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"");
   1484     assertEquals(0, calls.size());
   1485   }
   1486 
   1487   @Test public void allAttributesSetInServerAuthenticationCallbacks() throws Exception {
   1488     List<String> calls = authCallsForHeader("WWW-Authenticate: Basic realm=\"Bar\"");
   1489     assertEquals(1, calls.size());
   1490     URL url = server.getUrl("/");
   1491     String call = calls.get(0);
   1492     assertTrue(call, call.contains("host=" + url.getHost()));
   1493     assertTrue(call, call.contains("port=" + url.getPort()));
   1494     assertTrue(call, call.contains("site=" + url.getHost()));
   1495     assertTrue(call, call.contains("url=" + url));
   1496     assertTrue(call, call.contains("type=" + Authenticator.RequestorType.SERVER));
   1497     assertTrue(call, call.contains("prompt=Bar"));
   1498     assertTrue(call, call.contains("protocol=http"));
   1499     assertTrue(call, call.toLowerCase().contains("scheme=basic")); // lowercase for the RI.
   1500   }
   1501 
   1502   @Test public void allAttributesSetInProxyAuthenticationCallbacks() throws Exception {
   1503     List<String> calls = authCallsForHeader("Proxy-Authenticate: Basic realm=\"Bar\"");
   1504     assertEquals(1, calls.size());
   1505     URL url = server.getUrl("/");
   1506     String call = calls.get(0);
   1507     assertTrue(call, call.contains("host=" + url.getHost()));
   1508     assertTrue(call, call.contains("port=" + url.getPort()));
   1509     assertTrue(call, call.contains("site=" + url.getHost()));
   1510     assertTrue(call, call.contains("url=http://android.com"));
   1511     assertTrue(call, call.contains("type=" + Authenticator.RequestorType.PROXY));
   1512     assertTrue(call, call.contains("prompt=Bar"));
   1513     assertTrue(call, call.contains("protocol=http"));
   1514     assertTrue(call, call.toLowerCase().contains("scheme=basic")); // lowercase for the RI.
   1515   }
   1516 
   1517   private List<String> authCallsForHeader(String authHeader) throws IOException {
   1518     boolean proxy = authHeader.startsWith("Proxy-");
   1519     int responseCode = proxy ? 407 : 401;
   1520     RecordingAuthenticator authenticator = new RecordingAuthenticator(null);
   1521     Authenticator.setDefault(authenticator);
   1522     MockResponse pleaseAuthenticate = new MockResponse()
   1523         .setResponseCode(responseCode)
   1524         .addHeader(authHeader)
   1525         .setBody("Please authenticate.");
   1526     server.enqueue(pleaseAuthenticate);
   1527 
   1528     if (proxy) {
   1529       client.client().setProxy(server.toProxyAddress());
   1530       connection = client.open(new URL("http://android.com"));
   1531     } else {
   1532       connection = client.open(server.getUrl("/"));
   1533     }
   1534     assertEquals(responseCode, connection.getResponseCode());
   1535     connection.getErrorStream().close();
   1536     return authenticator.calls;
   1537   }
   1538 
   1539   @Test public void setValidRequestMethod() throws Exception {
   1540     assertValidRequestMethod("GET");
   1541     assertValidRequestMethod("DELETE");
   1542     assertValidRequestMethod("HEAD");
   1543     assertValidRequestMethod("OPTIONS");
   1544     assertValidRequestMethod("POST");
   1545     assertValidRequestMethod("PUT");
   1546     assertValidRequestMethod("TRACE");
   1547     assertValidRequestMethod("PATCH");
   1548   }
   1549 
   1550   private void assertValidRequestMethod(String requestMethod) throws Exception {
   1551     connection = client.open(server.getUrl("/"));
   1552     connection.setRequestMethod(requestMethod);
   1553     assertEquals(requestMethod, connection.getRequestMethod());
   1554   }
   1555 
   1556   @Test public void setInvalidRequestMethodLowercase() throws Exception {
   1557     assertInvalidRequestMethod("get");
   1558   }
   1559 
   1560   @Test public void setInvalidRequestMethodConnect() throws Exception {
   1561     assertInvalidRequestMethod("CONNECT");
   1562   }
   1563 
   1564   private void assertInvalidRequestMethod(String requestMethod) throws Exception {
   1565     connection = client.open(server.getUrl("/"));
   1566     try {
   1567       connection.setRequestMethod(requestMethod);
   1568       fail();
   1569     } catch (ProtocolException expected) {
   1570     }
   1571   }
   1572 
   1573   @Test public void shoutcast() throws Exception {
   1574     server.enqueue(new MockResponse().setStatus("ICY 200 OK")
   1575         // .addHeader("HTTP/1.0 200 OK")
   1576         .addHeader("Accept-Ranges: none")
   1577         .addHeader("Content-Type: audio/mpeg")
   1578         .addHeader("icy-br:128")
   1579         .addHeader("ice-audio-info: bitrate=128;samplerate=44100;channels=2")
   1580         .addHeader("icy-br:128")
   1581         .addHeader("icy-description:Rock")
   1582         .addHeader("icy-genre:riders")
   1583         .addHeader("icy-name:A2RRock")
   1584         .addHeader("icy-pub:1")
   1585         .addHeader("icy-url:http://www.A2Rradio.com")
   1586         .addHeader("Server: Icecast 2.3.3-kh8")
   1587         .addHeader("Cache-Control: no-cache")
   1588         .addHeader("Pragma: no-cache")
   1589         .addHeader("Expires: Mon, 26 Jul 1997 05:00:00 GMT")
   1590         .addHeader("icy-metaint:16000")
   1591         .setBody("mp3 data"));
   1592     connection = client.open(server.getUrl("/"));
   1593     assertEquals(200, connection.getResponseCode());
   1594     assertEquals("OK", connection.getResponseMessage());
   1595     assertContent("mp3 data", connection);
   1596   }
   1597 
   1598   @Test public void cannotSetNegativeFixedLengthStreamingMode() throws Exception {
   1599     connection = client.open(server.getUrl("/"));
   1600     try {
   1601       connection.setFixedLengthStreamingMode(-2);
   1602       fail();
   1603     } catch (IllegalArgumentException expected) {
   1604     }
   1605   }
   1606 
   1607   @Test public void canSetNegativeChunkedStreamingMode() throws Exception {
   1608     connection = client.open(server.getUrl("/"));
   1609     connection.setChunkedStreamingMode(-2);
   1610   }
   1611 
   1612   @Test public void cannotSetFixedLengthStreamingModeAfterConnect() throws Exception {
   1613     server.enqueue(new MockResponse().setBody("A"));
   1614     connection = client.open(server.getUrl("/"));
   1615     assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   1616     try {
   1617       connection.setFixedLengthStreamingMode(1);
   1618       fail();
   1619     } catch (IllegalStateException expected) {
   1620     }
   1621   }
   1622 
   1623   @Test public void cannotSetChunkedStreamingModeAfterConnect() throws Exception {
   1624     server.enqueue(new MockResponse().setBody("A"));
   1625     connection = client.open(server.getUrl("/"));
   1626     assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   1627     try {
   1628       connection.setChunkedStreamingMode(1);
   1629       fail();
   1630     } catch (IllegalStateException expected) {
   1631     }
   1632   }
   1633 
   1634   @Test public void cannotSetFixedLengthStreamingModeAfterChunkedStreamingMode() throws Exception {
   1635     connection = client.open(server.getUrl("/"));
   1636     connection.setChunkedStreamingMode(1);
   1637     try {
   1638       connection.setFixedLengthStreamingMode(1);
   1639       fail();
   1640     } catch (IllegalStateException expected) {
   1641     }
   1642   }
   1643 
   1644   @Test public void cannotSetChunkedStreamingModeAfterFixedLengthStreamingMode() throws Exception {
   1645     connection = client.open(server.getUrl("/"));
   1646     connection.setFixedLengthStreamingMode(1);
   1647     try {
   1648       connection.setChunkedStreamingMode(1);
   1649       fail();
   1650     } catch (IllegalStateException expected) {
   1651     }
   1652   }
   1653 
   1654   @Test public void secureFixedLengthStreaming() throws Exception {
   1655     testSecureStreamingPost(StreamingMode.FIXED_LENGTH);
   1656   }
   1657 
   1658   @Test public void secureChunkedStreaming() throws Exception {
   1659     testSecureStreamingPost(StreamingMode.CHUNKED);
   1660   }
   1661 
   1662   /**
   1663    * Users have reported problems using HTTPS with streaming request bodies.
   1664    * http://code.google.com/p/android/issues/detail?id=12860
   1665    */
   1666   private void testSecureStreamingPost(StreamingMode streamingMode) throws Exception {
   1667     server.useHttps(sslContext.getSocketFactory(), false);
   1668     server.enqueue(new MockResponse().setBody("Success!"));
   1669 
   1670     client.client().setSslSocketFactory(sslContext.getSocketFactory());
   1671     client.client().setHostnameVerifier(new RecordingHostnameVerifier());
   1672     connection = client.open(server.getUrl("/"));
   1673     connection.setDoOutput(true);
   1674     byte[] requestBody = { 'A', 'B', 'C', 'D' };
   1675     if (streamingMode == StreamingMode.FIXED_LENGTH) {
   1676       connection.setFixedLengthStreamingMode(requestBody.length);
   1677     } else if (streamingMode == StreamingMode.CHUNKED) {
   1678       connection.setChunkedStreamingMode(0);
   1679     }
   1680     OutputStream outputStream = connection.getOutputStream();
   1681     outputStream.write(requestBody);
   1682     outputStream.close();
   1683     assertEquals("Success!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   1684 
   1685     RecordedRequest request = server.takeRequest();
   1686     assertEquals("POST / HTTP/1.1", request.getRequestLine());
   1687     if (streamingMode == StreamingMode.FIXED_LENGTH) {
   1688       assertEquals(Collections.<Integer>emptyList(), request.getChunkSizes());
   1689     } else if (streamingMode == StreamingMode.CHUNKED) {
   1690       assertEquals(Arrays.asList(4), request.getChunkSizes());
   1691     }
   1692     assertEquals("ABCD", request.getBody().readUtf8());
   1693   }
   1694 
   1695   enum StreamingMode {
   1696     FIXED_LENGTH, CHUNKED
   1697   }
   1698 
   1699   @Test public void authenticateWithPost() throws Exception {
   1700     MockResponse pleaseAuthenticate = new MockResponse().setResponseCode(401)
   1701         .addHeader("WWW-Authenticate: Basic realm=\"protected area\"")
   1702         .setBody("Please authenticate.");
   1703     // fail auth three times...
   1704     server.enqueue(pleaseAuthenticate);
   1705     server.enqueue(pleaseAuthenticate);
   1706     server.enqueue(pleaseAuthenticate);
   1707     // ...then succeed the fourth time
   1708     server.enqueue(new MockResponse().setBody("Successful auth!"));
   1709 
   1710     Authenticator.setDefault(new RecordingAuthenticator());
   1711     connection = client.open(server.getUrl("/"));
   1712     connection.setDoOutput(true);
   1713     byte[] requestBody = { 'A', 'B', 'C', 'D' };
   1714     OutputStream outputStream = connection.getOutputStream();
   1715     outputStream.write(requestBody);
   1716     outputStream.close();
   1717     assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   1718 
   1719     // no authorization header for the first request...
   1720     RecordedRequest request = server.takeRequest();
   1721     assertNull(request.getHeader("Authorization"));
   1722 
   1723     // ...but the three requests that follow include an authorization header
   1724     for (int i = 0; i < 3; i++) {
   1725       request = server.takeRequest();
   1726       assertEquals("POST / HTTP/1.1", request.getRequestLine());
   1727       assertEquals("Basic " + RecordingAuthenticator.BASE_64_CREDENTIALS,
   1728           request.getHeader("Authorization"));
   1729       assertEquals("ABCD", request.getBody().readUtf8());
   1730     }
   1731   }
   1732 
   1733   @Test public void authenticateWithGet() throws Exception {
   1734     MockResponse pleaseAuthenticate = new MockResponse().setResponseCode(401)
   1735         .addHeader("WWW-Authenticate: Basic realm=\"protected area\"")
   1736         .setBody("Please authenticate.");
   1737     // fail auth three times...
   1738     server.enqueue(pleaseAuthenticate);
   1739     server.enqueue(pleaseAuthenticate);
   1740     server.enqueue(pleaseAuthenticate);
   1741     // ...then succeed the fourth time
   1742     server.enqueue(new MockResponse().setBody("Successful auth!"));
   1743 
   1744     Authenticator.setDefault(new RecordingAuthenticator());
   1745     connection = client.open(server.getUrl("/"));
   1746     assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   1747 
   1748     // no authorization header for the first request...
   1749     RecordedRequest request = server.takeRequest();
   1750     assertNull(request.getHeader("Authorization"));
   1751 
   1752     // ...but the three requests that follow requests include an authorization header
   1753     for (int i = 0; i < 3; i++) {
   1754       request = server.takeRequest();
   1755       assertEquals("GET / HTTP/1.1", request.getRequestLine());
   1756       assertEquals("Basic " + RecordingAuthenticator.BASE_64_CREDENTIALS,
   1757           request.getHeader("Authorization"));
   1758     }
   1759   }
   1760 
   1761   /** https://code.google.com/p/android/issues/detail?id=74026 */
   1762   @Test public void authenticateWithGetAndTransparentGzip() throws Exception {
   1763     MockResponse pleaseAuthenticate = new MockResponse().setResponseCode(401)
   1764         .addHeader("WWW-Authenticate: Basic realm=\"protected area\"")
   1765         .setBody("Please authenticate.");
   1766     // fail auth three times...
   1767     server.enqueue(pleaseAuthenticate);
   1768     server.enqueue(pleaseAuthenticate);
   1769     server.enqueue(pleaseAuthenticate);
   1770     // ...then succeed the fourth time
   1771     MockResponse successfulResponse = new MockResponse()
   1772         .addHeader("Content-Encoding", "gzip")
   1773         .setBody(gzip("Successful auth!"));
   1774     server.enqueue(successfulResponse);
   1775 
   1776     Authenticator.setDefault(new RecordingAuthenticator());
   1777     connection = client.open(server.getUrl("/"));
   1778     assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   1779 
   1780     // no authorization header for the first request...
   1781     RecordedRequest request = server.takeRequest();
   1782     assertNull(request.getHeader("Authorization"));
   1783 
   1784     // ...but the three requests that follow requests include an authorization header
   1785     for (int i = 0; i < 3; i++) {
   1786       request = server.takeRequest();
   1787       assertEquals("GET / HTTP/1.1", request.getRequestLine());
   1788       assertEquals("Basic " + RecordingAuthenticator.BASE_64_CREDENTIALS,
   1789           request.getHeader("Authorization"));
   1790     }
   1791   }
   1792 
   1793   /** https://github.com/square/okhttp/issues/342 */
   1794   @Test public void authenticateRealmUppercase() throws Exception {
   1795     server.enqueue(new MockResponse().setResponseCode(401)
   1796         .addHeader("wWw-aUtHeNtIcAtE: bAsIc rEaLm=\"pRoTeCtEd aReA\"")
   1797         .setBody("Please authenticate."));
   1798     server.enqueue(new MockResponse().setBody("Successful auth!"));
   1799 
   1800     Authenticator.setDefault(new RecordingAuthenticator());
   1801     connection = client.open(server.getUrl("/"));
   1802     assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   1803   }
   1804 
   1805   @Test public void redirectedWithChunkedEncoding() throws Exception {
   1806     testRedirected(TransferKind.CHUNKED, true);
   1807   }
   1808 
   1809   @Test public void redirectedWithContentLengthHeader() throws Exception {
   1810     testRedirected(TransferKind.FIXED_LENGTH, true);
   1811   }
   1812 
   1813   @Test public void redirectedWithNoLengthHeaders() throws Exception {
   1814     testRedirected(TransferKind.END_OF_STREAM, false);
   1815   }
   1816 
   1817   private void testRedirected(TransferKind transferKind, boolean reuse) throws Exception {
   1818     MockResponse response = new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
   1819         .addHeader("Location: /foo");
   1820     transferKind.setBody(response, "This page has moved!", 10);
   1821     server.enqueue(response);
   1822     server.enqueue(new MockResponse().setBody("This is the new location!"));
   1823 
   1824     URLConnection connection = client.open(server.getUrl("/"));
   1825     assertEquals("This is the new location!",
   1826         readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   1827 
   1828     RecordedRequest first = server.takeRequest();
   1829     assertEquals("GET / HTTP/1.1", first.getRequestLine());
   1830     RecordedRequest retry = server.takeRequest();
   1831     assertEquals("GET /foo HTTP/1.1", retry.getRequestLine());
   1832     if (reuse) {
   1833       assertEquals("Expected connection reuse", 1, retry.getSequenceNumber());
   1834     }
   1835   }
   1836 
   1837   @Test public void redirectedOnHttps() throws IOException, InterruptedException {
   1838     server.useHttps(sslContext.getSocketFactory(), false);
   1839     server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
   1840         .addHeader("Location: /foo")
   1841         .setBody("This page has moved!"));
   1842     server.enqueue(new MockResponse().setBody("This is the new location!"));
   1843 
   1844     client.client().setSslSocketFactory(sslContext.getSocketFactory());
   1845     client.client().setHostnameVerifier(new RecordingHostnameVerifier());
   1846     connection = client.open(server.getUrl("/"));
   1847     assertEquals("This is the new location!",
   1848         readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   1849 
   1850     RecordedRequest first = server.takeRequest();
   1851     assertEquals("GET / HTTP/1.1", first.getRequestLine());
   1852     RecordedRequest retry = server.takeRequest();
   1853     assertEquals("GET /foo HTTP/1.1", retry.getRequestLine());
   1854     assertEquals("Expected connection reuse", 1, retry.getSequenceNumber());
   1855   }
   1856 
   1857   @Test public void notRedirectedFromHttpsToHttp() throws IOException, InterruptedException {
   1858     server.useHttps(sslContext.getSocketFactory(), false);
   1859     server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
   1860         .addHeader("Location: http://anyhost/foo")
   1861         .setBody("This page has moved!"));
   1862 
   1863     client.client().setFollowSslRedirects(false);
   1864     client.client().setSslSocketFactory(sslContext.getSocketFactory());
   1865     client.client().setHostnameVerifier(new RecordingHostnameVerifier());
   1866     connection = client.open(server.getUrl("/"));
   1867     assertEquals("This page has moved!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   1868   }
   1869 
   1870   @Test public void notRedirectedFromHttpToHttps() throws IOException, InterruptedException {
   1871     server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
   1872         .addHeader("Location: https://anyhost/foo")
   1873         .setBody("This page has moved!"));
   1874 
   1875     client.client().setFollowSslRedirects(false);
   1876     connection = client.open(server.getUrl("/"));
   1877     assertEquals("This page has moved!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   1878   }
   1879 
   1880   @Test public void redirectedFromHttpsToHttpFollowingProtocolRedirects() throws Exception {
   1881     server2.enqueue(new MockResponse().setBody("This is insecure HTTP!"));
   1882 
   1883     server.useHttps(sslContext.getSocketFactory(), false);
   1884     server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
   1885         .addHeader("Location: " + server2.getUrl("/"))
   1886         .setBody("This page has moved!"));
   1887 
   1888     client.client().setSslSocketFactory(sslContext.getSocketFactory());
   1889     client.client().setHostnameVerifier(new RecordingHostnameVerifier());
   1890     client.client().setFollowSslRedirects(true);
   1891     HttpsURLConnection connection = (HttpsURLConnection) client.open(server.getUrl("/"));
   1892     assertContent("This is insecure HTTP!", connection);
   1893     assertNull(connection.getCipherSuite());
   1894     assertNull(connection.getLocalCertificates());
   1895     assertNull(connection.getServerCertificates());
   1896     assertNull(connection.getPeerPrincipal());
   1897     assertNull(connection.getLocalPrincipal());
   1898   }
   1899 
   1900   @Test public void redirectedFromHttpToHttpsFollowingProtocolRedirects() throws Exception {
   1901     server2.useHttps(sslContext.getSocketFactory(), false);
   1902     server2.enqueue(new MockResponse().setBody("This is secure HTTPS!"));
   1903 
   1904     server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
   1905         .addHeader("Location: " + server2.getUrl("/"))
   1906         .setBody("This page has moved!"));
   1907 
   1908     client.client().setSslSocketFactory(sslContext.getSocketFactory());
   1909     client.client().setHostnameVerifier(new RecordingHostnameVerifier());
   1910     client.client().setFollowSslRedirects(true);
   1911     connection = client.open(server.getUrl("/"));
   1912     assertContent("This is secure HTTPS!", connection);
   1913     assertFalse(connection instanceof HttpsURLConnection);
   1914   }
   1915 
   1916   @Test public void redirectToAnotherOriginServer() throws Exception {
   1917     redirectToAnotherOriginServer(false);
   1918   }
   1919 
   1920   @Test public void redirectToAnotherOriginServerWithHttps() throws Exception {
   1921     redirectToAnotherOriginServer(true);
   1922   }
   1923 
   1924   private void redirectToAnotherOriginServer(boolean https) throws Exception {
   1925     if (https) {
   1926       server.useHttps(sslContext.getSocketFactory(), false);
   1927       server2.useHttps(sslContext.getSocketFactory(), false);
   1928       server2.setProtocolNegotiationEnabled(false);
   1929       client.client().setSslSocketFactory(sslContext.getSocketFactory());
   1930       client.client().setHostnameVerifier(new RecordingHostnameVerifier());
   1931     }
   1932 
   1933     server2.enqueue(new MockResponse().setBody("This is the 2nd server!"));
   1934     server2.enqueue(new MockResponse().setBody("This is the 2nd server, again!"));
   1935 
   1936     server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
   1937         .addHeader("Location: " + server2.getUrl("/").toString())
   1938         .setBody("This page has moved!"));
   1939     server.enqueue(new MockResponse().setBody("This is the first server again!"));
   1940 
   1941     connection = client.open(server.getUrl("/"));
   1942     assertContent("This is the 2nd server!", connection);
   1943     assertEquals(server2.getUrl("/"), connection.getURL());
   1944 
   1945     // make sure the first server was careful to recycle the connection
   1946     assertContent("This is the first server again!", client.open(server.getUrl("/")));
   1947     assertContent("This is the 2nd server, again!", client.open(server2.getUrl("/")));
   1948 
   1949     String server1Host = server.getHostName() + ":" + server.getPort();
   1950     String server2Host = server2.getHostName() + ":" + server2.getPort();
   1951     assertEquals(server1Host, server.takeRequest().getHeader("Host"));
   1952     assertEquals(server2Host, server2.takeRequest().getHeader("Host"));
   1953     assertEquals("Expected connection reuse", 1, server.takeRequest().getSequenceNumber());
   1954     assertEquals("Expected connection reuse", 1, server2.takeRequest().getSequenceNumber());
   1955   }
   1956 
   1957   @Test public void redirectWithProxySelector() throws Exception {
   1958     final List<URI> proxySelectionRequests = new ArrayList<URI>();
   1959     client.client().setProxySelector(new ProxySelector() {
   1960       @Override public List<Proxy> select(URI uri) {
   1961         proxySelectionRequests.add(uri);
   1962         MockWebServer proxyServer = (uri.getPort() == server.getPort())
   1963             ? server
   1964             : server2;
   1965         return Arrays.asList(proxyServer.toProxyAddress());
   1966       }
   1967 
   1968       @Override public void connectFailed(URI uri, SocketAddress address, IOException failure) {
   1969         throw new AssertionError();
   1970       }
   1971     });
   1972 
   1973     server2.enqueue(new MockResponse().setBody("This is the 2nd server!"));
   1974 
   1975     server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
   1976         .addHeader("Location: " + server2.getUrl("/b").toString())
   1977         .setBody("This page has moved!"));
   1978 
   1979     assertContent("This is the 2nd server!", client.open(server.getUrl("/a")));
   1980 
   1981     assertEquals(Arrays.asList(server.getUrl("/").toURI(), server2.getUrl("/").toURI()),
   1982         proxySelectionRequests);
   1983   }
   1984 
   1985   @Test public void redirectWithAuthentication() throws Exception {
   1986     server2.enqueue(new MockResponse().setBody("Page 2"));
   1987 
   1988     server.enqueue(new MockResponse().setResponseCode(401));
   1989     server.enqueue(new MockResponse().setResponseCode(302)
   1990         .addHeader("Location: " + server2.getUrl("/b")));
   1991 
   1992     client.client().setAuthenticator(
   1993         new RecordingOkAuthenticator(Credentials.basic("jesse", "secret")));
   1994     assertContent("Page 2", client.open(server.getUrl("/a")));
   1995 
   1996     RecordedRequest redirectRequest = server2.takeRequest();
   1997     assertNull(redirectRequest.getHeader("Authorization"));
   1998     assertEquals("/b", redirectRequest.getPath());
   1999   }
   2000 
   2001   @Test public void response300MultipleChoiceWithPost() throws Exception {
   2002     // Chrome doesn't follow the redirect, but Firefox and the RI both do
   2003     testResponseRedirectedWithPost(HttpURLConnection.HTTP_MULT_CHOICE, TransferKind.END_OF_STREAM);
   2004   }
   2005 
   2006   @Test public void response301MovedPermanentlyWithPost() throws Exception {
   2007     testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_PERM, TransferKind.END_OF_STREAM);
   2008   }
   2009 
   2010   @Test public void response302MovedTemporarilyWithPost() throws Exception {
   2011     testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_TEMP, TransferKind.END_OF_STREAM);
   2012   }
   2013 
   2014   @Test public void response303SeeOtherWithPost() throws Exception {
   2015     testResponseRedirectedWithPost(HttpURLConnection.HTTP_SEE_OTHER, TransferKind.END_OF_STREAM);
   2016   }
   2017 
   2018   @Test public void postRedirectToGetWithChunkedRequest() throws Exception {
   2019     testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_TEMP, TransferKind.CHUNKED);
   2020   }
   2021 
   2022   @Test public void postRedirectToGetWithStreamedRequest() throws Exception {
   2023     testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_TEMP, TransferKind.FIXED_LENGTH);
   2024   }
   2025 
   2026   private void testResponseRedirectedWithPost(int redirectCode, TransferKind transferKind)
   2027       throws Exception {
   2028     server.enqueue(new MockResponse().setResponseCode(redirectCode)
   2029         .addHeader("Location: /page2")
   2030         .setBody("This page has moved!"));
   2031     server.enqueue(new MockResponse().setBody("Page 2"));
   2032 
   2033     connection = client.open(server.getUrl("/page1"));
   2034     connection.setDoOutput(true);
   2035     transferKind.setForRequest(connection, 4);
   2036     byte[] requestBody = { 'A', 'B', 'C', 'D' };
   2037     OutputStream outputStream = connection.getOutputStream();
   2038     outputStream.write(requestBody);
   2039     outputStream.close();
   2040     assertEquals("Page 2", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   2041     assertTrue(connection.getDoOutput());
   2042 
   2043     RecordedRequest page1 = server.takeRequest();
   2044     assertEquals("POST /page1 HTTP/1.1", page1.getRequestLine());
   2045     assertEquals("ABCD", page1.getBody().readUtf8());
   2046 
   2047     RecordedRequest page2 = server.takeRequest();
   2048     assertEquals("GET /page2 HTTP/1.1", page2.getRequestLine());
   2049   }
   2050 
   2051   @Test public void redirectedPostStripsRequestBodyHeaders() throws Exception {
   2052     server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
   2053         .addHeader("Location: /page2"));
   2054     server.enqueue(new MockResponse().setBody("Page 2"));
   2055 
   2056     connection = client.open(server.getUrl("/page1"));
   2057     connection.setDoOutput(true);
   2058     connection.addRequestProperty("Content-Length", "4");
   2059     connection.addRequestProperty("Content-Type", "text/plain; charset=utf-8");
   2060     connection.addRequestProperty("Transfer-Encoding", "identity");
   2061     OutputStream outputStream = connection.getOutputStream();
   2062     outputStream.write("ABCD".getBytes("UTF-8"));
   2063     outputStream.close();
   2064     assertEquals("Page 2", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   2065 
   2066     assertEquals("POST /page1 HTTP/1.1", server.takeRequest().getRequestLine());
   2067 
   2068     RecordedRequest page2 = server.takeRequest();
   2069     assertEquals("GET /page2 HTTP/1.1", page2.getRequestLine());
   2070     assertNull(page2.getHeader("Content-Length"));
   2071     assertNull(page2.getHeader("Content-Type"));
   2072     assertNull(page2.getHeader("Transfer-Encoding"));
   2073   }
   2074 
   2075   @Test public void response305UseProxy() throws Exception {
   2076     server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_USE_PROXY)
   2077         .addHeader("Location: " + server.getUrl("/"))
   2078         .setBody("This page has moved!"));
   2079     server.enqueue(new MockResponse().setBody("Proxy Response"));
   2080 
   2081     connection = client.open(server.getUrl("/foo"));
   2082     // Fails on the RI, which gets "Proxy Response"
   2083     assertEquals("This page has moved!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   2084 
   2085     RecordedRequest page1 = server.takeRequest();
   2086     assertEquals("GET /foo HTTP/1.1", page1.getRequestLine());
   2087     assertEquals(1, server.getRequestCount());
   2088   }
   2089 
   2090   @Test public void response307WithGet() throws Exception {
   2091     testRedirect(true, "GET");
   2092   }
   2093 
   2094   @Test public void response307WithHead() throws Exception {
   2095     testRedirect(true, "HEAD");
   2096   }
   2097 
   2098   @Test public void response307WithOptions() throws Exception {
   2099     testRedirect(true, "OPTIONS");
   2100   }
   2101 
   2102   @Test public void response307WithPost() throws Exception {
   2103     testRedirect(true, "POST");
   2104   }
   2105 
   2106   @Test public void response308WithGet() throws Exception {
   2107     testRedirect(false, "GET");
   2108   }
   2109 
   2110   @Test public void response308WithHead() throws Exception {
   2111     testRedirect(false, "HEAD");
   2112   }
   2113 
   2114   @Test public void response308WithOptions() throws Exception {
   2115     testRedirect(false, "OPTIONS");
   2116   }
   2117 
   2118   @Test public void response308WithPost() throws Exception {
   2119     testRedirect(false, "POST");
   2120   }
   2121 
   2122   private void testRedirect(boolean temporary, String method) throws Exception {
   2123     MockResponse response1 = new MockResponse()
   2124         .setResponseCode(temporary ? HTTP_TEMP_REDIRECT : HTTP_PERM_REDIRECT)
   2125         .addHeader("Location: /page2");
   2126     if (!method.equals("HEAD")) {
   2127       response1.setBody("This page has moved!");
   2128     }
   2129     server.enqueue(response1);
   2130     server.enqueue(new MockResponse().setBody("Page 2"));
   2131 
   2132     connection = client.open(server.getUrl("/page1"));
   2133     connection.setRequestMethod(method);
   2134     byte[] requestBody = { 'A', 'B', 'C', 'D' };
   2135     if (method.equals("POST")) {
   2136       connection.setDoOutput(true);
   2137       OutputStream outputStream = connection.getOutputStream();
   2138       outputStream.write(requestBody);
   2139       outputStream.close();
   2140     }
   2141 
   2142     String response = readAscii(connection.getInputStream(), Integer.MAX_VALUE);
   2143 
   2144     RecordedRequest page1 = server.takeRequest();
   2145     assertEquals(method + " /page1 HTTP/1.1", page1.getRequestLine());
   2146 
   2147     if (method.equals("GET")) {
   2148       assertEquals("Page 2", response);
   2149     } else if (method.equals("HEAD"))  {
   2150       assertEquals("", response);
   2151     } else {
   2152       // Methods other than GET/HEAD shouldn't follow the redirect
   2153       if (method.equals("POST")) {
   2154         assertTrue(connection.getDoOutput());
   2155         assertEquals("ABCD", page1.getBody().readUtf8());
   2156       }
   2157       assertEquals(1, server.getRequestCount());
   2158       assertEquals("This page has moved!", response);
   2159       return;
   2160     }
   2161 
   2162     // GET/HEAD requests should have followed the redirect with the same method
   2163     assertFalse(connection.getDoOutput());
   2164     assertEquals(2, server.getRequestCount());
   2165     RecordedRequest page2 = server.takeRequest();
   2166     assertEquals(method + " /page2 HTTP/1.1", page2.getRequestLine());
   2167   }
   2168 
   2169   @Test public void follow20Redirects() throws Exception {
   2170     for (int i = 0; i < 20; i++) {
   2171       server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
   2172           .addHeader("Location: /" + (i + 1))
   2173           .setBody("Redirecting to /" + (i + 1)));
   2174     }
   2175     server.enqueue(new MockResponse().setBody("Success!"));
   2176 
   2177     connection = client.open(server.getUrl("/0"));
   2178     assertContent("Success!", connection);
   2179     assertEquals(server.getUrl("/20"), connection.getURL());
   2180   }
   2181 
   2182   @Test public void doesNotFollow21Redirects() throws Exception {
   2183     for (int i = 0; i < 21; i++) {
   2184       server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
   2185           .addHeader("Location: /" + (i + 1))
   2186           .setBody("Redirecting to /" + (i + 1)));
   2187     }
   2188 
   2189     connection = client.open(server.getUrl("/0"));
   2190     try {
   2191       connection.getInputStream();
   2192       fail();
   2193     } catch (ProtocolException expected) {
   2194       assertEquals(HttpURLConnection.HTTP_MOVED_TEMP, connection.getResponseCode());
   2195       assertEquals("Too many follow-up requests: 21", expected.getMessage());
   2196       assertContent("Redirecting to /21", connection);
   2197       assertEquals(server.getUrl("/20"), connection.getURL());
   2198     }
   2199   }
   2200 
   2201   @Test public void httpsWithCustomTrustManager() throws Exception {
   2202     RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier();
   2203     RecordingTrustManager trustManager = new RecordingTrustManager(sslContext);
   2204     SSLContext sc = SSLContext.getInstance("TLS");
   2205     sc.init(null, new TrustManager[] {trustManager}, new SecureRandom());
   2206 
   2207     client.client().setHostnameVerifier(hostnameVerifier);
   2208     client.client().setSslSocketFactory(sc.getSocketFactory());
   2209     server.useHttps(sslContext.getSocketFactory(), false);
   2210     server.enqueue(new MockResponse().setBody("ABC"));
   2211     server.enqueue(new MockResponse().setBody("DEF"));
   2212     server.enqueue(new MockResponse().setBody("GHI"));
   2213 
   2214     URL url = server.getUrl("/");
   2215     assertContent("ABC", client.open(url));
   2216     assertContent("DEF", client.open(url));
   2217     assertContent("GHI", client.open(url));
   2218 
   2219     assertEquals(Arrays.asList("verify " + server.getHostName()), hostnameVerifier.calls);
   2220     assertEquals(Arrays.asList("checkServerTrusted [CN=" + server.getHostName() + " 1]"),
   2221         trustManager.calls);
   2222   }
   2223 
   2224   @Test public void readTimeouts() throws IOException {
   2225     // This relies on the fact that MockWebServer doesn't close the
   2226     // connection after a response has been sent. This causes the client to
   2227     // try to read more bytes than are sent, which results in a timeout.
   2228     MockResponse timeout =
   2229         new MockResponse().setBody("ABC").clearHeaders().addHeader("Content-Length: 4");
   2230     server.enqueue(timeout);
   2231     server.enqueue(new MockResponse().setBody("unused")); // to keep the server alive
   2232 
   2233     URLConnection connection = client.open(server.getUrl("/"));
   2234     connection.setReadTimeout(1000);
   2235     InputStream in = connection.getInputStream();
   2236     assertEquals('A', in.read());
   2237     assertEquals('B', in.read());
   2238     assertEquals('C', in.read());
   2239     try {
   2240       in.read(); // if Content-Length was accurate, this would return -1 immediately
   2241       fail();
   2242     } catch (SocketTimeoutException expected) {
   2243     }
   2244     in.close();
   2245   }
   2246 
   2247   /** Confirm that an unacknowledged write times out. */
   2248   @Test public void writeTimeouts() throws IOException {
   2249     MockWebServer server = new MockWebServer();
   2250     // Sockets on some platforms can have large buffers that mean writes do not block when
   2251     // required. These socket factories explicitly set the buffer sizes on sockets created.
   2252     final int SOCKET_BUFFER_SIZE = 4 * 1024;
   2253     server.setServerSocketFactory(
   2254         new DelegatingServerSocketFactory(ServerSocketFactory.getDefault()) {
   2255           @Override
   2256           protected ServerSocket configureServerSocket(ServerSocket serverSocket)
   2257               throws IOException {
   2258             serverSocket.setReceiveBufferSize(SOCKET_BUFFER_SIZE);
   2259             return serverSocket;
   2260           }
   2261         });
   2262     client.client().setSocketFactory(new DelegatingSocketFactory(SocketFactory.getDefault()) {
   2263       @Override
   2264       protected Socket configureSocket(Socket socket) throws IOException {
   2265         socket.setReceiveBufferSize(SOCKET_BUFFER_SIZE);
   2266         socket.setSendBufferSize(SOCKET_BUFFER_SIZE);
   2267         return socket;
   2268       }
   2269     });
   2270 
   2271     server.start();
   2272     server.enqueue(new MockResponse()
   2273         .throttleBody(1, 1, TimeUnit.SECONDS)); // Prevent the server from reading!
   2274 
   2275     client.client().setWriteTimeout(500, TimeUnit.MILLISECONDS);
   2276     connection = client.open(server.getUrl("/"));
   2277     connection.setDoOutput(true);
   2278     connection.setChunkedStreamingMode(0);
   2279     OutputStream out = connection.getOutputStream();
   2280     try {
   2281       byte[] data = new byte[2 * 1024 * 1024]; // 2 MiB.
   2282       out.write(data);
   2283       fail();
   2284     } catch (SocketTimeoutException expected) {
   2285     }
   2286   }
   2287 
   2288   @Test public void setChunkedEncodingAsRequestProperty() throws IOException, InterruptedException {
   2289     server.enqueue(new MockResponse());
   2290 
   2291     connection = client.open(server.getUrl("/"));
   2292     connection.setRequestProperty("Transfer-encoding", "chunked");
   2293     connection.setDoOutput(true);
   2294     connection.getOutputStream().write("ABC".getBytes("UTF-8"));
   2295     assertEquals(200, connection.getResponseCode());
   2296 
   2297     RecordedRequest request = server.takeRequest();
   2298     assertEquals("ABC", request.getBody().readUtf8());
   2299   }
   2300 
   2301   @Test public void connectionCloseInRequest() throws IOException, InterruptedException {
   2302     server.enqueue(new MockResponse()); // server doesn't honor the connection: close header!
   2303     server.enqueue(new MockResponse());
   2304 
   2305     HttpURLConnection a = client.open(server.getUrl("/"));
   2306     a.setRequestProperty("Connection", "close");
   2307     assertEquals(200, a.getResponseCode());
   2308 
   2309     HttpURLConnection b = client.open(server.getUrl("/"));
   2310     assertEquals(200, b.getResponseCode());
   2311 
   2312     assertEquals(0, server.takeRequest().getSequenceNumber());
   2313     assertEquals("When connection: close is used, each request should get its own connection", 0,
   2314         server.takeRequest().getSequenceNumber());
   2315   }
   2316 
   2317   @Test public void connectionCloseInResponse() throws IOException, InterruptedException {
   2318     server.enqueue(new MockResponse().addHeader("Connection: close"));
   2319     server.enqueue(new MockResponse());
   2320 
   2321     HttpURLConnection a = client.open(server.getUrl("/"));
   2322     assertEquals(200, a.getResponseCode());
   2323 
   2324     HttpURLConnection b = client.open(server.getUrl("/"));
   2325     assertEquals(200, b.getResponseCode());
   2326 
   2327     assertEquals(0, server.takeRequest().getSequenceNumber());
   2328     assertEquals("When connection: close is used, each request should get its own connection", 0,
   2329         server.takeRequest().getSequenceNumber());
   2330   }
   2331 
   2332   @Test public void connectionCloseWithRedirect() throws IOException, InterruptedException {
   2333     MockResponse response = new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
   2334         .addHeader("Location: /foo")
   2335         .addHeader("Connection: close");
   2336     server.enqueue(response);
   2337     server.enqueue(new MockResponse().setBody("This is the new location!"));
   2338 
   2339     URLConnection connection = client.open(server.getUrl("/"));
   2340     assertEquals("This is the new location!",
   2341         readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   2342 
   2343     assertEquals(0, server.takeRequest().getSequenceNumber());
   2344     assertEquals("When connection: close is used, each request should get its own connection", 0,
   2345         server.takeRequest().getSequenceNumber());
   2346   }
   2347 
   2348   /**
   2349    * Retry redirects if the socket is closed.
   2350    * https://code.google.com/p/android/issues/detail?id=41576
   2351    */
   2352   @Test public void sameConnectionRedirectAndReuse() throws Exception {
   2353     server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
   2354         .setSocketPolicy(SHUTDOWN_INPUT_AT_END)
   2355         .addHeader("Location: /foo"));
   2356     server.enqueue(new MockResponse().setBody("This is the new page!"));
   2357 
   2358     assertContent("This is the new page!", client.open(server.getUrl("/")));
   2359 
   2360     assertEquals(0, server.takeRequest().getSequenceNumber());
   2361     assertEquals(0, server.takeRequest().getSequenceNumber());
   2362   }
   2363 
   2364   @Test public void responseCodeDisagreesWithHeaders() throws IOException, InterruptedException {
   2365     server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NO_CONTENT)
   2366         .setBody("This body is not allowed!"));
   2367 
   2368     URLConnection connection = client.open(server.getUrl("/"));
   2369     assertEquals("This body is not allowed!",
   2370         readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   2371   }
   2372 
   2373   @Test public void singleByteReadIsSigned() throws IOException {
   2374     server.enqueue(new MockResponse().setBody(new Buffer().writeByte(-2).writeByte(-1)));
   2375 
   2376     connection = client.open(server.getUrl("/"));
   2377     InputStream in = connection.getInputStream();
   2378     assertEquals(254, in.read());
   2379     assertEquals(255, in.read());
   2380     assertEquals(-1, in.read());
   2381   }
   2382 
   2383   @Test public void flushAfterStreamTransmittedWithChunkedEncoding() throws IOException {
   2384     testFlushAfterStreamTransmitted(TransferKind.CHUNKED);
   2385   }
   2386 
   2387   @Test public void flushAfterStreamTransmittedWithFixedLength() throws IOException {
   2388     testFlushAfterStreamTransmitted(TransferKind.FIXED_LENGTH);
   2389   }
   2390 
   2391   @Test public void flushAfterStreamTransmittedWithNoLengthHeaders() throws IOException {
   2392     testFlushAfterStreamTransmitted(TransferKind.END_OF_STREAM);
   2393   }
   2394 
   2395   /**
   2396    * We explicitly permit apps to close the upload stream even after it has
   2397    * been transmitted.  We also permit flush so that buffered streams can
   2398    * do a no-op flush when they are closed. http://b/3038470
   2399    */
   2400   private void testFlushAfterStreamTransmitted(TransferKind transferKind) throws IOException {
   2401     server.enqueue(new MockResponse().setBody("abc"));
   2402 
   2403     connection = client.open(server.getUrl("/"));
   2404     connection.setDoOutput(true);
   2405     byte[] upload = "def".getBytes("UTF-8");
   2406 
   2407     if (transferKind == TransferKind.CHUNKED) {
   2408       connection.setChunkedStreamingMode(0);
   2409     } else if (transferKind == TransferKind.FIXED_LENGTH) {
   2410       connection.setFixedLengthStreamingMode(upload.length);
   2411     }
   2412 
   2413     OutputStream out = connection.getOutputStream();
   2414     out.write(upload);
   2415     assertEquals("abc", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   2416 
   2417     out.flush(); // Dubious but permitted.
   2418     try {
   2419       out.write("ghi".getBytes("UTF-8"));
   2420       fail();
   2421     } catch (IOException expected) {
   2422     }
   2423   }
   2424 
   2425   @Test public void getHeadersThrows() throws IOException {
   2426     server.enqueue(new MockResponse().setSocketPolicy(DISCONNECT_AT_START));
   2427 
   2428     connection = client.open(server.getUrl("/"));
   2429     try {
   2430       connection.getInputStream();
   2431       fail();
   2432     } catch (IOException expected) {
   2433     }
   2434 
   2435     try {
   2436       connection.getInputStream();
   2437       fail();
   2438     } catch (IOException expected) {
   2439     }
   2440   }
   2441 
   2442   @Test public void dnsFailureThrowsIOException() throws IOException {
   2443     connection = client.open(new URL("http://host.unlikelytld"));
   2444     try {
   2445       connection.connect();
   2446       fail();
   2447     } catch (IOException expected) {
   2448     }
   2449   }
   2450 
   2451   @Test public void malformedUrlThrowsUnknownHostException() throws IOException {
   2452     connection = client.open(new URL("http://./foo.html"));
   2453     try {
   2454       connection.connect();
   2455       fail();
   2456     } catch (UnknownHostException expected) {
   2457     }
   2458   }
   2459 
   2460   @Test public void getKeepAlive() throws Exception {
   2461     server.enqueue(new MockResponse().setBody("ABC"));
   2462 
   2463     // The request should work once and then fail
   2464     HttpURLConnection connection1 = client.open(server.getUrl("/"));
   2465     connection1.setReadTimeout(100);
   2466     InputStream input = connection1.getInputStream();
   2467     assertEquals("ABC", readAscii(input, Integer.MAX_VALUE));
   2468     server.shutdown();
   2469     try {
   2470       HttpURLConnection connection2 = client.open(server.getUrl(""));
   2471       connection2.setReadTimeout(100);
   2472       connection2.getInputStream();
   2473       fail();
   2474     } catch (ConnectException expected) {
   2475     }
   2476   }
   2477 
   2478   /** http://code.google.com/p/android/issues/detail?id=14562 */
   2479   @Test public void readAfterLastByte() throws Exception {
   2480     server.enqueue(new MockResponse().setBody("ABC")
   2481         .clearHeaders()
   2482         .addHeader("Connection: close")
   2483         .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END));
   2484 
   2485     connection = client.open(server.getUrl("/"));
   2486     InputStream in = connection.getInputStream();
   2487     assertEquals("ABC", readAscii(in, 3));
   2488     assertEquals(-1, in.read());
   2489     assertEquals(-1, in.read()); // throws IOException in Gingerbread
   2490   }
   2491 
   2492   @Test public void getContent() throws Exception {
   2493     server.enqueue(new MockResponse().addHeader("Content-Type: text/plain").setBody("A"));
   2494     connection = client.open(server.getUrl("/"));
   2495     InputStream in = (InputStream) connection.getContent();
   2496     assertEquals("A", readAscii(in, Integer.MAX_VALUE));
   2497   }
   2498 
   2499   @Test public void getContentOfType() throws Exception {
   2500     server.enqueue(new MockResponse().addHeader("Content-Type: text/plain").setBody("A"));
   2501     connection = client.open(server.getUrl("/"));
   2502     try {
   2503       connection.getContent(null);
   2504       fail();
   2505     } catch (NullPointerException expected) {
   2506     }
   2507     try {
   2508       connection.getContent(new Class[] { null });
   2509       fail();
   2510     } catch (NullPointerException expected) {
   2511     }
   2512     assertNull(connection.getContent(new Class[] { getClass() }));
   2513     connection.getInputStream().close();
   2514   }
   2515 
   2516   @Test public void getOutputStreamOnGetFails() throws Exception {
   2517     server.enqueue(new MockResponse());
   2518     connection = client.open(server.getUrl("/"));
   2519     try {
   2520       connection.getOutputStream();
   2521       fail();
   2522     } catch (ProtocolException expected) {
   2523     }
   2524     connection.getInputStream().close();
   2525   }
   2526 
   2527   @Test public void getOutputAfterGetInputStreamFails() throws Exception {
   2528     server.enqueue(new MockResponse());
   2529     connection = client.open(server.getUrl("/"));
   2530     connection.setDoOutput(true);
   2531     try {
   2532       connection.getInputStream();
   2533       connection.getOutputStream();
   2534       fail();
   2535     } catch (ProtocolException expected) {
   2536     }
   2537   }
   2538 
   2539   @Test public void setDoOutputOrDoInputAfterConnectFails() throws Exception {
   2540     server.enqueue(new MockResponse());
   2541     connection = client.open(server.getUrl("/"));
   2542     connection.connect();
   2543     try {
   2544       connection.setDoOutput(true);
   2545       fail();
   2546     } catch (IllegalStateException expected) {
   2547     }
   2548     try {
   2549       connection.setDoInput(true);
   2550       fail();
   2551     } catch (IllegalStateException expected) {
   2552     }
   2553     connection.getInputStream().close();
   2554   }
   2555 
   2556   @Test public void clientSendsContentLength() throws Exception {
   2557     server.enqueue(new MockResponse().setBody("A"));
   2558     connection = client.open(server.getUrl("/"));
   2559     connection.setDoOutput(true);
   2560     OutputStream out = connection.getOutputStream();
   2561     out.write(new byte[] { 'A', 'B', 'C' });
   2562     out.close();
   2563     assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
   2564     RecordedRequest request = server.takeRequest();
   2565     assertEquals("3", request.getHeader("Content-Length"));
   2566     connection.getInputStream().close();
   2567   }
   2568 
   2569   @Test public void getContentLengthConnects() throws Exception {
   2570     server.enqueue(new MockResponse().setBody("ABC"));
   2571     connection = client.open(server.getUrl("/"));
   2572     assertEquals(3, connection.getContentLength());
   2573     connection.getInputStream().close();
   2574   }
   2575 
   2576   @Test public void getContentTypeConnects() throws Exception {
   2577     server.enqueue(new MockResponse().addHeader("Content-Type: text/plain").setBody("ABC"));
   2578     connection = client.open(server.getUrl("/"));
   2579     assertEquals("text/plain", connection.getContentType());
   2580     connection.getInputStream().close();
   2581   }
   2582 
   2583   @Test public void getContentEncodingConnects() throws Exception {
   2584     server.enqueue(new MockResponse().addHeader("Content-Encoding: identity").setBody("ABC"));
   2585     connection = client.open(server.getUrl("/"));
   2586     assertEquals("identity", connection.getContentEncoding());
   2587     connection.getInputStream().close();
   2588   }
   2589 
   2590   // http://b/4361656
   2591   @Test public void urlContainsQueryButNoPath() throws Exception {
   2592     server.enqueue(new MockResponse().setBody("A"));
   2593 
   2594     URL url = new URL("http", server.getHostName(), server.getPort(), "?query");
   2595     assertEquals("A", readAscii(client.open(url).getInputStream(), Integer.MAX_VALUE));
   2596     RecordedRequest request = server.takeRequest();
   2597     assertEquals("GET /?query HTTP/1.1", request.getRequestLine());
   2598   }
   2599 
   2600   @Test public void doOutputForMethodThatDoesntSupportOutput() throws Exception {
   2601     connection = client.open(server.getUrl("/"));
   2602     connection.setRequestMethod("HEAD");
   2603     connection.setDoOutput(true);
   2604     try {
   2605       connection.connect();
   2606       fail();
   2607     } catch (IOException expected) {
   2608     }
   2609   }
   2610 
   2611   // http://code.google.com/p/android/issues/detail?id=20442
   2612   @Test public void inputStreamAvailableWithChunkedEncoding() throws Exception {
   2613     testInputStreamAvailable(TransferKind.CHUNKED);
   2614   }
   2615 
   2616   @Test public void inputStreamAvailableWithContentLengthHeader() throws Exception {
   2617     testInputStreamAvailable(TransferKind.FIXED_LENGTH);
   2618   }
   2619 
   2620   @Test public void inputStreamAvailableWithNoLengthHeaders() throws Exception {
   2621     testInputStreamAvailable(TransferKind.END_OF_STREAM);
   2622   }
   2623 
   2624   private void testInputStreamAvailable(TransferKind transferKind) throws IOException {
   2625     String body = "ABCDEFGH";
   2626     MockResponse response = new MockResponse();
   2627     transferKind.setBody(response, body, 4);
   2628     server.enqueue(response);
   2629     connection = client.open(server.getUrl("/"));
   2630     InputStream in = connection.getInputStream();
   2631     for (int i = 0; i < body.length(); i++) {
   2632       assertTrue(in.available() >= 0);
   2633       assertEquals(body.charAt(i), in.read());
   2634     }
   2635     assertEquals(0, in.available());
   2636     assertEquals(-1, in.read());
   2637   }
   2638 
   2639   @Test public void postFailsWithBufferedRequestForSmallRequest() throws Exception {
   2640     reusedConnectionFailsWithPost(TransferKind.END_OF_STREAM, 1024);
   2641   }
   2642 
   2643   @Test public void postFailsWithBufferedRequestForLargeRequest() throws Exception {
   2644     reusedConnectionFailsWithPost(TransferKind.END_OF_STREAM, 16384);
   2645   }
   2646 
   2647   @Test public void postFailsWithChunkedRequestForSmallRequest() throws Exception {
   2648     reusedConnectionFailsWithPost(TransferKind.CHUNKED, 1024);
   2649   }
   2650 
   2651   @Test public void postFailsWithChunkedRequestForLargeRequest() throws Exception {
   2652     reusedConnectionFailsWithPost(TransferKind.CHUNKED, 16384);
   2653   }
   2654 
   2655   @Test public void postFailsWithFixedLengthRequestForSmallRequest() throws Exception {
   2656     reusedConnectionFailsWithPost(TransferKind.FIXED_LENGTH, 1024);
   2657   }
   2658 
   2659   @Test public void postFailsWithFixedLengthRequestForLargeRequest() throws Exception {
   2660     reusedConnectionFailsWithPost(TransferKind.FIXED_LENGTH, 16384);
   2661   }
   2662 
   2663   private void reusedConnectionFailsWithPost(TransferKind transferKind, int requestSize)
   2664       throws Exception {
   2665     server.enqueue(new MockResponse().setBody("A").setSocketPolicy(DISCONNECT_AT_END));
   2666     server.enqueue(new MockResponse().setBody("B"));
   2667     server.enqueue(new MockResponse().setBody("C"));
   2668 
   2669     assertContent("A", client.open(server.getUrl("/a")));
   2670 
   2671     // If the request body is larger than OkHttp's replay buffer, the failure may still occur.
   2672     byte[] requestBody = new byte[requestSize];
   2673     new Random(0).nextBytes(requestBody);
   2674 
   2675     for (int j = 0; j < 2; j++) {
   2676       try {
   2677         connection = client.open(server.getUrl("/b"));
   2678         connection.setRequestMethod("POST");
   2679         transferKind.setForRequest(connection, requestBody.length);
   2680         for (int i = 0; i < requestBody.length; i += 1024) {
   2681           connection.getOutputStream().write(requestBody, i, 1024);
   2682         }
   2683         connection.getOutputStream().close();
   2684         assertContent("B", connection);
   2685         break;
   2686       } catch (IOException socketException) {
   2687         // If there's a socket exception, this must have a streamed request body.
   2688         assertEquals(0, j);
   2689         assertTrue(transferKind == TransferKind.CHUNKED
   2690             || transferKind == TransferKind.FIXED_LENGTH);
   2691       }
   2692     }
   2693 
   2694     RecordedRequest requestA = server.takeRequest();
   2695     assertEquals("/a", requestA.getPath());
   2696     RecordedRequest requestB = server.takeRequest();
   2697     assertEquals("/b", requestB.getPath());
   2698     assertEquals(Arrays.toString(requestBody), Arrays.toString(requestB.getBody().readByteArray()));
   2699   }
   2700 
   2701   @Test public void postBodyRetransmittedOnFailureRecovery() throws Exception {
   2702     server.enqueue(new MockResponse().setBody("abc"));
   2703     server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AFTER_REQUEST));
   2704     server.enqueue(new MockResponse().setBody("def"));
   2705 
   2706     // Seed the connection pool so we have something that can fail.
   2707     assertContent("abc", client.open(server.getUrl("/")));
   2708 
   2709     HttpURLConnection post = client.open(server.getUrl("/"));
   2710     post.setDoOutput(true);
   2711     post.getOutputStream().write("body!".getBytes(Util.UTF_8));
   2712     assertContent("def", post);
   2713 
   2714     RecordedRequest get = server.takeRequest();
   2715     assertEquals(0, get.getSequenceNumber());
   2716 
   2717     RecordedRequest post1 = server.takeRequest();
   2718     assertEquals("body!", post1.getBody().readUtf8());
   2719     assertEquals(1, post1.getSequenceNumber());
   2720 
   2721     RecordedRequest post2 = server.takeRequest();
   2722     assertEquals("body!", post2.getBody().readUtf8());
   2723     assertEquals(0, post2.getSequenceNumber());
   2724   }
   2725 
   2726   @Test public void fullyBufferedPostIsTooShort() throws Exception {
   2727     server.enqueue(new MockResponse().setBody("A"));
   2728 
   2729     connection = client.open(server.getUrl("/b"));
   2730     connection.setRequestProperty("Content-Length", "4");
   2731     connection.setRequestMethod("POST");
   2732     OutputStream out = connection.getOutputStream();
   2733     out.write('a');
   2734     out.write('b');
   2735     out.write('c');
   2736     try {
   2737       out.close();
   2738       fail();
   2739     } catch (IOException expected) {
   2740     }
   2741   }
   2742 
   2743   @Test public void fullyBufferedPostIsTooLong() throws Exception {
   2744     server.enqueue(new MockResponse().setBody("A"));
   2745 
   2746     connection = client.open(server.getUrl("/b"));
   2747     connection.setRequestProperty("Content-Length", "3");
   2748     connection.setRequestMethod("POST");
   2749     OutputStream out = connection.getOutputStream();
   2750     out.write('a');
   2751     out.write('b');
   2752     out.write('c');
   2753     try {
   2754       out.write('d');
   2755       out.flush();
   2756       fail();
   2757     } catch (IOException expected) {
   2758     }
   2759   }
   2760 
   2761   @Test @Ignore public void testPooledConnectionsDetectHttp10() {
   2762     // TODO: write a test that shows pooled connections detect HTTP/1.0 (vs. HTTP/1.1)
   2763     fail("TODO");
   2764   }
   2765 
   2766   @Test @Ignore public void postBodiesRetransmittedOnAuthProblems() {
   2767     fail("TODO");
   2768   }
   2769 
   2770   @Test @Ignore public void cookiesAndTrailers() {
   2771     // Do cookie headers get processed too many times?
   2772     fail("TODO");
   2773   }
   2774 
   2775   @Test @Ignore public void headerNamesContainingNullCharacter() {
   2776     // This is relevant for SPDY
   2777     fail("TODO");
   2778   }
   2779 
   2780   @Test @Ignore public void headerValuesContainingNullCharacter() {
   2781     // This is relevant for SPDY
   2782     fail("TODO");
   2783   }
   2784 
   2785   @Test public void emptyRequestHeaderValueIsAllowed() throws Exception {
   2786     server.enqueue(new MockResponse().setBody("body"));
   2787     connection = client.open(server.getUrl("/"));
   2788     connection.addRequestProperty("B", "");
   2789     assertContent("body", connection);
   2790     assertEquals("", connection.getRequestProperty("B"));
   2791   }
   2792 
   2793   @Test public void emptyResponseHeaderValueIsAllowed() throws Exception {
   2794     server.enqueue(new MockResponse().addHeader("A:").setBody("body"));
   2795     connection = client.open(server.getUrl("/"));
   2796     assertContent("body", connection);
   2797     assertEquals("", connection.getHeaderField("A"));
   2798   }
   2799 
   2800   @Test public void emptyRequestHeaderNameIsStrict() throws Exception {
   2801     server.enqueue(new MockResponse().setBody("body"));
   2802     connection = client.open(server.getUrl("/"));
   2803     try {
   2804       connection.setRequestProperty("", "A");
   2805       fail();
   2806     } catch (IllegalArgumentException expected) {
   2807     }
   2808   }
   2809 
   2810   @Test public void emptyResponseHeaderNameIsLenient() throws Exception {
   2811     Headers.Builder headers = new Headers.Builder();
   2812     Internal.instance.addLenient(headers, ":A");
   2813     server.enqueue(new MockResponse().setHeaders(headers.build()).setBody("body"));
   2814     connection = client.open(server.getUrl("/"));
   2815     connection.getResponseCode();
   2816     assertEquals("A", connection.getHeaderField(""));
   2817     connection.getInputStream().close();
   2818   }
   2819 
   2820   @Test public void requestHeaderValidationIsStrict() throws Exception {
   2821     connection = client.open(server.getUrl("/"));
   2822     try {
   2823       connection.addRequestProperty("a\tb", "Value");
   2824       fail();
   2825     } catch (IllegalArgumentException expected) {
   2826     }
   2827     try {
   2828       connection.addRequestProperty("Name", "c\u007fd");
   2829       fail();
   2830     } catch (IllegalArgumentException expected) {
   2831     }
   2832     try {
   2833       connection.addRequestProperty("", "Value");
   2834       fail();
   2835     } catch (IllegalArgumentException expected) {
   2836     }
   2837     try {
   2838       connection.addRequestProperty("\ud83c\udf69", "Value");
   2839       fail();
   2840     } catch (IllegalArgumentException expected) {
   2841     }
   2842 
   2843     // ANDROID-BEGIN Disabled for http://b/28867041
   2844     // try {
   2845     //   connection.addRequestProperty("Name", "\u2615\ufe0f");
   2846     //   fail();
   2847     // } catch (IllegalArgumentException expected) {
   2848     // }
   2849     // ANDROID-END
   2850   }
   2851 
   2852   @Test public void responseHeaderParsingIsLenient() throws Exception {
   2853     Headers headers = new Headers.Builder()
   2854         .add("Content-Length", "0")
   2855         .addLenient("a\tb: c\u007fd")
   2856         .addLenient(": ef")
   2857         .addLenient("\ud83c\udf69: \u2615\ufe0f")
   2858         .build();
   2859     server.enqueue(new MockResponse().setHeaders(headers));
   2860 
   2861     connection = client.open(server.getUrl("/"));
   2862     connection.getResponseCode();
   2863     assertEquals("c\u007fd", connection.getHeaderField("a\tb"));
   2864     assertEquals("\u2615\ufe0f", connection.getHeaderField("\ud83c\udf69"));
   2865     assertEquals("ef", connection.getHeaderField(""));
   2866   }
   2867 
   2868   @Test @Ignore public void deflateCompression() {
   2869     fail("TODO");
   2870   }
   2871 
   2872   @Test @Ignore public void postBodiesRetransmittedOnIpAddressProblems() {
   2873     fail("TODO");
   2874   }
   2875 
   2876   @Test @Ignore public void pooledConnectionProblemsNotReportedToProxySelector() {
   2877     fail("TODO");
   2878   }
   2879 
   2880   @Test public void customBasicAuthenticator() throws Exception {
   2881     MockResponse pleaseAuthenticate = new MockResponse().setResponseCode(401)
   2882         .addHeader("WWW-Authenticate: Basic realm=\"protected area\"")
   2883         .setBody("Please authenticate.");
   2884     server.enqueue(pleaseAuthenticate);
   2885     server.enqueue(new MockResponse().setBody("A"));
   2886 
   2887     String credential = Credentials.basic("jesse", "peanutbutter");
   2888     RecordingOkAuthenticator authenticator = new RecordingOkAuthenticator(credential);
   2889     client.client().setAuthenticator(authenticator);
   2890     assertContent("A", client.open(server.getUrl("/private")));
   2891 
   2892     assertNull(server.takeRequest().getHeader("Authorization"));
   2893     assertEquals(credential, server.takeRequest().getHeader("Authorization"));
   2894 
   2895     assertEquals(Proxy.NO_PROXY, authenticator.onlyProxy());
   2896     Response response = authenticator.onlyResponse();
   2897     assertEquals("/private", response.request().url().getPath());
   2898     assertEquals(Arrays.asList(new Challenge("Basic", "protected area")), response.challenges());
   2899   }
   2900 
   2901   @Test public void customTokenAuthenticator() throws Exception {
   2902     MockResponse pleaseAuthenticate = new MockResponse().setResponseCode(401)
   2903             .addHeader("WWW-Authenticate: Bearer realm=\"oauthed\"")
   2904             .setBody("Please authenticate.");
   2905     server.enqueue(pleaseAuthenticate);
   2906     server.enqueue(new MockResponse().setBody("A"));
   2907 
   2908     RecordingOkAuthenticator authenticator = new RecordingOkAuthenticator("oauthed abc123");
   2909     client.client().setAuthenticator(authenticator);
   2910     assertContent("A", client.open(server.getUrl("/private")));
   2911 
   2912     assertNull(server.takeRequest().getHeader("Authorization"));
   2913     assertEquals("oauthed abc123", server.takeRequest().getHeader("Authorization"));
   2914 
   2915     Response response = authenticator.onlyResponse();
   2916     assertEquals("/private", response.request().url().getPath());
   2917     assertEquals(Arrays.asList(new Challenge("Bearer", "oauthed")), response.challenges());
   2918   }
   2919 
   2920   @Test public void authenticateCallsTrackedAsRedirects() throws Exception {
   2921     server.enqueue(new MockResponse()
   2922         .setResponseCode(302)
   2923         .addHeader("Location: /b"));
   2924     server.enqueue(new MockResponse()
   2925         .setResponseCode(401)
   2926         .addHeader("WWW-Authenticate: Basic realm=\"protected area\""));
   2927     server.enqueue(new MockResponse().setBody("c"));
   2928 
   2929     RecordingOkAuthenticator authenticator = new RecordingOkAuthenticator(
   2930         Credentials.basic("jesse", "peanutbutter"));
   2931     client.client().setAuthenticator(authenticator);
   2932     assertContent("c", client.open(server.getUrl("/a")));
   2933 
   2934     Response challengeResponse = authenticator.responses.get(0);
   2935     assertEquals("/b", challengeResponse.request().url().getPath());
   2936 
   2937     Response redirectedBy = challengeResponse.priorResponse();
   2938     assertEquals("/a", redirectedBy.request().url().getPath());
   2939   }
   2940 
   2941   @Test public void attemptAuthorization20Times() throws Exception {
   2942     for (int i = 0; i < 20; i++) {
   2943       server.enqueue(new MockResponse().setResponseCode(401));
   2944     }
   2945     server.enqueue(new MockResponse().setBody("Success!"));
   2946 
   2947     String credential = Credentials.basic("jesse", "peanutbutter");
   2948     client.client().setAuthenticator(new RecordingOkAuthenticator(credential));
   2949 
   2950     connection = client.open(server.getUrl("/0"));
   2951     assertContent("Success!", connection);
   2952   }
   2953 
   2954   @Test public void doesNotAttemptAuthorization21Times() throws Exception {
   2955     for (int i = 0; i < 21; i++) {
   2956       server.enqueue(new MockResponse().setResponseCode(401));
   2957     }
   2958 
   2959     String credential = Credentials.basic("jesse", "peanutbutter");
   2960     client.client().setAuthenticator(new RecordingOkAuthenticator(credential));
   2961 
   2962     connection = client.open(server.getUrl("/"));
   2963     try {
   2964       connection.getInputStream();
   2965       fail();
   2966     } catch (ProtocolException expected) {
   2967       assertEquals(401, connection.getResponseCode());
   2968       assertEquals("Too many follow-up requests: 21", expected.getMessage());
   2969     }
   2970   }
   2971 
   2972   @Test public void setsNegotiatedProtocolHeader_SPDY_3() throws Exception {
   2973     setsNegotiatedProtocolHeader(Protocol.SPDY_3);
   2974   }
   2975 
   2976   @Test public void setsNegotiatedProtocolHeader_HTTP_2() throws Exception {
   2977     setsNegotiatedProtocolHeader(Protocol.HTTP_2);
   2978   }
   2979 
   2980   private void setsNegotiatedProtocolHeader(Protocol protocol) throws IOException {
   2981     enableProtocol(protocol);
   2982     server.enqueue(new MockResponse().setBody("A"));
   2983     client.client().setProtocols(Arrays.asList(protocol, Protocol.HTTP_1_1));
   2984     connection = client.open(server.getUrl("/"));
   2985     List<String> protocolValues = connection.getHeaderFields().get(SELECTED_PROTOCOL);
   2986     assertEquals(Arrays.asList(protocol.toString()), protocolValues);
   2987     assertContent("A", connection);
   2988   }
   2989 
   2990   @Test public void http10SelectedProtocol() throws Exception {
   2991     server.enqueue(new MockResponse().setStatus("HTTP/1.0 200 OK"));
   2992     connection = client.open(server.getUrl("/"));
   2993     List<String> protocolValues = connection.getHeaderFields().get(SELECTED_PROTOCOL);
   2994     assertEquals(Arrays.asList("http/1.0"), protocolValues);
   2995   }
   2996 
   2997   @Test public void http11SelectedProtocol() throws Exception {
   2998     server.enqueue(new MockResponse().setStatus("HTTP/1.1 200 OK"));
   2999     connection = client.open(server.getUrl("/"));
   3000     List<String> protocolValues = connection.getHeaderFields().get(SELECTED_PROTOCOL);
   3001     assertEquals(Arrays.asList("http/1.1"), protocolValues);
   3002   }
   3003 
   3004   /** For example, empty Protobuf RPC messages end up as a zero-length POST. */
   3005   @Test public void zeroLengthPost() throws IOException, InterruptedException {
   3006     zeroLengthPayload("POST");
   3007   }
   3008 
   3009   @Test public void zeroLengthPost_SPDY_3() throws Exception {
   3010     enableProtocol(Protocol.SPDY_3);
   3011     zeroLengthPost();
   3012   }
   3013 
   3014   @Test public void zeroLengthPost_HTTP_2() throws Exception {
   3015     enableProtocol(Protocol.HTTP_2);
   3016     zeroLengthPost();
   3017   }
   3018 
   3019   /** For example, creating an Amazon S3 bucket ends up as a zero-length POST. */
   3020   @Test public void zeroLengthPut() throws IOException, InterruptedException {
   3021     zeroLengthPayload("PUT");
   3022   }
   3023 
   3024   @Test public void zeroLengthPut_SPDY_3() throws Exception {
   3025     enableProtocol(Protocol.SPDY_3);
   3026     zeroLengthPut();
   3027   }
   3028 
   3029   @Test public void zeroLengthPut_HTTP_2() throws Exception {
   3030     enableProtocol(Protocol.HTTP_2);
   3031     zeroLengthPut();
   3032   }
   3033 
   3034   private void zeroLengthPayload(String method)
   3035       throws IOException, InterruptedException {
   3036     server.enqueue(new MockResponse());
   3037     connection = client.open(server.getUrl("/"));
   3038     connection.setRequestProperty("Content-Length", "0");
   3039     connection.setRequestMethod(method);
   3040     connection.setFixedLengthStreamingMode(0);
   3041     connection.setDoOutput(true);
   3042     assertContent("", connection);
   3043     RecordedRequest zeroLengthPayload = server.takeRequest();
   3044     assertEquals(method, zeroLengthPayload.getMethod());
   3045     assertEquals("0", zeroLengthPayload.getHeader("content-length"));
   3046     assertEquals(0L, zeroLengthPayload.getBodySize());
   3047   }
   3048 
   3049   @Test public void unspecifiedRequestBodyContentTypeGetsDefault() throws Exception {
   3050     server.enqueue(new MockResponse());
   3051 
   3052     connection = client.open(server.getUrl("/"));
   3053     connection.setDoOutput(true);
   3054     connection.getOutputStream().write("abc".getBytes(UTF_8));
   3055     assertEquals(200, connection.getResponseCode());
   3056 
   3057     RecordedRequest request = server.takeRequest();
   3058     assertEquals("application/x-www-form-urlencoded", request.getHeader("Content-Type"));
   3059     assertEquals("3", request.getHeader("Content-Length"));
   3060     assertEquals("abc", request.getBody().readUtf8());
   3061   }
   3062 
   3063   @Test public void setProtocols() throws Exception {
   3064     server.enqueue(new MockResponse().setBody("A"));
   3065     client.client().setProtocols(Arrays.asList(Protocol.HTTP_1_1));
   3066     assertContent("A", client.open(server.getUrl("/")));
   3067   }
   3068 
   3069   @Test public void setProtocolsWithoutHttp11() throws Exception {
   3070     try {
   3071       client.client().setProtocols(Arrays.asList(Protocol.SPDY_3));
   3072       fail();
   3073     } catch (IllegalArgumentException expected) {
   3074     }
   3075   }
   3076 
   3077   @Test public void setProtocolsWithNull() throws Exception {
   3078     try {
   3079       client.client().setProtocols(Arrays.asList(Protocol.HTTP_1_1, null));
   3080       fail();
   3081     } catch (IllegalArgumentException expected) {
   3082     }
   3083   }
   3084 
   3085   @Test public void veryLargeFixedLengthRequest() throws Exception {
   3086     server.setBodyLimit(0);
   3087     server.enqueue(new MockResponse());
   3088 
   3089     connection = client.open(server.getUrl("/"));
   3090     connection.setDoOutput(true);
   3091     long contentLength = Integer.MAX_VALUE + 1L;
   3092     connection.setFixedLengthStreamingMode(contentLength);
   3093     OutputStream out = connection.getOutputStream();
   3094     byte[] buffer = new byte[1024 * 1024];
   3095     for (long bytesWritten = 0; bytesWritten < contentLength; ) {
   3096       int byteCount = (int) Math.min(buffer.length, contentLength - bytesWritten);
   3097       out.write(buffer, 0, byteCount);
   3098       bytesWritten += byteCount;
   3099     }
   3100     assertContent("", connection);
   3101 
   3102     RecordedRequest request = server.takeRequest();
   3103     assertEquals(Long.toString(contentLength), request.getHeader("Content-Length"));
   3104   }
   3105 
   3106   /**
   3107    * We had a bug where we attempted to gunzip responses that didn't have a
   3108    * body. This only came up with 304s since that response code can include
   3109    * headers (like "Content-Encoding") without any content to go along with it.
   3110    * https://github.com/square/okhttp/issues/358
   3111    */
   3112   @Test public void noTransparentGzipFor304NotModified() throws Exception {
   3113     server.enqueue(new MockResponse()
   3114         .clearHeaders()
   3115         .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED)
   3116         .addHeader("Content-Encoding: gzip"));
   3117     server.enqueue(new MockResponse().setBody("b"));
   3118 
   3119     HttpURLConnection connection1 = client.open(server.getUrl("/"));
   3120     assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, connection1.getResponseCode());
   3121     assertContent("", connection1);
   3122 
   3123     HttpURLConnection connection2 = client.open(server.getUrl("/"));
   3124     assertEquals(HttpURLConnection.HTTP_OK, connection2.getResponseCode());
   3125     assertContent("b", connection2);
   3126 
   3127     RecordedRequest requestA = server.takeRequest();
   3128     assertEquals(0, requestA.getSequenceNumber());
   3129 
   3130     RecordedRequest requestB = server.takeRequest();
   3131     assertEquals(1, requestB.getSequenceNumber());
   3132   }
   3133 
   3134   /**
   3135    * We had a bug where we weren't closing Gzip streams on redirects.
   3136    * https://github.com/square/okhttp/issues/441
   3137    */
   3138   @Test public void gzipWithRedirectAndConnectionReuse() throws Exception {
   3139     server.enqueue(new MockResponse()
   3140         .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
   3141         .addHeader("Location: /foo")
   3142         .addHeader("Content-Encoding: gzip")
   3143         .setBody(gzip("Moved! Moved! Moved!")));
   3144     server.enqueue(new MockResponse().setBody("This is the new page!"));
   3145 
   3146     HttpURLConnection connection = client.open(server.getUrl("/"));
   3147     assertContent("This is the new page!", connection);
   3148 
   3149     RecordedRequest requestA = server.takeRequest();
   3150     assertEquals(0, requestA.getSequenceNumber());
   3151 
   3152     RecordedRequest requestB = server.takeRequest();
   3153     assertEquals(1, requestB.getSequenceNumber());
   3154   }
   3155 
   3156   /**
   3157    * The RFC is unclear in this regard as it only specifies that this should
   3158    * invalidate the cache entry (if any).
   3159    */
   3160   @Test public void bodyPermittedOnDelete() throws Exception {
   3161     server.enqueue(new MockResponse());
   3162 
   3163     HttpURLConnection connection = client.open(server.getUrl("/"));
   3164     connection.setRequestMethod("DELETE");
   3165     connection.setDoOutput(true);
   3166     connection.getOutputStream().write("BODY".getBytes(UTF_8));
   3167     assertEquals(200, connection.getResponseCode());
   3168 
   3169     RecordedRequest request = server.takeRequest();
   3170     assertEquals("DELETE", request.getMethod());
   3171     assertEquals("BODY", request.getBody().readUtf8());
   3172   }
   3173 
   3174   @Test public void userAgentPicksUpHttpAgentSystemProperty() throws Exception {
   3175     server.enqueue(new MockResponse().setBody("abc"));
   3176 
   3177     System.setProperty("http.agent", "foo");
   3178     assertContent("abc", client.open(server.getUrl("/")));
   3179 
   3180     RecordedRequest request = server.takeRequest();
   3181     assertEquals("foo", request.getHeader("User-Agent"));
   3182   }
   3183 
   3184   /** https://github.com/square/okhttp/issues/891 */
   3185   @Test public void userAgentSystemPropertyIsNotAscii() throws Exception {
   3186     server.enqueue(new MockResponse().setBody("abc"));
   3187 
   3188     System.setProperty("http.agent", "a\nb\ud83c\udf69c\ud83c\udf68d\u007fe");
   3189     assertContent("abc", client.open(server.getUrl("/")));
   3190 
   3191     RecordedRequest request = server.takeRequest();
   3192     assertEquals("a?b?c?d?e", request.getHeader("User-Agent"));
   3193   }
   3194 
   3195   @Test public void userAgentDefaultsToOkHttpVersion() throws Exception {
   3196     server.enqueue(new MockResponse().setBody("abc"));
   3197 
   3198     assertContent("abc", client.open(server.getUrl("/")));
   3199 
   3200     RecordedRequest request = server.takeRequest();
   3201     assertEquals(Version.userAgent(), request.getHeader("User-Agent"));
   3202   }
   3203 
   3204   @Test public void interceptorsNotInvoked() throws Exception {
   3205     Interceptor interceptor = new Interceptor() {
   3206       @Override public Response intercept(Chain chain) throws IOException {
   3207         throw new AssertionError();
   3208       }
   3209     };
   3210     client.client().interceptors().add(interceptor);
   3211     client.client().networkInterceptors().add(interceptor);
   3212 
   3213     server.enqueue(new MockResponse().setBody("abc"));
   3214     assertContent("abc", client.open(server.getUrl("/")));
   3215   }
   3216 
   3217   @Test public void urlWithSpaceInHost() throws Exception {
   3218     URLConnection urlConnection = client.open(new URL("http://and roid.com/"));
   3219     try {
   3220       urlConnection.getInputStream();
   3221       fail();
   3222     } catch (UnknownHostException expected) {
   3223     }
   3224   }
   3225 
   3226   @Test public void urlWithSpaceInHostViaHttpProxy() throws Exception {
   3227     server.enqueue(new MockResponse());
   3228     URLConnection urlConnection =
   3229         client.open(new URL("http://and roid.com/"), server.toProxyAddress());
   3230 
   3231     try {
   3232       // This test is to check that a NullPointerException is not thrown.
   3233       urlConnection.getInputStream();
   3234       fail(); // the RI makes a bogus proxy request for "GET http://and roid.com/ HTTP/1.1"
   3235     } catch (UnknownHostException expected) {
   3236     }
   3237   }
   3238 
   3239   @Test public void urlHostWithNul() throws Exception {
   3240     URLConnection urlConnection = client.open(new URL("http://host\u0000/"));
   3241     try {
   3242       urlConnection.getInputStream();
   3243       fail();
   3244     } catch (UnknownHostException expected) {
   3245     }
   3246   }
   3247 
   3248   @Test public void urlRedirectToHostWithNul() throws Exception {
   3249     String redirectUrl = "http://host\u0000/";
   3250     server.enqueue(new MockResponse().setResponseCode(302)
   3251         .addHeaderLenient("Location", redirectUrl));
   3252 
   3253     HttpURLConnection urlConnection = client.open(server.getUrl("/"));
   3254     assertEquals(302, urlConnection.getResponseCode());
   3255     assertEquals(redirectUrl, urlConnection.getHeaderField("Location"));
   3256   }
   3257 
   3258   @Test public void urlWithBadAsciiHost() throws Exception {
   3259     URLConnection urlConnection = client.open(new URL("http://host\u0001/"));
   3260     try {
   3261       urlConnection.getInputStream();
   3262       fail();
   3263     } catch (UnknownHostException expected) {
   3264     }
   3265   }
   3266 
   3267   @Test public void instanceFollowsRedirects() throws Exception {
   3268     testInstanceFollowsRedirects("http://www.google.com/");
   3269     testInstanceFollowsRedirects("https://www.google.com/");
   3270   }
   3271 
   3272   private void testInstanceFollowsRedirects(String spec) throws Exception {
   3273     URL url = new URL(spec);
   3274     HttpURLConnection urlConnection = client.open(url);
   3275     urlConnection.setInstanceFollowRedirects(true);
   3276     assertTrue(urlConnection.getInstanceFollowRedirects());
   3277     urlConnection.setInstanceFollowRedirects(false);
   3278     assertFalse(urlConnection.getInstanceFollowRedirects());
   3279   }
   3280 
   3281   /** Returns a gzipped copy of {@code bytes}. */
   3282   public Buffer gzip(String data) throws IOException {
   3283     Buffer result = new Buffer();
   3284     BufferedSink gzipSink = Okio.buffer(new GzipSink(result));
   3285     gzipSink.writeUtf8(data);
   3286     gzipSink.close();
   3287     return result;
   3288   }
   3289 
   3290   /**
   3291    * Reads at most {@code limit} characters from {@code in} and asserts that
   3292    * content equals {@code expected}.
   3293    */
   3294   private void assertContent(String expected, HttpURLConnection connection, int limit)
   3295       throws IOException {
   3296     connection.connect();
   3297     assertEquals(expected, readAscii(connection.getInputStream(), limit));
   3298   }
   3299 
   3300   private void assertContent(String expected, HttpURLConnection connection) throws IOException {
   3301     assertContent(expected, connection, Integer.MAX_VALUE);
   3302   }
   3303 
   3304   private Set<String> newSet(String... elements) {
   3305     return new LinkedHashSet<>(Arrays.asList(elements));
   3306   }
   3307 
   3308   enum TransferKind {
   3309     CHUNKED() {
   3310       @Override void setBody(MockResponse response, Buffer content, int chunkSize)
   3311           throws IOException {
   3312         response.setChunkedBody(content, chunkSize);
   3313       }
   3314       @Override void setForRequest(HttpURLConnection connection, int contentLength) {
   3315         connection.setChunkedStreamingMode(5);
   3316       }
   3317     },
   3318     FIXED_LENGTH() {
   3319       @Override void setBody(MockResponse response, Buffer content, int chunkSize) {
   3320         response.setBody(content);
   3321       }
   3322       @Override void setForRequest(HttpURLConnection connection, int contentLength) {
   3323         connection.setFixedLengthStreamingMode(contentLength);
   3324       }
   3325     },
   3326     END_OF_STREAM() {
   3327       @Override void setBody(MockResponse response, Buffer content, int chunkSize) {
   3328         response.setBody(content);
   3329         response.setSocketPolicy(DISCONNECT_AT_END);
   3330         response.removeHeader("Content-Length");
   3331       }
   3332       @Override void setForRequest(HttpURLConnection connection, int contentLength) {
   3333       }
   3334     };
   3335 
   3336     abstract void setBody(MockResponse response, Buffer content, int chunkSize) throws IOException;
   3337 
   3338     abstract void setForRequest(HttpURLConnection connection, int contentLength);
   3339 
   3340     void setBody(MockResponse response, String content, int chunkSize) throws IOException {
   3341       setBody(response, new Buffer().writeUtf8(content), chunkSize);
   3342     }
   3343   }
   3344 
   3345   enum ProxyConfig {
   3346     NO_PROXY() {
   3347       @Override public HttpURLConnection connect(
   3348           MockWebServer server, OkUrlFactory streamHandlerFactory, URL url)
   3349           throws IOException {
   3350         streamHandlerFactory.client().setProxy(Proxy.NO_PROXY);
   3351         return streamHandlerFactory.open(url);
   3352       }
   3353     },
   3354 
   3355     CREATE_ARG() {
   3356       @Override public HttpURLConnection connect(
   3357           MockWebServer server, OkUrlFactory streamHandlerFactory, URL url)
   3358           throws IOException {
   3359         streamHandlerFactory.client().setProxy(server.toProxyAddress());
   3360         return streamHandlerFactory.open(url);
   3361       }
   3362     },
   3363 
   3364     PROXY_SYSTEM_PROPERTY() {
   3365       @Override public HttpURLConnection connect(
   3366           MockWebServer server, OkUrlFactory streamHandlerFactory, URL url)
   3367           throws IOException {
   3368         System.setProperty("proxyHost", server.getHostName());
   3369         System.setProperty("proxyPort", Integer.toString(server.getPort()));
   3370         return streamHandlerFactory.open(url);
   3371       }
   3372     },
   3373 
   3374     HTTP_PROXY_SYSTEM_PROPERTY() {
   3375       @Override public HttpURLConnection connect(
   3376           MockWebServer server, OkUrlFactory streamHandlerFactory, URL url)
   3377           throws IOException {
   3378         System.setProperty("http.proxyHost", server.getHostName());
   3379         System.setProperty("http.proxyPort", Integer.toString(server.getPort()));
   3380         return streamHandlerFactory.open(url);
   3381       }
   3382     },
   3383 
   3384     HTTPS_PROXY_SYSTEM_PROPERTY() {
   3385       @Override public HttpURLConnection connect(
   3386           MockWebServer server, OkUrlFactory streamHandlerFactory, URL url)
   3387           throws IOException {
   3388         System.setProperty("https.proxyHost", server.getHostName());
   3389         System.setProperty("https.proxyPort", Integer.toString(server.getPort()));
   3390         return streamHandlerFactory.open(url);
   3391       }
   3392     };
   3393 
   3394     public abstract HttpURLConnection connect(
   3395         MockWebServer server, OkUrlFactory streamHandlerFactory, URL url)
   3396         throws IOException;
   3397   }
   3398 
   3399   private static class RecordingTrustManager implements X509TrustManager {
   3400     private final List<String> calls = new ArrayList<String>();
   3401     private final X509TrustManager delegate;
   3402 
   3403     public RecordingTrustManager(SSLContext sslContext) {
   3404       this.delegate = Platform.get().trustManager(sslContext.getSocketFactory());
   3405     }
   3406 
   3407     public X509Certificate[] getAcceptedIssuers() {
   3408       return delegate.getAcceptedIssuers();
   3409     }
   3410 
   3411     public void checkClientTrusted(X509Certificate[] chain, String authType)
   3412         throws CertificateException {
   3413       calls.add("checkClientTrusted " + certificatesToString(chain));
   3414     }
   3415 
   3416     public void checkServerTrusted(X509Certificate[] chain, String authType)
   3417         throws CertificateException {
   3418       calls.add("checkServerTrusted " + certificatesToString(chain));
   3419     }
   3420 
   3421     private String certificatesToString(X509Certificate[] certificates) {
   3422       List<String> result = new ArrayList<String>();
   3423       for (X509Certificate certificate : certificates) {
   3424         result.add(certificate.getSubjectDN() + " " + certificate.getSerialNumber());
   3425       }
   3426       return result.toString();
   3427     }
   3428   }
   3429 
   3430   private static class FakeProxySelector extends ProxySelector {
   3431     List<Proxy> proxies = new ArrayList<>();
   3432 
   3433     @Override public List<Proxy> select(URI uri) {
   3434       // Don't handle 'socket' schemes, which the RI's Socket class may request (for SOCKS).
   3435       return uri.getScheme().equals("http") || uri.getScheme().equals("https") ? proxies
   3436           : Collections.singletonList(Proxy.NO_PROXY);
   3437     }
   3438 
   3439     @Override public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
   3440     }
   3441   }
   3442 
   3443   /**
   3444    * Tests that use this will fail unless boot classpath is set. Ex. {@code
   3445    * -Xbootclasspath/p:/tmp/alpn-boot-8.0.0.v20140317}
   3446    */
   3447   private void enableProtocol(Protocol protocol) {
   3448     client.client().setSslSocketFactory(sslContext.getSocketFactory());
   3449     client.client().setHostnameVerifier(new RecordingHostnameVerifier());
   3450     client.client().setProtocols(Arrays.asList(protocol, Protocol.HTTP_1_1));
   3451     server.useHttps(sslContext.getSocketFactory(), false);
   3452     server.setProtocolNegotiationEnabled(true);
   3453     server.setProtocols(client.client().getProtocols());
   3454   }
   3455 
   3456   /**
   3457    * Used during tests that involve TLS connection fallback attempts. OkHttp includes the
   3458    * TLS_FALLBACK_SCSV cipher on fallback connections. See
   3459    * {@link com.squareup.okhttp.FallbackTestClientSocketFactory} for details.
   3460    */
   3461   private void suppressTlsFallbackScsv(OkHttpClient client) {
   3462     FallbackTestClientSocketFactory clientSocketFactory =
   3463         new FallbackTestClientSocketFactory(sslContext.getSocketFactory());
   3464     client.setSslSocketFactory(clientSocketFactory);
   3465   }
   3466 }
   3467