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