1c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/*
2c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Copyright (C) 2009 The Android Open Source Project
3c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
4c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Licensed under the Apache License, Version 2.0 (the "License");
5c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * you may not use this file except in compliance with the License.
6c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * You may obtain a copy of the License at
7c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
8c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *      http://www.apache.org/licenses/LICENSE-2.0
9c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
10c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Unless required by applicable law or agreed to in writing, software
11c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS,
12c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * See the License for the specific language governing permissions and
14c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * limitations under the License.
15c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */
16c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
1771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerpackage com.squareup.okhttp;
1871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
19e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport com.squareup.okhttp.internal.Internal;
206c251e20f00c7574b217bd4351ac81666f574380Tobias Thiererimport com.squareup.okhttp.internal.Platform;
2154cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport com.squareup.okhttp.internal.RecordingAuthenticator;
22faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport com.squareup.okhttp.internal.RecordingOkAuthenticator;
236c251e20f00c7574b217bd4351ac81666f574380Tobias Thiererimport com.squareup.okhttp.internal.SingleInetAddressDns;
242231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.internal.SslContextBuilder;
25e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport com.squareup.okhttp.internal.Util;
2671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport com.squareup.okhttp.internal.Version;
27166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport com.squareup.okhttp.mockwebserver.MockResponse;
28166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport com.squareup.okhttp.mockwebserver.MockWebServer;
29166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport com.squareup.okhttp.mockwebserver.RecordedRequest;
30166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport com.squareup.okhttp.mockwebserver.SocketPolicy;
3171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport com.squareup.okhttp.testing.RecordingHostnameVerifier;
32c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.IOException;
33c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.InputStream;
34c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.OutputStream;
35c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.Authenticator;
36c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.ConnectException;
37c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.HttpRetryException;
38c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.HttpURLConnection;
39c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.InetAddress;
40c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.ProtocolException;
41c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.Proxy;
422231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.ProxySelector;
43e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.net.ServerSocket;
44a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colittiimport java.net.Socket;
452231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.SocketAddress;
46b5f9076b16fcc41c3dad31aecfdcfd962a7a1f75Neil Fullerimport java.net.SocketTimeoutException;
47c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URI;
48c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URL;
49c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URLConnection;
50c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.UnknownHostException;
516c251e20f00c7574b217bd4351ac81666f574380Tobias Thiererimport java.security.SecureRandom;
52c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.security.cert.CertificateException;
53c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.security.cert.X509Certificate;
54c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.ArrayList;
55c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Arrays;
56c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Collections;
57e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.EnumSet;
5871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport java.util.LinkedHashSet;
59c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.List;
60c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Map;
61faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport java.util.Random;
62c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Set;
6387ed7244fb53ae2bac9f23c033bbd5f23ac269f8Jesse Wilsonimport java.util.concurrent.TimeUnit;
64c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.zip.GZIPInputStream;
65e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport javax.net.ServerSocketFactory;
66a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colittiimport javax.net.SocketFactory;
677407d6984ce69693097befc9b72609a8156463bbNarayan Kamathimport javax.net.ssl.HttpsURLConnection;
68c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport javax.net.ssl.SSLContext;
69c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport javax.net.ssl.SSLException;
702231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport javax.net.ssl.SSLHandshakeException;
71c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport javax.net.ssl.SSLSocketFactory;
722231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport javax.net.ssl.TrustManager;
73c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport javax.net.ssl.X509TrustManager;
74e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Buffer;
75e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.BufferedSink;
76e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.GzipSink;
77e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Okio;
782231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport org.junit.After;
7954cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport org.junit.Before;
8054cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport org.junit.Ignore;
81e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport org.junit.Rule;
8254cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport org.junit.Test;
83e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport org.junit.rules.TemporaryFolder;
8454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.Util.UTF_8;
863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.http.OkHeaders.SELECTED_PROTOCOL;
87e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.http.StatusLine.HTTP_PERM_REDIRECT;
883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.http.StatusLine.HTTP_TEMP_REDIRECT;
89166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport static com.squareup.okhttp.mockwebserver.SocketPolicy.DISCONNECT_AT_END;
90166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport static com.squareup.okhttp.mockwebserver.SocketPolicy.DISCONNECT_AT_START;
91166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport static com.squareup.okhttp.mockwebserver.SocketPolicy.SHUTDOWN_INPUT_AT_END;
92166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport static com.squareup.okhttp.mockwebserver.SocketPolicy.SHUTDOWN_OUTPUT_AT_END;
933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static java.util.concurrent.TimeUnit.MILLISECONDS;
943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static java.util.concurrent.TimeUnit.NANOSECONDS;
952231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.assertEquals;
962231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.assertFalse;
973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.assertNotNull;
982231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.assertNull;
992231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.assertTrue;
1002231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport static org.junit.Assert.fail;
101c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
10254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson/** Android's URLConnectionTest. */
1032231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonpublic final class URLConnectionTest {
10471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Rule public final MockWebServer server = new MockWebServer();
10571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Rule public final MockWebServer server2 = new MockWebServer();
106e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Rule public final TemporaryFolder tempDir = new TemporaryFolder();
10754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
10871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  private SSLContext sslContext = SslContextBuilder.localhost();
109e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private OkUrlFactory client;
1103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private HttpURLConnection connection;
111e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private Cache cache;
11254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
11354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Before public void setUp() throws Exception {
11471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.setProtocolNegotiationEnabled(false);
115e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client = new OkUrlFactory(new OkHttpClient());
11654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
11754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
11854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @After public void tearDown() throws Exception {
119166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    Authenticator.setDefault(null);
12054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    System.clearProperty("proxyHost");
12154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    System.clearProperty("proxyPort");
122e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    System.clearProperty("http.agent");
12354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    System.clearProperty("http.proxyHost");
12454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    System.clearProperty("http.proxyPort");
12554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    System.clearProperty("https.proxyHost");
12654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    System.clearProperty("https.proxyPort");
12754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (cache != null) {
128faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      cache.delete();
12954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
13054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
13154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
13254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestHeaders() throws IOException, InterruptedException {
13354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse());
13454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
1363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.addRequestProperty("D", "e");
1373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.addRequestProperty("D", "f");
1383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("f", connection.getRequestProperty("D"));
1393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("f", connection.getRequestProperty("d"));
1403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Map<String, List<String>> requestHeaders = connection.getRequestProperties();
14171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals(newSet("e", "f"), new LinkedHashSet<>(requestHeaders.get("D")));
14271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals(newSet("e", "f"), new LinkedHashSet<>(requestHeaders.get("d")));
14354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
14454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      requestHeaders.put("G", Arrays.asList("h"));
14554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail("Modified an unmodifiable view.");
14654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (UnsupportedOperationException expected) {
14754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
14854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
14954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      requestHeaders.get("D").add("i");
15054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail("Modified an unmodifiable view.");
15154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (UnsupportedOperationException expected) {
15254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
15354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
1543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      connection.setRequestProperty(null, "j");
15554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
15654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (NullPointerException expected) {
15754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
15854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
1593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      connection.addRequestProperty(null, "k");
16054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
16154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (NullPointerException expected) {
16254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
1633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.setRequestProperty("NullValue", null);
1643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertNull(connection.getRequestProperty("NullValue"));
1653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.addRequestProperty("AnotherNullValue", null);
1663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertNull(connection.getRequestProperty("AnotherNullValue"));
16754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.getResponseCode();
16954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = server.takeRequest();
170e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(Arrays.asList("e", "f"), request.getHeaders().values("D"));
171e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(request.getHeader("NullValue"));
172e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(request.getHeader("AnotherNullValue"));
173e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(request.getHeader("G"));
174e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(request.getHeader("null"));
17554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
17654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
1773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      connection.addRequestProperty("N", "o");
17854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail("Set header after connect");
17954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IllegalStateException expected) {
18054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
18154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
1823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      connection.setRequestProperty("P", "q");
18354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail("Set header after connect");
18454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IllegalStateException expected) {
18554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
18654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
1873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      connection.getRequestProperties();
18854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
18954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IllegalStateException expected) {
19054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
19154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
19254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
19354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getRequestPropertyReturnsLastValue() throws Exception {
1943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
1953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.addRequestProperty("A", "value1");
1963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.addRequestProperty("A", "value2");
1973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("value2", connection.getRequestProperty("A"));
19854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
19954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
20054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseHeaders() throws IOException, InterruptedException {
20154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setStatus("HTTP/1.0 200 Fantastic")
20254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("A: c")
20354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("B: d")
20454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("A: e")
20554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setChunkedBody("ABCDE\nFGHIJ\nKLMNO\nPQR", 8));
20654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
2083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(200, connection.getResponseCode());
2093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("Fantastic", connection.getResponseMessage());
2103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("HTTP/1.0 200 Fantastic", connection.getHeaderField(null));
2113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Map<String, List<String>> responseHeaders = connection.getHeaderFields();
21254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(Arrays.asList("HTTP/1.0 200 Fantastic"), responseHeaders.get(null));
21371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals(newSet("c", "e"), new LinkedHashSet<>(responseHeaders.get("A")));
21471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals(newSet("c", "e"), new LinkedHashSet<>(responseHeaders.get("a")));
21554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
21654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      responseHeaders.put("N", Arrays.asList("o"));
21754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail("Modified an unmodifiable view.");
21854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (UnsupportedOperationException expected) {
21954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
22054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
22154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      responseHeaders.get("A").add("f");
22254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail("Modified an unmodifiable view.");
22354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (UnsupportedOperationException expected) {
22454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
2253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("A", connection.getHeaderFieldKey(0));
2263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("c", connection.getHeaderField(0));
2273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("B", connection.getHeaderFieldKey(1));
2283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("d", connection.getHeaderField(1));
2293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("A", connection.getHeaderFieldKey(2));
2303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("e", connection.getHeaderField(2));
2316c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    connection.getInputStream().close();
23254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
23354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
23454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void serverSendsInvalidResponseHeaders() throws Exception {
23554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setStatus("HTP/1.1 200 OK"));
23654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
23854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
2393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      connection.getResponseCode();
24054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
24154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException expected) {
24254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
24354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
24454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
24554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void serverSendsInvalidCodeTooLarge() throws Exception {
24654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setStatus("HTTP/1.1 2147483648 OK"));
24754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
24954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
2503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      connection.getResponseCode();
25154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
25254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException expected) {
25354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
25454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
25554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
25654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void serverSendsInvalidCodeNotANumber() throws Exception {
25754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setStatus("HTTP/1.1 00a OK"));
25854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
26054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
2613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      connection.getResponseCode();
26254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
26354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException expected) {
26454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
26554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
26654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
26754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void serverSendsUnnecessaryWhitespace() throws Exception {
26854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setStatus(" HTTP/1.1 2147483648 OK"));
26954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
27154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
2723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      connection.getResponseCode();
27354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
27454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException expected) {
27554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
27654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
27754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
27854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectRetriesUntilConnectedOrFailed() throws Exception {
27954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/foo");
28071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.shutdown();
28154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(url);
28354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
28454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.connect();
28554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
28654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException expected) {
28754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
28854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
28954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
29054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestBodySurvivesRetriesWithFixedLength() throws Exception {
29154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRequestBodySurvivesRetries(TransferKind.FIXED_LENGTH);
29254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
29354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
29454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestBodySurvivesRetriesWithChunkedStreaming() throws Exception {
29554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRequestBodySurvivesRetries(TransferKind.CHUNKED);
29654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
29754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
29854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void requestBodySurvivesRetriesWithBufferedBody() throws Exception {
29954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRequestBodySurvivesRetries(TransferKind.END_OF_STREAM);
30054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
30154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
30254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testRequestBodySurvivesRetries(TransferKind transferKind) throws Exception {
30354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("abc"));
30454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
30554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Use a misconfigured proxy to guarantee that the request is retried.
30654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    FakeProxySelector proxySelector = new FakeProxySelector();
30771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    proxySelector.proxies.add(server2.toProxyAddress());
308e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setProxySelector(proxySelector);
30971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server2.shutdown();
31054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
3113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/def"));
31254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setDoOutput(true);
31354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transferKind.setForRequest(connection, 4);
31454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.getOutputStream().write("body".getBytes("UTF-8"));
31554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("abc", connection);
31654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
317e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("body", server.takeRequest().getBody().readUtf8());
31854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
31954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
32054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getErrorStreamOnSuccessfulRequest() throws Exception {
32154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A"));
3223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
32354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNull(connection.getErrorStream());
3246c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    connection.getInputStream().close();
32554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
32654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
32754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getErrorStreamOnUnsuccessfulRequest() throws Exception {
32854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(404).setBody("A"));
3293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
33054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection.getErrorStream(), Integer.MAX_VALUE));
33154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
33254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
33354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // Check that if we don't read to the end of a response, the next request on the
33454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // recycled connection doesn't get the unread tail of the first request's response.
33554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // http://code.google.com/p/android/issues/detail?id=2939
33654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void bug2939() throws Exception {
33754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response = new MockResponse().setChunkedBody("ABCDE\nFGHIJ\nKLMNO\nPQR", 8);
33854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
33954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
34054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
34154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
3426c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    HttpURLConnection c1 = client.open(server.getUrl("/"));
3436c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    assertContent("ABCDE", c1, 5);
3446c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    HttpURLConnection c2 = client.open(server.getUrl("/"));
3456c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    assertContent("ABCDE", c2, 5);
3466c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer
3476c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    c1.getInputStream().close();
3486c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    c2.getInputStream().close();
34954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
35054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
35154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // Check that we recognize a few basic mime types by extension.
35254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // http://code.google.com/p/android/issues/detail?id=10100
35354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void bug10100() throws Exception {
35454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("image/jpeg", URLConnection.guessContentTypeFromName("someFile.jpg"));
35554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("application/pdf", URLConnection.guessContentTypeFromName("stuff.pdf"));
35654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
35754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
35854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectionsArePooled() throws Exception {
35954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response = new MockResponse().setBody("ABCDEFGHIJKLMNOPQR");
36054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
36154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
36254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
36354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
36454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
36554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("ABCDEFGHIJKLMNOPQR", client.open(server.getUrl("/foo")));
36654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, server.takeRequest().getSequenceNumber());
36754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("ABCDEFGHIJKLMNOPQR", client.open(server.getUrl("/bar?baz=quux")));
36854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, server.takeRequest().getSequenceNumber());
36954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("ABCDEFGHIJKLMNOPQR", client.open(server.getUrl("/z")));
37054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, server.takeRequest().getSequenceNumber());
37154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
37254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
37354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void chunkedConnectionsArePooled() throws Exception {
37454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response = new MockResponse().setChunkedBody("ABCDEFGHIJKLMNOPQR", 5);
37554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
37654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
37754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
37854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
37954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
38054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("ABCDEFGHIJKLMNOPQR", client.open(server.getUrl("/foo")));
38154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, server.takeRequest().getSequenceNumber());
38254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("ABCDEFGHIJKLMNOPQR", client.open(server.getUrl("/bar?baz=quux")));
38354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, server.takeRequest().getSequenceNumber());
38454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("ABCDEFGHIJKLMNOPQR", client.open(server.getUrl("/z")));
38554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(2, server.takeRequest().getSequenceNumber());
38654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
38754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
38854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void serverClosesSocket() throws Exception {
38954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testServerClosesOutput(DISCONNECT_AT_END);
39054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
39154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
39254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void serverShutdownInput() throws Exception {
39354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testServerClosesOutput(SHUTDOWN_INPUT_AT_END);
39454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
39554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
39654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void serverShutdownOutput() throws Exception {
39754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testServerClosesOutput(SHUTDOWN_OUTPUT_AT_END);
39854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
39954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
4003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void invalidHost() throws Exception {
4013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Note that 1234.1.1.1 is an invalid host in a URI, but URL isn't as strict.
4023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    URL url = new URL("http://1234.1.1.1/index.html");
4033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpURLConnection connection = client.open(url);
4043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    try {
4053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      connection.connect();
4063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      fail();
407ca342d6c3c54ae2417d4e61c925ba6f63b31a571jwilson    } catch (UnknownHostException expected) {
4083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
4093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
4103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
41154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testServerClosesOutput(SocketPolicy socketPolicy) throws Exception {
41254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("This connection won't pool properly")
41354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setSocketPolicy(socketPolicy));
41454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse responseAfter = new MockResponse().setBody("This comes after a busted connection");
41554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(responseAfter);
41654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(responseAfter); // Enqueue 2x because the broken connection may be reused.
41754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
41854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection1 = client.open(server.getUrl("/a"));
41954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.setReadTimeout(100);
42054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("This connection won't pool properly", connection1);
42154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, server.takeRequest().getSequenceNumber());
42254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection2 = client.open(server.getUrl("/b"));
42354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection2.setReadTimeout(100);
42454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("This comes after a busted connection", connection2);
42554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
42654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Check that a fresh connection was created, either immediately or after attempting reuse.
42754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest requestAfter = server.takeRequest();
42854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (server.getRequestCount() == 3) {
42954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      requestAfter = server.takeRequest(); // The failure consumed a response.
43054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
43154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // sequence number 0 means the HTTP socket connection was not reused
43254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, requestAfter.getSequenceNumber());
43354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
43454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
43554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  enum WriteKind {BYTE_BY_BYTE, SMALL_BUFFERS, LARGE_BUFFERS}
43654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
43754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void chunkedUpload_byteByByte() throws Exception {
43854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    doUpload(TransferKind.CHUNKED, WriteKind.BYTE_BY_BYTE);
43954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
44054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
44154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void chunkedUpload_smallBuffers() throws Exception {
44254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    doUpload(TransferKind.CHUNKED, WriteKind.SMALL_BUFFERS);
44354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
44454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
44554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void chunkedUpload_largeBuffers() throws Exception {
44654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    doUpload(TransferKind.CHUNKED, WriteKind.LARGE_BUFFERS);
44754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
44854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
44954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void fixedLengthUpload_byteByByte() throws Exception {
45054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    doUpload(TransferKind.FIXED_LENGTH, WriteKind.BYTE_BY_BYTE);
45154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
45254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
45354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void fixedLengthUpload_smallBuffers() throws Exception {
45454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    doUpload(TransferKind.FIXED_LENGTH, WriteKind.SMALL_BUFFERS);
45554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
45654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
45754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void fixedLengthUpload_largeBuffers() throws Exception {
45854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    doUpload(TransferKind.FIXED_LENGTH, WriteKind.LARGE_BUFFERS);
45954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
46054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
46154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void doUpload(TransferKind uploadKind, WriteKind writeKind) throws Exception {
46254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    int n = 512 * 1024;
46371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.setBodyLimit(0);
46454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse());
46554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
46654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection conn = client.open(server.getUrl("/"));
46754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    conn.setDoOutput(true);
46854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    conn.setRequestMethod("POST");
46954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (uploadKind == TransferKind.CHUNKED) {
47054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      conn.setChunkedStreamingMode(-1);
47154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
47254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      conn.setFixedLengthStreamingMode(n);
47354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
47454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    OutputStream out = conn.getOutputStream();
47554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (writeKind == WriteKind.BYTE_BY_BYTE) {
47654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      for (int i = 0; i < n; ++i) {
47754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        out.write('x');
47854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
47954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
48054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      byte[] buf = new byte[writeKind == WriteKind.SMALL_BUFFERS ? 256 : 64 * 1024];
48154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      Arrays.fill(buf, (byte) 'x');
48254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      for (int i = 0; i < n; i += buf.length) {
48354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        out.write(buf, 0, Math.min(buf.length, n - i));
48454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
48554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
48654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    out.close();
48754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(200, conn.getResponseCode());
48854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = server.takeRequest();
48954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(n, request.getBodySize());
49054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (uploadKind == TransferKind.CHUNKED) {
49154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertTrue(request.getChunkSizes().size() > 0);
49254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
49354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertTrue(request.getChunkSizes().isEmpty());
49454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
49554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
49654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
49754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getResponseCodeNoResponseBody() throws Exception {
49854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("abc: def"));
49954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
50054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
50154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection conn = client.open(url);
50254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    conn.setDoInput(false);
50354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("def", conn.getHeaderField("abc"));
50454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(200, conn.getResponseCode());
50554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
50654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      conn.getInputStream();
50754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
50854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (ProtocolException expected) {
50954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
51054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
51154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
51254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectViaHttps() throws Exception {
51371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), false);
51454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("this response comes via HTTPS"));
51554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
516e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSslSocketFactory(sslContext.getSocketFactory());
517e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(new RecordingHostnameVerifier());
5183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/foo"));
51954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
52054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("this response comes via HTTPS", connection);
52154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
52254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = server.takeRequest();
52354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET /foo HTTP/1.1", request.getRequestLine());
52454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
52554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
5263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void inspectHandshakeThroughoutRequestLifecycle() throws Exception {
52771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), false);
5283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse());
5293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
530e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSslSocketFactory(sslContext.getSocketFactory());
531e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(new RecordingHostnameVerifier());
5323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
5333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpsURLConnection httpsConnection = (HttpsURLConnection) client.open(server.getUrl("/foo"));
5343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
5353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Prior to calling connect(), getting the cipher suite is forbidden.
5363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    try {
5373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      httpsConnection.getCipherSuite();
5383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      fail();
5393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } catch (IllegalStateException expected) {
5403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
5413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
5423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Calling connect establishes a handshake...
5433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    httpsConnection.connect();
5443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertNotNull(httpsConnection.getCipherSuite());
5453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
5463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // ...which remains after we read the response body...
5473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertContent("", httpsConnection);
5483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertNotNull(httpsConnection.getCipherSuite());
5493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
5503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // ...and after we disconnect.
5513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    httpsConnection.disconnect();
5523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertNotNull(httpsConnection.getCipherSuite());
5533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
5543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
55554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectViaHttpsReusingConnections() throws IOException, InterruptedException {
55671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), false);
55754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("this response comes via HTTPS"));
55854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("another response via HTTPS"));
55954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
56054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // The pool will only reuse sockets if the SSL socket factories are the same.
56154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    SSLSocketFactory clientSocketFactory = sslContext.getSocketFactory();
56254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier();
56354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
564e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSslSocketFactory(clientSocketFactory);
565e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(hostnameVerifier);
5663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
56754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("this response comes via HTTPS", connection);
56854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
56954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection = client.open(server.getUrl("/"));
57054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("another response via HTTPS", connection);
57154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
57254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, server.takeRequest().getSequenceNumber());
57354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, server.takeRequest().getSequenceNumber());
57454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
57554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
57654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectViaHttpsReusingConnectionsDifferentFactories()
57754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throws IOException, InterruptedException {
57871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), false);
57954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("this response comes via HTTPS"));
58054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("another response via HTTPS"));
58154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
58254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // install a custom SSL socket factory so the server can be authorized
583e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSslSocketFactory(sslContext.getSocketFactory());
584e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(new RecordingHostnameVerifier());
58554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection1 = client.open(server.getUrl("/"));
58654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("this response comes via HTTPS", connection1);
58754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
588e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSslSocketFactory(null);
58954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection2 = client.open(server.getUrl("/"));
59054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
59154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      readAscii(connection2.getInputStream(), Integer.MAX_VALUE);
59254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail("without an SSL socket factory, the connection should fail");
59354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (SSLException expected) {
59454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
59554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
59654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
5977aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  @Test public void connectViaHttpsWithSSLFallback() throws Exception {
59871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), false);
59954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE));
60054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("this response comes via SSL"));
60154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
602e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    suppressTlsFallbackScsv(client.client());
603e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(new RecordingHostnameVerifier());
6043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/foo"));
60554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
60654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("this response comes via SSL", connection);
60754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
60854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = server.takeRequest();
60954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET /foo HTTP/1.1", request.getRequestLine());
6107aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertEquals(TlsVersion.TLS_1_0, request.getTlsVersion());
6117aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  }
6127aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller
6137aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  @Test public void connectViaHttpsWithSSLFallbackFailuresRecorded() throws Exception {
61471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), false);
6157aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE));
6167aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.FAIL_HANDSHAKE));
6177aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller
6187aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    suppressTlsFallbackScsv(client.client());
6196c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    client.client().setDns(new SingleInetAddressDns());
6207aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller
6217aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    client.client().setHostnameVerifier(new RecordingHostnameVerifier());
6227aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    connection = client.open(server.getUrl("/foo"));
6237aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller
6247aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    try {
6257aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller      connection.getResponseCode();
62671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      fail();
62771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    } catch (IOException expected) {
62871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      assertEquals(1, expected.getSuppressed().length);
6297aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    }
63054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
63154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
63254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
6333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * When a pooled connection fails, don't blame the route. Otherwise pooled
6343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * connection failures can cause unnecessary SSL fallbacks.
6353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   *
6363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * https://github.com/square/okhttp/issues/515
6373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
6383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void sslFallbackNotUsedWhenRecycledConnectionFails() throws Exception {
63971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), false);
6403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse()
6413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .setBody("abc")
6423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END));
6433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse().setBody("def"));
6443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
645e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    suppressTlsFallbackScsv(client.client());
646e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(new RecordingHostnameVerifier());
6473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
6483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertContent("abc", client.open(server.getUrl("/")));
6493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertContent("def", client.open(server.getUrl("/")));
6503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
651e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Set<TlsVersion> tlsVersions =
652e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        EnumSet.of(TlsVersion.TLS_1_0, TlsVersion.TLS_1_2); // v1.2 on OpenJDK 8.
653e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
6543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    RecordedRequest request1 = server.takeRequest();
655e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(tlsVersions.contains(request1.getTlsVersion()));
6563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
6573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    RecordedRequest request2 = server.takeRequest();
658e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(tlsVersions.contains(request2.getTlsVersion()));
6593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
6603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
6613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
66254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Verify that we don't retry connections on certificate verification errors.
66354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   *
66454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * http://code.google.com/p/android/issues/detail?id=13178
66554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
66654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectViaHttpsToUntrustedServer() throws IOException, InterruptedException {
66771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), false);
66854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse()); // unused
66954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
6703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/foo"));
67154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
67254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.getInputStream();
67354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
67454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (SSLHandshakeException expected) {
67554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertTrue(expected.getCause() instanceof CertificateException);
67654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
67754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, server.getRequestCount());
67854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
67954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
68054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectViaProxyUsingProxyArg() throws Exception {
68154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testConnectViaProxy(ProxyConfig.CREATE_ARG);
68254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
68354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
68454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectViaProxyUsingProxySystemProperty() throws Exception {
68554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testConnectViaProxy(ProxyConfig.PROXY_SYSTEM_PROPERTY);
68654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
68754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
68854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectViaProxyUsingHttpProxySystemProperty() throws Exception {
68954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testConnectViaProxy(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY);
69054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
69154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
69254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testConnectViaProxy(ProxyConfig proxyConfig) throws Exception {
69354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse mockResponse = new MockResponse().setBody("this response comes via a proxy");
69454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(mockResponse);
69554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
696078ce1ecb0917cbf08ab74325e6fe1fa6474c2a7Tobias Thierer    URL url = new URL("http://android.com/foo");
69771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    connection = proxyConfig.connect(server, client, url);
69854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("this response comes via a proxy", connection);
6993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertTrue(connection.usingProxy());
70054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
70171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    RecordedRequest request = server.takeRequest();
702078ce1ecb0917cbf08ab74325e6fe1fa6474c2a7Tobias Thierer    assertEquals("GET http://android.com/foo HTTP/1.1", request.getRequestLine());
703078ce1ecb0917cbf08ab74325e6fe1fa6474c2a7Tobias Thierer    assertEquals("android.com", request.getHeader("Host"));
70454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
70554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
70671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Test public void contentDisagreesWithContentLengthHeaderBodyTooLong() throws IOException {
70754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("abc\r\nYOU SHOULD NOT SEE THIS")
70854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .clearHeaders()
70954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Content-Length: 3"));
71054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("abc", client.open(server.getUrl("/")));
71154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
71254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
71371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Test public void contentDisagreesWithContentLengthHeaderBodyTooShort() throws IOException {
71471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.enqueue(new MockResponse().setBody("abc")
71571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .setHeader("Content-Length", "5")
71671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END));
71771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    try {
71871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      readAscii(client.open(server.getUrl("/")).getInputStream(), 5);
71971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      fail();
72071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    } catch (ProtocolException expected) {
72171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    }
72271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  }
72371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
724a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti  public void testConnectViaSocketFactory(boolean useHttps) throws IOException {
725a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti    SocketFactory uselessSocketFactory = new SocketFactory() {
726a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti      public Socket createSocket() { throw new IllegalArgumentException("useless"); }
727a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti      public Socket createSocket(InetAddress host, int port) { return null; }
728a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti      public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
729a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti          int localPort) { return null; }
730a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti      public Socket createSocket(String host, int port) { return null; }
731a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti      public Socket createSocket(String host, int port, InetAddress localHost, int localPort) {
732a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti        return null;
733a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti      }
734a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti    };
735a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti
736a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti    if (useHttps) {
73771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      server.useHttps(sslContext.getSocketFactory(), false);
738e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      client.client().setSslSocketFactory(sslContext.getSocketFactory());
739e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      client.client().setHostnameVerifier(new RecordingHostnameVerifier());
740a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti    }
741a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti
742a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti    server.enqueue(new MockResponse().setStatus("HTTP/1.1 200 OK"));
743a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti
744e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSocketFactory(uselessSocketFactory);
745a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti    connection = client.open(server.getUrl("/"));
746a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti    try {
747a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti      connection.getResponseCode();
748a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti      fail();
749a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti    } catch (IllegalArgumentException expected) {
750a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti    }
751a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti
752e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSocketFactory(SocketFactory.getDefault());
753a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti    connection = client.open(server.getUrl("/"));
754a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti    assertEquals(200, connection.getResponseCode());
755a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti  }
756a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti
757a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti  @Test public void connectHttpViaSocketFactory() throws Exception {
758a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti    testConnectViaSocketFactory(false);
759a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti  }
760a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti
761a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti  @Test public void connectHttpsViaSocketFactory() throws Exception {
762a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti    testConnectViaSocketFactory(true);
763a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti  }
764a83ddf194ffbae04ce536967efff0ec72df70e10Lorenzo Colitti
76571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Test public void contentDisagreesWithChunkedHeaderBodyTooLong() throws IOException {
76654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse mockResponse = new MockResponse();
76754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    mockResponse.setChunkedBody("abc", 3);
768e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer buffer = mockResponse.getBody();
769e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    buffer.writeUtf8("\r\nYOU SHOULD NOT SEE THIS");
770e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    mockResponse.setBody(buffer);
77154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    mockResponse.clearHeaders();
77254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    mockResponse.addHeader("Transfer-encoding: chunked");
77354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
77454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(mockResponse);
77554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
77654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("abc", client.open(server.getUrl("/")));
77754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
77854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
77971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Test public void contentDisagreesWithChunkedHeaderBodyTooShort() throws IOException {
78071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    MockResponse mockResponse = new MockResponse();
78171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    mockResponse.setChunkedBody("abcde", 5);
78271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
78371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Buffer truncatedBody = new Buffer();
78471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Buffer fullBody = mockResponse.getBody();
78571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    truncatedBody.write(fullBody, fullBody.indexOf((byte) 'e'));
78671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    mockResponse.setBody(truncatedBody);
78771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
78871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    mockResponse.clearHeaders();
78971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    mockResponse.addHeader("Transfer-encoding: chunked");
79071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    mockResponse.setSocketPolicy(SocketPolicy.DISCONNECT_AT_END);
79171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
79271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.enqueue(mockResponse);
79371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
79471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    try {
79571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      readAscii(client.open(server.getUrl("/")).getInputStream(), 5);
79671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      fail();
79771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    } catch (ProtocolException expected) {
79871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    }
79971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  }
80071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
80154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectViaHttpProxyToHttpsUsingProxyArgWithNoProxy() throws Exception {
80254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testConnectViaDirectProxyToHttps(ProxyConfig.NO_PROXY);
80354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
80454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
80554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectViaHttpProxyToHttpsUsingHttpProxySystemProperty() throws Exception {
80654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // https should not use http proxy
80754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testConnectViaDirectProxyToHttps(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY);
80854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
80954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
81054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testConnectViaDirectProxyToHttps(ProxyConfig proxyConfig) throws Exception {
81171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), false);
81254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("this response comes via HTTPS"));
81354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
81454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/foo");
815e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSslSocketFactory(sslContext.getSocketFactory());
816e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(new RecordingHostnameVerifier());
81771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    connection = proxyConfig.connect(server, client, url);
81854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
81954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("this response comes via HTTPS", connection);
82054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
82154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = server.takeRequest();
82254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET /foo HTTP/1.1", request.getRequestLine());
82354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
82454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
82554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectViaHttpProxyToHttpsUsingProxyArg() throws Exception {
82654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testConnectViaHttpProxyToHttps(ProxyConfig.CREATE_ARG);
82754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
82854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
82954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
83054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * We weren't honoring all of the appropriate proxy system properties when
83154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * connecting via HTTPS. http://b/3097518
83254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
83354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectViaHttpProxyToHttpsUsingProxySystemProperty() throws Exception {
83454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testConnectViaHttpProxyToHttps(ProxyConfig.PROXY_SYSTEM_PROPERTY);
83554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
83654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
83754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectViaHttpProxyToHttpsUsingHttpsProxySystemProperty() throws Exception {
83854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testConnectViaHttpProxyToHttps(ProxyConfig.HTTPS_PROXY_SYSTEM_PROPERTY);
83954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
84054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
84154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
84254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * We were verifying the wrong hostname when connecting to an HTTPS site
84354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * through a proxy. http://b/3097277
84454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
84554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testConnectViaHttpProxyToHttps(ProxyConfig proxyConfig) throws Exception {
84654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier();
84754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
84871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), true);
84954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
85054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END).clearHeaders());
85154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("this response comes via a secure proxy"));
85254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
85354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = new URL("https://android.com/foo");
854e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSslSocketFactory(sslContext.getSocketFactory());
855e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(hostnameVerifier);
85671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    connection = proxyConfig.connect(server, client, url);
85754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
85854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("this response comes via a secure proxy", connection);
85954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
86054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest connect = server.takeRequest();
86154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("Connect line failure on proxy", "CONNECT android.com:443 HTTP/1.1",
86254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        connect.getRequestLine());
863eaf55917fcd2f118e26d6b31dcaf9fb8f3ad0d1fjwilson    assertEquals("android.com:443", connect.getHeader("Host"));
86454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
86554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest get = server.takeRequest();
86654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET /foo HTTP/1.1", get.getRequestLine());
867e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("android.com", get.getHeader("Host"));
86854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls);
86954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
87054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
871e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** Tolerate bad https proxy response when using HttpResponseCache. Android bug 6754912. */
87254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectViaHttpProxyToHttpsUsingBadProxyAndHttpResponseCache() throws Exception {
87354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    initResponseCache();
87454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
87571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), true);
876e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // The inclusion of a body in the response to a CONNECT is key to reproducing b/6754912.
877e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    MockResponse badProxyResponse = new MockResponse()
87854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END)
87954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("bogus proxy connect response content");
880e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(badProxyResponse);
881e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setBody("response"));
88254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
883e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Configure a single IP address for the host and a single configuration, so we only need one
884e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // failure to fail permanently.
8856c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    client.client().setDns(new SingleInetAddressDns());
886e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSslSocketFactory(sslContext.getSocketFactory());
887e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setConnectionSpecs(Util.immutableList(ConnectionSpec.MODERN_TLS));
888e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(new RecordingHostnameVerifier());
88971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    client.client().setProxy(server.toProxyAddress());
89054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
89154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = new URL("https://android.com/foo");
8923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(url);
893e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertContent("response", connection);
89454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
89554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest connect = server.takeRequest();
896e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("CONNECT android.com:443 HTTP/1.1", connect.getRequestLine());
897eaf55917fcd2f118e26d6b31dcaf9fb8f3ad0d1fjwilson    assertEquals("android.com:443", connect.getHeader("Host"));
89854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
89954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
90054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void initResponseCache() throws IOException {
901e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache = new Cache(tempDir.getRoot(), Integer.MAX_VALUE);
902e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setCache(cache);
90354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
90454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
90554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Test which headers are sent unencrypted to the HTTP proxy. */
90654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void proxyConnectIncludesProxyHeadersOnly()
90754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throws IOException, InterruptedException {
90854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier();
90954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
91071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), true);
91154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
91254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END).clearHeaders());
91354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("encrypted response from the origin server"));
914e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
91571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    client.client().setProxy(server.toProxyAddress());
91654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
91754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = new URL("https://android.com/foo");
918e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSslSocketFactory(sslContext.getSocketFactory());
919e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(hostnameVerifier);
9203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(url);
92154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Private", "Secret");
92254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Proxy-Authorization", "bar");
92354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("User-Agent", "baz");
92454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("encrypted response from the origin server", connection);
92554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
92654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest connect = server.takeRequest();
927e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(connect.getHeader("Private"));
9286c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    assertNull(connect.getHeader("Proxy-Authorization"));
9296c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    assertEquals(Version.userAgent(), connect.getHeader("User-Agent"));
930eaf55917fcd2f118e26d6b31dcaf9fb8f3ad0d1fjwilson    assertEquals("android.com:443", connect.getHeader("Host"));
931e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("Keep-Alive", connect.getHeader("Proxy-Connection"));
93254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
93354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest get = server.takeRequest();
934e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("Secret", get.getHeader("Private"));
93554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls);
93654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
93754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
93854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void proxyAuthenticateOnConnect() throws Exception {
93954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Authenticator.setDefault(new RecordingAuthenticator());
94071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), true);
94154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(407)
94254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Proxy-Authenticate: Basic realm=\"localhost\""));
94354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
94454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END).clearHeaders());
94554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A"));
946e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
94771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    client.client().setProxy(server.toProxyAddress());
94854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
94954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = new URL("https://android.com/foo");
950e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSslSocketFactory(sslContext.getSocketFactory());
951e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(new RecordingHostnameVerifier());
9523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(url);
95354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("A", connection);
95454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
95554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest connect1 = server.takeRequest();
95654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("CONNECT android.com:443 HTTP/1.1", connect1.getRequestLine());
957e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(connect1.getHeader("Proxy-Authorization"));
95854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
95954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest connect2 = server.takeRequest();
96054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("CONNECT android.com:443 HTTP/1.1", connect2.getRequestLine());
961e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("Basic " + RecordingAuthenticator.BASE_64_CREDENTIALS,
962e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        connect2.getHeader("Proxy-Authorization"));
96354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
96454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest get = server.takeRequest();
96554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET /foo HTTP/1.1", get.getRequestLine());
966e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(get.getHeader("Proxy-Authorization"));
96754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
96854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
96954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // Don't disconnect after building a tunnel with CONNECT
97054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // http://code.google.com/p/android/issues/detail?id=37221
97154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void proxyWithConnectionClose() throws IOException {
97271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), true);
97354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
97454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END).clearHeaders());
97554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("this response comes via a proxy"));
976e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
97771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    client.client().setProxy(server.toProxyAddress());
97854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
97954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = new URL("https://android.com/foo");
980e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSslSocketFactory(sslContext.getSocketFactory());
981e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(new RecordingHostnameVerifier());
9823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(url);
98354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setRequestProperty("Connection", "close");
98454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
98554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("this response comes via a proxy", connection);
98654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
98754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
98854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void proxyWithConnectionReuse() throws IOException {
98954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    SSLSocketFactory socketFactory = sslContext.getSocketFactory();
99054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier();
99154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
99271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(socketFactory, true);
99354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(
99454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END).clearHeaders());
99554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("response 1"));
99654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("response 2"));
997e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
99871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    client.client().setProxy(server.toProxyAddress());
99954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
100054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = new URL("https://android.com/foo");
1001e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSslSocketFactory(socketFactory);
1002e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(hostnameVerifier);
100354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("response 1", client.open(url));
100454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("response 2", client.open(url));
100554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
100654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
100754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void disconnectedConnection() throws IOException {
100887ed7244fb53ae2bac9f23c033bbd5f23ac269f8Jesse Wilson    server.enqueue(new MockResponse()
100987ed7244fb53ae2bac9f23c033bbd5f23ac269f8Jesse Wilson        .throttleBody(2, 100, TimeUnit.MILLISECONDS)
101087ed7244fb53ae2bac9f23c033bbd5f23ac269f8Jesse Wilson        .setBody("ABCD"));
101154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
10123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
101354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream in = connection.getInputStream();
101454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals('A', (char) in.read());
101554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.disconnect();
101654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
101787ed7244fb53ae2bac9f23c033bbd5f23ac269f8Jesse Wilson      // Reading 'B' may succeed if it's buffered.
101887ed7244fb53ae2bac9f23c033bbd5f23ac269f8Jesse Wilson      in.read();
101987ed7244fb53ae2bac9f23c033bbd5f23ac269f8Jesse Wilson
102087ed7244fb53ae2bac9f23c033bbd5f23ac269f8Jesse Wilson      // But 'C' shouldn't be buffered (the response is throttled) and this should fail.
102154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      in.read();
102254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail("Expected a connection closed exception");
102354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException expected) {
102454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
10256c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    in.close();
102654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
102754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
102854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void disconnectBeforeConnect() throws IOException {
102954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A"));
103054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
10313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
103254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.disconnect();
103354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("A", connection);
103454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(200, connection.getResponseCode());
103554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
103654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
103754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @SuppressWarnings("deprecation") @Test public void defaultRequestProperty() throws Exception {
103854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection.setDefaultRequestProperty("X-testSetDefaultRequestProperty", "A");
103954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNull(URLConnection.getDefaultRequestProperty("X-setDefaultRequestProperty"));
104054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
104154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
104254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
104354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Reads {@code count} characters from the stream. If the stream is
104454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * exhausted before {@code count} characters can be read, the remaining
104554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * characters are returned and the stream is closed.
104654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
104754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private String readAscii(InputStream in, int count) throws IOException {
104854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    StringBuilder result = new StringBuilder();
104954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 0; i < count; i++) {
105054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      int value = in.read();
105154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (value == -1) {
1052c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        in.close();
105354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        break;
105454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
105554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      result.append((char) value);
105654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
105754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return result.toString();
105854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
105954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
106054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void markAndResetWithContentLengthHeader() throws IOException {
106154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testMarkAndReset(TransferKind.FIXED_LENGTH);
106254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
106354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
106454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void markAndResetWithChunkedEncoding() throws IOException {
106554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testMarkAndReset(TransferKind.CHUNKED);
106654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
106754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
106854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void markAndResetWithNoLengthHeaders() throws IOException {
106954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testMarkAndReset(TransferKind.END_OF_STREAM);
107054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
107154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
107254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testMarkAndReset(TransferKind transferKind) throws IOException {
107354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response = new MockResponse();
107454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transferKind.setBody(response, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1024);
107554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
107654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
107754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
107854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream in = client.open(server.getUrl("/")).getInputStream();
107954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertFalse("This implementation claims to support mark().", in.markSupported());
108054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    in.mark(5);
108154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABCDE", readAscii(in, 5));
108254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
108354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      in.reset();
108454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
108554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException expected) {
108654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
108754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("FGHIJKLMNOPQRSTUVWXYZ", readAscii(in, Integer.MAX_VALUE));
10886c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    in.close();
108954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("ABCDEFGHIJKLMNOPQRSTUVWXYZ", client.open(server.getUrl("/")));
109054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
109154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
109254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
109354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * We've had a bug where we forget the HTTP response when we see response
109454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * code 401. This causes a new HTTP request to be issued for every call into
109554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * the URLConnection.
109654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
109754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void unauthorizedResponseHandling() throws IOException {
109854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response = new MockResponse().addHeader("WWW-Authenticate: challenge")
109954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setResponseCode(401) // UNAUTHORIZED
110054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("Unauthorized");
110154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
110254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
110354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
110454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
110554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
110654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection conn = client.open(url);
110754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
110854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(401, conn.getResponseCode());
110954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(401, conn.getResponseCode());
111054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(401, conn.getResponseCode());
111154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, server.getRequestCount());
11126c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    conn.getErrorStream().close();
111354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
111454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
111554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void nonHexChunkSize() throws IOException {
111654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("5\r\nABCDE\r\nG\r\nFGHIJKLMNOPQRSTU\r\n0\r\n\r\n")
111754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .clearHeaders()
111854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Transfer-encoding: chunked"));
111954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
112054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = client.open(server.getUrl("/"));
112154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
112254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      readAscii(connection.getInputStream(), Integer.MAX_VALUE);
112354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
112454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException e) {
112554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
11266c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    connection.getInputStream().close();
112754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
112854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1129a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  @Test public void malformedChunkSize() throws IOException {
1130a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    server.enqueue(new MockResponse().setBody("5:x\r\nABCDE\r\n0\r\n\r\n")
1131a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        .clearHeaders()
1132a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        .addHeader("Transfer-encoding: chunked"));
1133a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
1134a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    URLConnection connection = client.open(server.getUrl("/"));
1135a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    try {
1136a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      readAscii(connection.getInputStream(), Integer.MAX_VALUE);
1137a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      fail();
1138a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    } catch (IOException e) {
11396c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    } finally {
11406c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer      connection.getInputStream().close();
1141a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    }
1142a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  }
1143a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
1144a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  @Test public void extensionAfterChunkSize() throws IOException {
1145a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    server.enqueue(new MockResponse().setBody("5;x\r\nABCDE\r\n0\r\n\r\n")
1146a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        .clearHeaders()
1147a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        .addHeader("Transfer-encoding: chunked"));
1148a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
1149a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    HttpURLConnection connection = client.open(server.getUrl("/"));
1150a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertContent("ABCDE", connection);
1151a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  }
1152a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
115354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void missingChunkBody() throws IOException {
115454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("5")
115554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .clearHeaders()
115654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Transfer-encoding: chunked")
115754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setSocketPolicy(DISCONNECT_AT_END));
115854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
115954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = client.open(server.getUrl("/"));
116054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
116154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      readAscii(connection.getInputStream(), Integer.MAX_VALUE);
116254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
116354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException e) {
11646c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    } finally {
11656c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer      connection.getInputStream().close();
116654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
116754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
116854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
116954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
117054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * This test checks whether connections are gzipped by default. This
117154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * behavior in not required by the API, so a failure of this test does not
117254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * imply a bug in the implementation.
117354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
117454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void gzipEncodingEnabledByDefault() throws IOException, InterruptedException {
1175e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
1176e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody(gzip("ABCABCABC"))
117754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Content-Encoding: gzip"));
117854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
117954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = client.open(server.getUrl("/"));
118054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABCABCABC", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
118154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertNull(connection.getContentEncoding());
118254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(-1, connection.getContentLength());
118354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
118454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = server.takeRequest();
1185e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("gzip", request.getHeader("Accept-Encoding"));
118654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
118754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
118854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientConfiguredGzipContentEncoding() throws Exception {
1189e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer bodyBytes = gzip("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
119054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse()
119154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody(bodyBytes)
119254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Content-Encoding: gzip"));
119354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
119454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = client.open(server.getUrl("/"));
119554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Accept-Encoding", "gzip");
119654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream gunzippedIn = new GZIPInputStream(connection.getInputStream());
119754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZ", readAscii(gunzippedIn, Integer.MAX_VALUE));
1198e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(bodyBytes.size(), connection.getContentLength());
119954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
120054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = server.takeRequest();
1201e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("gzip", request.getHeader("Accept-Encoding"));
120254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
120354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
120454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void gzipAndConnectionReuseWithFixedLength() throws Exception {
120554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.FIXED_LENGTH, false);
120654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
120754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
120854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void gzipAndConnectionReuseWithChunkedEncoding() throws Exception {
120954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.CHUNKED, false);
121054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
121154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
121254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void gzipAndConnectionReuseWithFixedLengthAndTls() throws Exception {
121354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.FIXED_LENGTH, true);
121454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
121554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
121654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void gzipAndConnectionReuseWithChunkedEncodingAndTls() throws Exception {
121754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.CHUNKED, true);
121854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
121954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
122054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientConfiguredCustomContentEncoding() throws Exception {
122154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("ABCDE").addHeader("Content-Encoding: custom"));
122254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
122354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = client.open(server.getUrl("/"));
122454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.addRequestProperty("Accept-Encoding", "custom");
122554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABCDE", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
122654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
122754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = server.takeRequest();
1228e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("custom", request.getHeader("Accept-Encoding"));
122954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
123054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
123154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
123254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Test a bug where gzip input streams weren't exhausting the input stream,
123354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * which corrupted the request that followed or prevented connection reuse.
123454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * http://code.google.com/p/android/issues/detail?id=7059
123554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * http://code.google.com/p/android/issues/detail?id=38817
123654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
123754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind transferKind,
123854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      boolean tls) throws Exception {
123954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (tls) {
124054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      SSLSocketFactory socketFactory = sslContext.getSocketFactory();
124154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier();
124271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      server.useHttps(socketFactory, false);
1243e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      client.client().setSslSocketFactory(socketFactory);
1244e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      client.client().setHostnameVerifier(hostnameVerifier);
124554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
124654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
124754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse responseOne = new MockResponse();
124854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    responseOne.addHeader("Content-Encoding: gzip");
1249e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    transferKind.setBody(responseOne, gzip("one (gzipped)"), 5);
125054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(responseOne);
125154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse responseTwo = new MockResponse();
125254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transferKind.setBody(responseTwo, "two (identity)", 5);
125354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(responseTwo);
125454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
125554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection1 = client.open(server.getUrl("/"));
125654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.addRequestProperty("Accept-Encoding", "gzip");
125754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream gunzippedIn = new GZIPInputStream(connection1.getInputStream());
125854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("one (gzipped)", readAscii(gunzippedIn, Integer.MAX_VALUE));
125954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, server.takeRequest().getSequenceNumber());
126054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
126154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection2 = client.open(server.getUrl("/"));
126254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("two (identity)", readAscii(connection2.getInputStream(), Integer.MAX_VALUE));
126354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, server.takeRequest().getSequenceNumber());
126454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
126554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1266c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller  @Test public void transparentGzipWorksAfterExceptionRecovery() throws Exception {
1267c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    server.enqueue(new MockResponse()
1268c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller        .setBody("a")
1269c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller        .setSocketPolicy(SHUTDOWN_INPUT_AT_END));
1270c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    server.enqueue(new MockResponse()
1271c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller        .addHeader("Content-Encoding: gzip")
1272e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody(gzip("b")));
1273c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller
1274c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    // Seed the pool with a bad connection.
1275c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    assertContent("a", client.open(server.getUrl("/")));
1276c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller
1277c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    // This connection will need to be recovered. When it is, transparent gzip should still work!
1278c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    assertContent("b", client.open(server.getUrl("/")));
1279c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller
1280c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    assertEquals(0, server.takeRequest().getSequenceNumber());
1281c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    assertEquals(0, server.takeRequest().getSequenceNumber()); // Connection is not pooled.
1282c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller  }
1283c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller
1284c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller  @Test public void endOfStreamResponseIsNotPooled() throws Exception {
1285c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    server.enqueue(new MockResponse()
1286c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller        .setBody("{}")
1287c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller        .clearHeaders()
1288c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller        .setSocketPolicy(DISCONNECT_AT_END));
1289c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller
1290c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    ConnectionPool pool = ConnectionPool.getDefault();
1291c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    pool.evictAll();
1292e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setConnectionPool(pool);
1293c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller
1294c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    HttpURLConnection connection = client.open(server.getUrl("/"));
1295c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    assertContent("{}", connection);
12966c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    assertEquals(0, client.client().getConnectionPool().getIdleConnectionCount());
1297c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller  }
1298c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller
129954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void earlyDisconnectDoesntHarmPoolingWithChunkedEncoding() throws Exception {
130054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testEarlyDisconnectDoesntHarmPooling(TransferKind.CHUNKED);
130154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
130254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
130354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void earlyDisconnectDoesntHarmPoolingWithFixedLengthEncoding() throws Exception {
130454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testEarlyDisconnectDoesntHarmPooling(TransferKind.FIXED_LENGTH);
130554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
130654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
130754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testEarlyDisconnectDoesntHarmPooling(TransferKind transferKind) throws Exception {
130854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response1 = new MockResponse();
130954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transferKind.setBody(response1, "ABCDEFGHIJK", 1024);
131054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response1);
131154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
131254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response2 = new MockResponse();
131354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transferKind.setBody(response2, "LMNOPQRSTUV", 1024);
131454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response2);
131554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
13163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpURLConnection connection1 = client.open(server.getUrl("/"));
131754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream in1 = connection1.getInputStream();
131854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABCDE", readAscii(in1, 5));
131987ed7244fb53ae2bac9f23c033bbd5f23ac269f8Jesse Wilson    in1.close();
13203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection1.disconnect();
132154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
132254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection connection2 = client.open(server.getUrl("/"));
132354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream in2 = connection2.getInputStream();
132454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("LMNOP", readAscii(in2, 5));
132587ed7244fb53ae2bac9f23c033bbd5f23ac269f8Jesse Wilson    in2.close();
13263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection2.disconnect();
132754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
132854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, server.takeRequest().getSequenceNumber());
132954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, server.takeRequest().getSequenceNumber()); // Connection is pooled!
133054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
133154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
13323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void streamDiscardingIsTimely() throws Exception {
13333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // This response takes at least a full second to serve: 10,000 bytes served 100 bytes at a time.
13343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse()
1335e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody(new Buffer().write(new byte[10000]))
13363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .throttleBody(100, 10, MILLISECONDS));
13373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse().setBody("A"));
13383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
13393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    long startNanos = System.nanoTime();
13403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    URLConnection connection1 = client.open(server.getUrl("/"));
13413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    InputStream in = connection1.getInputStream();
13423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    in.close();
13433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    long elapsedNanos = System.nanoTime() - startNanos;
13443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    long elapsedMillis = NANOSECONDS.toMillis(elapsedNanos);
13453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
13463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // If we're working correctly, this should be greater than 100ms, but less than double that.
13473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Previously we had a bug where we would download the entire response body as long as no
13483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // individual read took longer than 100ms.
13493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertTrue(String.format("Time to close: %sms", elapsedMillis), elapsedMillis < 500);
13503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
13513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Do another request to confirm that the discarded connection was not pooled.
13523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertContent("A", client.open(server.getUrl("/")));
13533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
13543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(0, server.takeRequest().getSequenceNumber());
13553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(0, server.takeRequest().getSequenceNumber()); // Connection is not pooled.
13563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
13573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
135854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void setChunkedStreamingMode() throws IOException, InterruptedException {
135954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse());
136054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
13613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String body = "ABCDEFGHIJKLMNOPQ";
13623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
13633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.setChunkedStreamingMode(0); // OkHttp does not honor specific chunk sizes.
13643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.setDoOutput(true);
13653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    OutputStream outputStream = connection.getOutputStream();
13663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    outputStream.write(body.getBytes("US-ASCII"));
13673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(200, connection.getResponseCode());
13686c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    connection.getInputStream().close();
136954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
137054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = server.takeRequest();
1371e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(body, request.getBody().readUtf8());
13723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(Arrays.asList(body.length()), request.getChunkSizes());
137354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
137454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
137554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void authenticateWithFixedLengthStreaming() throws Exception {
137654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testAuthenticateWithStreamingPost(StreamingMode.FIXED_LENGTH);
137754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
137854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
137954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void authenticateWithChunkedStreaming() throws Exception {
138054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testAuthenticateWithStreamingPost(StreamingMode.CHUNKED);
138154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
138254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
138354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testAuthenticateWithStreamingPost(StreamingMode streamingMode) throws Exception {
138454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse pleaseAuthenticate = new MockResponse().setResponseCode(401)
138554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("WWW-Authenticate: Basic realm=\"protected area\"")
138654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("Please authenticate.");
138754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(pleaseAuthenticate);
138854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
138954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Authenticator.setDefault(new RecordingAuthenticator());
13903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
139154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setDoOutput(true);
139254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    byte[] requestBody = { 'A', 'B', 'C', 'D' };
139354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (streamingMode == StreamingMode.FIXED_LENGTH) {
139454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.setFixedLengthStreamingMode(requestBody.length);
139554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else if (streamingMode == StreamingMode.CHUNKED) {
139654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.setChunkedStreamingMode(0);
139754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
139854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    OutputStream outputStream = connection.getOutputStream();
139954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    outputStream.write(requestBody);
140054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    outputStream.close();
140154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
140254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.getInputStream();
140354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
140454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (HttpRetryException expected) {
140554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
140654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
140754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // no authorization header for the request...
140854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = server.takeRequest();
1409e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(request.getHeader("Authorization"));
1410e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("ABCD", request.getBody().readUtf8());
1411e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1412e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1413e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void postBodyRetransmittedAfterAuthorizationFail() throws Exception {
1414e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    postBodyRetransmittedAfterAuthorizationFail("abc");
1415e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1416e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1417e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void postBodyRetransmittedAfterAuthorizationFail_SPDY_3() throws Exception {
1418e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    enableProtocol(Protocol.SPDY_3);
1419e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    postBodyRetransmittedAfterAuthorizationFail("abc");
1420e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1421e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1422e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void postBodyRetransmittedAfterAuthorizationFail_HTTP_2() throws Exception {
1423e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    enableProtocol(Protocol.HTTP_2);
1424e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    postBodyRetransmittedAfterAuthorizationFail("abc");
1425e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1426e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1427e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** Don't explode when resending an empty post. https://github.com/square/okhttp/issues/1131 */
1428e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail() throws Exception {
1429e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    postBodyRetransmittedAfterAuthorizationFail("");
1430e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1431e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1432e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail_SPDY_3() throws Exception {
1433e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    enableProtocol(Protocol.SPDY_3);
1434e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    postBodyRetransmittedAfterAuthorizationFail("");
1435e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1436e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1437e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void postEmptyBodyRetransmittedAfterAuthorizationFail_HTTP_2() throws Exception {
1438e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    enableProtocol(Protocol.HTTP_2);
1439e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    postBodyRetransmittedAfterAuthorizationFail("");
1440e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1441e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1442e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void postBodyRetransmittedAfterAuthorizationFail(String body) throws Exception {
1443e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setResponseCode(401));
1444e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse());
1445e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1446e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String credential = Credentials.basic("jesse", "secret");
1447e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setAuthenticator(new RecordingOkAuthenticator(credential));
1448e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1449e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    connection = client.open(server.getUrl("/"));
1450e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    connection.setDoOutput(true);
1451e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    OutputStream outputStream = connection.getOutputStream();
1452e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    outputStream.write(body.getBytes("UTF-8"));
1453e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    outputStream.close();
1454e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(200, connection.getResponseCode());
1455e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1456e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest recordedRequest1 = server.takeRequest();
1457e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("POST", recordedRequest1.getMethod());
1458e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(body, recordedRequest1.getBody().readUtf8());
1459e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(recordedRequest1.getHeader("Authorization"));
1460e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1461e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest recordedRequest2 = server.takeRequest();
1462e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("POST", recordedRequest2.getMethod());
1463e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(body, recordedRequest2.getBody().readUtf8());
1464e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(credential, recordedRequest2.getHeader("Authorization"));
146554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
146654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
146754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void nonStandardAuthenticationScheme() throws Exception {
146854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> calls = authCallsForHeader("WWW-Authenticate: Foo");
146954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(Collections.<String>emptyList(), calls);
147054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
147154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
147254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void nonStandardAuthenticationSchemeWithRealm() throws Exception {
147354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> calls = authCallsForHeader("WWW-Authenticate: Foo realm=\"Bar\"");
1474166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    assertEquals(0, calls.size());
147554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
147654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
147754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // Digest auth is currently unsupported. Test that digest requests should fail reasonably.
147854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // http://code.google.com/p/android/issues/detail?id=11140
147954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void digestAuthentication() throws Exception {
148054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> calls = authCallsForHeader("WWW-Authenticate: Digest "
148154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        + "realm=\"testrealm@host.com\", qop=\"auth,auth-int\", "
148254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        + "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", "
148354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        + "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"");
1484166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    assertEquals(0, calls.size());
148554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
148654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
148754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void allAttributesSetInServerAuthenticationCallbacks() throws Exception {
148854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> calls = authCallsForHeader("WWW-Authenticate: Basic realm=\"Bar\"");
148954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, calls.size());
149054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
149154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String call = calls.get(0);
149254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(call, call.contains("host=" + url.getHost()));
149354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(call, call.contains("port=" + url.getPort()));
1494e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(call, call.contains("site=" + url.getHost()));
149554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(call, call.contains("url=" + url));
149654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(call, call.contains("type=" + Authenticator.RequestorType.SERVER));
149754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(call, call.contains("prompt=Bar"));
149854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(call, call.contains("protocol=http"));
149954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(call, call.toLowerCase().contains("scheme=basic")); // lowercase for the RI.
150054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
150154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
150254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void allAttributesSetInProxyAuthenticationCallbacks() throws Exception {
150354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    List<String> calls = authCallsForHeader("Proxy-Authenticate: Basic realm=\"Bar\"");
150454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, calls.size());
150554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
150654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String call = calls.get(0);
150754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(call, call.contains("host=" + url.getHost()));
150854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(call, call.contains("port=" + url.getPort()));
1509e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(call, call.contains("site=" + url.getHost()));
151054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(call, call.contains("url=http://android.com"));
151154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(call, call.contains("type=" + Authenticator.RequestorType.PROXY));
151254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(call, call.contains("prompt=Bar"));
151354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(call, call.contains("protocol=http"));
151454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(call, call.toLowerCase().contains("scheme=basic")); // lowercase for the RI.
151554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
151654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
151754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private List<String> authCallsForHeader(String authHeader) throws IOException {
151854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    boolean proxy = authHeader.startsWith("Proxy-");
151954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    int responseCode = proxy ? 407 : 401;
152054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordingAuthenticator authenticator = new RecordingAuthenticator(null);
152154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Authenticator.setDefault(authenticator);
15226c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    MockResponse pleaseAuthenticate = new MockResponse()
15236c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer        .setResponseCode(responseCode)
152454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader(authHeader)
152554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("Please authenticate.");
152654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(pleaseAuthenticate);
152754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
152854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (proxy) {
152971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      client.client().setProxy(server.toProxyAddress());
153054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection = client.open(new URL("http://android.com"));
153154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
153254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection = client.open(server.getUrl("/"));
153354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
153454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(responseCode, connection.getResponseCode());
15356c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    connection.getErrorStream().close();
153654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return authenticator.calls;
153754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
153854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
153954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void setValidRequestMethod() throws Exception {
154054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertValidRequestMethod("GET");
154154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertValidRequestMethod("DELETE");
154254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertValidRequestMethod("HEAD");
154354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertValidRequestMethod("OPTIONS");
154454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertValidRequestMethod("POST");
154554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertValidRequestMethod("PUT");
154654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertValidRequestMethod("TRACE");
15473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertValidRequestMethod("PATCH");
154854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
154954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
155054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void assertValidRequestMethod(String requestMethod) throws Exception {
15513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
155254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setRequestMethod(requestMethod);
155354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(requestMethod, connection.getRequestMethod());
155454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
155554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
155654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void setInvalidRequestMethodLowercase() throws Exception {
155754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertInvalidRequestMethod("get");
155854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
155954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
156054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void setInvalidRequestMethodConnect() throws Exception {
156154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertInvalidRequestMethod("CONNECT");
156254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
156354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
156454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void assertInvalidRequestMethod(String requestMethod) throws Exception {
15653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
156654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
156754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.setRequestMethod(requestMethod);
156854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
156954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (ProtocolException expected) {
157054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
157154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
157254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
15733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void shoutcast() throws Exception {
15743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse().setStatus("ICY 200 OK")
15753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        // .addHeader("HTTP/1.0 200 OK")
15763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("Accept-Ranges: none")
15773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("Content-Type: audio/mpeg")
15783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("icy-br:128")
15793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("ice-audio-info: bitrate=128;samplerate=44100;channels=2")
15803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("icy-br:128")
15813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("icy-description:Rock")
15823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("icy-genre:riders")
15833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("icy-name:A2RRock")
15843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("icy-pub:1")
15853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("icy-url:http://www.A2Rradio.com")
15863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("Server: Icecast 2.3.3-kh8")
15873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("Cache-Control: no-cache")
15883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("Pragma: no-cache")
15893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("Expires: Mon, 26 Jul 1997 05:00:00 GMT")
15903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("icy-metaint:16000")
15913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .setBody("mp3 data"));
15923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
15933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(200, connection.getResponseCode());
15943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("OK", connection.getResponseMessage());
15953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertContent("mp3 data", connection);
15963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
15973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
159854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cannotSetNegativeFixedLengthStreamingMode() throws Exception {
15993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
160054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
160154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.setFixedLengthStreamingMode(-2);
160254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
160354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IllegalArgumentException expected) {
160454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
160554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
160654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
160754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void canSetNegativeChunkedStreamingMode() throws Exception {
16083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
160954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setChunkedStreamingMode(-2);
161054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
161154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
161254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cannotSetFixedLengthStreamingModeAfterConnect() throws Exception {
161354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A"));
16143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
161554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
161654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
161754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.setFixedLengthStreamingMode(1);
161854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
161954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IllegalStateException expected) {
162054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
162154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
162254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
162354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cannotSetChunkedStreamingModeAfterConnect() throws Exception {
162454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A"));
16253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
162654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
162754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
162854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.setChunkedStreamingMode(1);
162954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
163054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IllegalStateException expected) {
163154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
163254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
163354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
163454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cannotSetFixedLengthStreamingModeAfterChunkedStreamingMode() throws Exception {
16353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
163654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setChunkedStreamingMode(1);
163754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
163854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.setFixedLengthStreamingMode(1);
163954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
164054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IllegalStateException expected) {
164154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
164254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
164354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
164454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void cannotSetChunkedStreamingModeAfterFixedLengthStreamingMode() throws Exception {
16453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
164654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setFixedLengthStreamingMode(1);
164754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
164854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.setChunkedStreamingMode(1);
164954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
165054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IllegalStateException expected) {
165154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
165254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
165354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
165454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void secureFixedLengthStreaming() throws Exception {
165554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testSecureStreamingPost(StreamingMode.FIXED_LENGTH);
165654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
165754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
165854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void secureChunkedStreaming() throws Exception {
165954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testSecureStreamingPost(StreamingMode.CHUNKED);
166054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
166154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
166254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
166354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Users have reported problems using HTTPS with streaming request bodies.
166454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * http://code.google.com/p/android/issues/detail?id=12860
166554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
166654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testSecureStreamingPost(StreamingMode streamingMode) throws Exception {
166771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), false);
166854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("Success!"));
166954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1670e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSslSocketFactory(sslContext.getSocketFactory());
1671e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(new RecordingHostnameVerifier());
16723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
167354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setDoOutput(true);
167454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    byte[] requestBody = { 'A', 'B', 'C', 'D' };
167554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (streamingMode == StreamingMode.FIXED_LENGTH) {
167654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.setFixedLengthStreamingMode(requestBody.length);
167754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else if (streamingMode == StreamingMode.CHUNKED) {
167854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.setChunkedStreamingMode(0);
167954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
168054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    OutputStream outputStream = connection.getOutputStream();
168154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    outputStream.write(requestBody);
168254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    outputStream.close();
168354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("Success!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
168454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
168554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = server.takeRequest();
168654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("POST / HTTP/1.1", request.getRequestLine());
168754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (streamingMode == StreamingMode.FIXED_LENGTH) {
168854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertEquals(Collections.<Integer>emptyList(), request.getChunkSizes());
168954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else if (streamingMode == StreamingMode.CHUNKED) {
169054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertEquals(Arrays.asList(4), request.getChunkSizes());
169154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
1692e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("ABCD", request.getBody().readUtf8());
169354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
169454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
169554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  enum StreamingMode {
169654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    FIXED_LENGTH, CHUNKED
169754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
169854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
169954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void authenticateWithPost() throws Exception {
170054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse pleaseAuthenticate = new MockResponse().setResponseCode(401)
170154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("WWW-Authenticate: Basic realm=\"protected area\"")
170254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("Please authenticate.");
170354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // fail auth three times...
170454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(pleaseAuthenticate);
170554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(pleaseAuthenticate);
170654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(pleaseAuthenticate);
170754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // ...then succeed the fourth time
170854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("Successful auth!"));
170954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
171054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Authenticator.setDefault(new RecordingAuthenticator());
17113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
171254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setDoOutput(true);
171354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    byte[] requestBody = { 'A', 'B', 'C', 'D' };
171454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    OutputStream outputStream = connection.getOutputStream();
171554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    outputStream.write(requestBody);
171654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    outputStream.close();
171754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
171854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
171954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // no authorization header for the first request...
172054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = server.takeRequest();
1721e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(request.getHeader("Authorization"));
172254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
172354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // ...but the three requests that follow include an authorization header
172454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 0; i < 3; i++) {
172554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      request = server.takeRequest();
172654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertEquals("POST / HTTP/1.1", request.getRequestLine());
1727e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("Basic " + RecordingAuthenticator.BASE_64_CREDENTIALS,
1728e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          request.getHeader("Authorization"));
1729e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("ABCD", request.getBody().readUtf8());
173054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
173154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
173254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
173354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void authenticateWithGet() throws Exception {
173454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse pleaseAuthenticate = new MockResponse().setResponseCode(401)
173554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("WWW-Authenticate: Basic realm=\"protected area\"")
173654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("Please authenticate.");
173754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // fail auth three times...
173854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(pleaseAuthenticate);
173954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(pleaseAuthenticate);
174054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(pleaseAuthenticate);
174154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // ...then succeed the fourth time
174254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("Successful auth!"));
174354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
174454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Authenticator.setDefault(new RecordingAuthenticator());
17453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
174654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
174754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
174854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // no authorization header for the first request...
174954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = server.takeRequest();
1750e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(request.getHeader("Authorization"));
175154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
175254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // ...but the three requests that follow requests include an authorization header
175354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 0; i < 3; i++) {
175454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      request = server.takeRequest();
175554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertEquals("GET / HTTP/1.1", request.getRequestLine());
1756e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("Basic " + RecordingAuthenticator.BASE_64_CREDENTIALS,
1757e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          request.getHeader("Authorization"));
175854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
175954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
176054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1761f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller  /** https://code.google.com/p/android/issues/detail?id=74026 */
1762f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller  @Test public void authenticateWithGetAndTransparentGzip() throws Exception {
1763f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller    MockResponse pleaseAuthenticate = new MockResponse().setResponseCode(401)
1764f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller        .addHeader("WWW-Authenticate: Basic realm=\"protected area\"")
1765f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller        .setBody("Please authenticate.");
1766f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller    // fail auth three times...
1767f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller    server.enqueue(pleaseAuthenticate);
1768f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller    server.enqueue(pleaseAuthenticate);
1769f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller    server.enqueue(pleaseAuthenticate);
1770f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller    // ...then succeed the fourth time
1771f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller    MockResponse successfulResponse = new MockResponse()
1772f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller        .addHeader("Content-Encoding", "gzip")
1773e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody(gzip("Successful auth!"));
1774f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller    server.enqueue(successfulResponse);
1775f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller
1776f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller    Authenticator.setDefault(new RecordingAuthenticator());
1777f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller    connection = client.open(server.getUrl("/"));
1778f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller    assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
1779f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller
1780f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller    // no authorization header for the first request...
1781f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller    RecordedRequest request = server.takeRequest();
1782e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(request.getHeader("Authorization"));
1783f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller
1784f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller    // ...but the three requests that follow requests include an authorization header
1785f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller    for (int i = 0; i < 3; i++) {
1786f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller      request = server.takeRequest();
1787f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller      assertEquals("GET / HTTP/1.1", request.getRequestLine());
1788e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("Basic " + RecordingAuthenticator.BASE_64_CREDENTIALS,
1789e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          request.getHeader("Authorization"));
1790f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller    }
1791f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller  }
1792f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller
1793166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath  /** https://github.com/square/okhttp/issues/342 */
1794166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath  @Test public void authenticateRealmUppercase() throws Exception {
1795166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    server.enqueue(new MockResponse().setResponseCode(401)
1796166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        .addHeader("wWw-aUtHeNtIcAtE: bAsIc rEaLm=\"pRoTeCtEd aReA\"")
1797166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        .setBody("Please authenticate."));
1798166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    server.enqueue(new MockResponse().setBody("Successful auth!"));
1799166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
1800166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    Authenticator.setDefault(new RecordingAuthenticator());
18013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
1802166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
1803166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath  }
1804166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
180554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void redirectedWithChunkedEncoding() throws Exception {
180654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRedirected(TransferKind.CHUNKED, true);
180754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
180854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
180954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void redirectedWithContentLengthHeader() throws Exception {
181054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRedirected(TransferKind.FIXED_LENGTH, true);
181154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
181254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
181354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void redirectedWithNoLengthHeaders() throws Exception {
181454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testRedirected(TransferKind.END_OF_STREAM, false);
181554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
181654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
181754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testRedirected(TransferKind transferKind, boolean reuse) throws Exception {
181854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response = new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
181954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Location: /foo");
182054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transferKind.setBody(response, "This page has moved!", 10);
182154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
182254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("This is the new location!"));
182354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
182454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = client.open(server.getUrl("/"));
182554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("This is the new location!",
182654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        readAscii(connection.getInputStream(), Integer.MAX_VALUE));
182754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
182854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest first = server.takeRequest();
182954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET / HTTP/1.1", first.getRequestLine());
183054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest retry = server.takeRequest();
183154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET /foo HTTP/1.1", retry.getRequestLine());
183254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (reuse) {
183354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertEquals("Expected connection reuse", 1, retry.getSequenceNumber());
183454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
183554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
183654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
183754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void redirectedOnHttps() throws IOException, InterruptedException {
183871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), false);
183954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
184054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Location: /foo")
184154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("This page has moved!"));
184254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("This is the new location!"));
184354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1844e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSslSocketFactory(sslContext.getSocketFactory());
1845e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(new RecordingHostnameVerifier());
18463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
184754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("This is the new location!",
184854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        readAscii(connection.getInputStream(), Integer.MAX_VALUE));
184954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
185054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest first = server.takeRequest();
185154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET / HTTP/1.1", first.getRequestLine());
185254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest retry = server.takeRequest();
185354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET /foo HTTP/1.1", retry.getRequestLine());
185454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("Expected connection reuse", 1, retry.getSequenceNumber());
185554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
185654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
185754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void notRedirectedFromHttpsToHttp() throws IOException, InterruptedException {
185871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), false);
185954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
186054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Location: http://anyhost/foo")
186154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("This page has moved!"));
186254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1863e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setFollowSslRedirects(false);
1864e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSslSocketFactory(sslContext.getSocketFactory());
1865e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(new RecordingHostnameVerifier());
18663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
186754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("This page has moved!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
186854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
186954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
187054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void notRedirectedFromHttpToHttps() throws IOException, InterruptedException {
187154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
187254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Location: https://anyhost/foo")
187354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("This page has moved!"));
187454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1875e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setFollowSslRedirects(false);
18763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
187754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("This page has moved!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
187854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
187954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
18807407d6984ce69693097befc9b72609a8156463bbNarayan Kamath  @Test public void redirectedFromHttpsToHttpFollowingProtocolRedirects() throws Exception {
18817407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    server2.enqueue(new MockResponse().setBody("This is insecure HTTP!"));
18827407d6984ce69693097befc9b72609a8156463bbNarayan Kamath
188371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), false);
18847407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
18857407d6984ce69693097befc9b72609a8156463bbNarayan Kamath        .addHeader("Location: " + server2.getUrl("/"))
18867407d6984ce69693097befc9b72609a8156463bbNarayan Kamath        .setBody("This page has moved!"));
18877407d6984ce69693097befc9b72609a8156463bbNarayan Kamath
1888e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSslSocketFactory(sslContext.getSocketFactory());
1889e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(new RecordingHostnameVerifier());
1890e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setFollowSslRedirects(true);
18917407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    HttpsURLConnection connection = (HttpsURLConnection) client.open(server.getUrl("/"));
18927407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    assertContent("This is insecure HTTP!", connection);
18937407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    assertNull(connection.getCipherSuite());
18947407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    assertNull(connection.getLocalCertificates());
18957407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    assertNull(connection.getServerCertificates());
18967407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    assertNull(connection.getPeerPrincipal());
18977407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    assertNull(connection.getLocalPrincipal());
18987407d6984ce69693097befc9b72609a8156463bbNarayan Kamath  }
18997407d6984ce69693097befc9b72609a8156463bbNarayan Kamath
19007407d6984ce69693097befc9b72609a8156463bbNarayan Kamath  @Test public void redirectedFromHttpToHttpsFollowingProtocolRedirects() throws Exception {
190171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server2.useHttps(sslContext.getSocketFactory(), false);
19027407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    server2.enqueue(new MockResponse().setBody("This is secure HTTPS!"));
19037407d6984ce69693097befc9b72609a8156463bbNarayan Kamath
19047407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
19057407d6984ce69693097befc9b72609a8156463bbNarayan Kamath        .addHeader("Location: " + server2.getUrl("/"))
19067407d6984ce69693097befc9b72609a8156463bbNarayan Kamath        .setBody("This page has moved!"));
19077407d6984ce69693097befc9b72609a8156463bbNarayan Kamath
1908e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSslSocketFactory(sslContext.getSocketFactory());
1909e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(new RecordingHostnameVerifier());
1910e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setFollowSslRedirects(true);
19113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
19127407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    assertContent("This is secure HTTPS!", connection);
19137407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    assertFalse(connection instanceof HttpsURLConnection);
19147407d6984ce69693097befc9b72609a8156463bbNarayan Kamath  }
19157407d6984ce69693097befc9b72609a8156463bbNarayan Kamath
191654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void redirectToAnotherOriginServer() throws Exception {
19177407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    redirectToAnotherOriginServer(false);
19187407d6984ce69693097befc9b72609a8156463bbNarayan Kamath  }
19197407d6984ce69693097befc9b72609a8156463bbNarayan Kamath
19207407d6984ce69693097befc9b72609a8156463bbNarayan Kamath  @Test public void redirectToAnotherOriginServerWithHttps() throws Exception {
19217407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    redirectToAnotherOriginServer(true);
19227407d6984ce69693097befc9b72609a8156463bbNarayan Kamath  }
19237407d6984ce69693097befc9b72609a8156463bbNarayan Kamath
19247407d6984ce69693097befc9b72609a8156463bbNarayan Kamath  private void redirectToAnotherOriginServer(boolean https) throws Exception {
19257407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    if (https) {
192671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      server.useHttps(sslContext.getSocketFactory(), false);
192771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      server2.useHttps(sslContext.getSocketFactory(), false);
192871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      server2.setProtocolNegotiationEnabled(false);
1929e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      client.client().setSslSocketFactory(sslContext.getSocketFactory());
1930e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      client.client().setHostnameVerifier(new RecordingHostnameVerifier());
19317407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    }
19327407d6984ce69693097befc9b72609a8156463bbNarayan Kamath
193354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server2.enqueue(new MockResponse().setBody("This is the 2nd server!"));
19347407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    server2.enqueue(new MockResponse().setBody("This is the 2nd server, again!"));
193554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
193654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
193754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Location: " + server2.getUrl("/").toString())
193854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("This page has moved!"));
193954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("This is the first server again!"));
194054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
19413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
19427407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    assertContent("This is the 2nd server!", connection);
194354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(server2.getUrl("/"), connection.getURL());
194454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
194554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // make sure the first server was careful to recycle the connection
19467407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    assertContent("This is the first server again!", client.open(server.getUrl("/")));
19477407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    assertContent("This is the 2nd server, again!", client.open(server2.getUrl("/")));
19487407d6984ce69693097befc9b72609a8156463bbNarayan Kamath
194971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    String server1Host = server.getHostName() + ":" + server.getPort();
195071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    String server2Host = server2.getHostName() + ":" + server2.getPort();
1951e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(server1Host, server.takeRequest().getHeader("Host"));
1952e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(server2Host, server2.takeRequest().getHeader("Host"));
19537407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    assertEquals("Expected connection reuse", 1, server.takeRequest().getSequenceNumber());
19547407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    assertEquals("Expected connection reuse", 1, server2.takeRequest().getSequenceNumber());
19557407d6984ce69693097befc9b72609a8156463bbNarayan Kamath  }
19567407d6984ce69693097befc9b72609a8156463bbNarayan Kamath
19577407d6984ce69693097befc9b72609a8156463bbNarayan Kamath  @Test public void redirectWithProxySelector() throws Exception {
19587407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    final List<URI> proxySelectionRequests = new ArrayList<URI>();
1959e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setProxySelector(new ProxySelector() {
19607407d6984ce69693097befc9b72609a8156463bbNarayan Kamath      @Override public List<Proxy> select(URI uri) {
19617407d6984ce69693097befc9b72609a8156463bbNarayan Kamath        proxySelectionRequests.add(uri);
196271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        MockWebServer proxyServer = (uri.getPort() == server.getPort())
196371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller            ? server
196471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller            : server2;
19657407d6984ce69693097befc9b72609a8156463bbNarayan Kamath        return Arrays.asList(proxyServer.toProxyAddress());
19667407d6984ce69693097befc9b72609a8156463bbNarayan Kamath      }
1967e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
19687407d6984ce69693097befc9b72609a8156463bbNarayan Kamath      @Override public void connectFailed(URI uri, SocketAddress address, IOException failure) {
19697407d6984ce69693097befc9b72609a8156463bbNarayan Kamath        throw new AssertionError();
19707407d6984ce69693097befc9b72609a8156463bbNarayan Kamath      }
19717407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    });
197254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
19737407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    server2.enqueue(new MockResponse().setBody("This is the 2nd server!"));
19747407d6984ce69693097befc9b72609a8156463bbNarayan Kamath
19757407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
19767407d6984ce69693097befc9b72609a8156463bbNarayan Kamath        .addHeader("Location: " + server2.getUrl("/b").toString())
19777407d6984ce69693097befc9b72609a8156463bbNarayan Kamath        .setBody("This page has moved!"));
19787407d6984ce69693097befc9b72609a8156463bbNarayan Kamath
19797407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    assertContent("This is the 2nd server!", client.open(server.getUrl("/a")));
19807407d6984ce69693097befc9b72609a8156463bbNarayan Kamath
19816c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    assertEquals(Arrays.asList(server.getUrl("/").toURI(), server2.getUrl("/").toURI()),
19827407d6984ce69693097befc9b72609a8156463bbNarayan Kamath        proxySelectionRequests);
1983e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
198454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1985e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void redirectWithAuthentication() throws Exception {
1986e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server2.enqueue(new MockResponse().setBody("Page 2"));
1987e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1988e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setResponseCode(401));
1989e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setResponseCode(302)
1990e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Location: " + server2.getUrl("/b")));
1991e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1992e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setAuthenticator(
1993e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        new RecordingOkAuthenticator(Credentials.basic("jesse", "secret")));
1994e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertContent("Page 2", client.open(server.getUrl("/a")));
1995e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1996e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest redirectRequest = server2.takeRequest();
1997e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(redirectRequest.getHeader("Authorization"));
1998e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("/b", redirectRequest.getPath());
199954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
200054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
200154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void response300MultipleChoiceWithPost() throws Exception {
200254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Chrome doesn't follow the redirect, but Firefox and the RI both do
200378092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller    testResponseRedirectedWithPost(HttpURLConnection.HTTP_MULT_CHOICE, TransferKind.END_OF_STREAM);
200454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
200554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
200654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void response301MovedPermanentlyWithPost() throws Exception {
200778092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller    testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_PERM, TransferKind.END_OF_STREAM);
200854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
200954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
201054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void response302MovedTemporarilyWithPost() throws Exception {
201178092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller    testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_TEMP, TransferKind.END_OF_STREAM);
201254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
201354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
201454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void response303SeeOtherWithPost() throws Exception {
201578092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller    testResponseRedirectedWithPost(HttpURLConnection.HTTP_SEE_OTHER, TransferKind.END_OF_STREAM);
201654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
201754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
201878092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller  @Test public void postRedirectToGetWithChunkedRequest() throws Exception {
201978092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller    testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_TEMP, TransferKind.CHUNKED);
202078092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller  }
202178092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller
202278092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller  @Test public void postRedirectToGetWithStreamedRequest() throws Exception {
202378092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller    testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_TEMP, TransferKind.FIXED_LENGTH);
202478092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller  }
202578092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller
202678092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller  private void testResponseRedirectedWithPost(int redirectCode, TransferKind transferKind)
202778092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller      throws Exception {
202854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(redirectCode)
202954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Location: /page2")
203054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("This page has moved!"));
203154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("Page 2"));
203254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
20333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/page1"));
203454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setDoOutput(true);
203578092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller    transferKind.setForRequest(connection, 4);
203654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    byte[] requestBody = { 'A', 'B', 'C', 'D' };
203754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    OutputStream outputStream = connection.getOutputStream();
203854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    outputStream.write(requestBody);
203954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    outputStream.close();
204054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("Page 2", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
204154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertTrue(connection.getDoOutput());
204254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
204354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest page1 = server.takeRequest();
204454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("POST /page1 HTTP/1.1", page1.getRequestLine());
2045e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("ABCD", page1.getBody().readUtf8());
204654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
204754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest page2 = server.takeRequest();
204854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET /page2 HTTP/1.1", page2.getRequestLine());
204954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
205054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2051166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath  @Test public void redirectedPostStripsRequestBodyHeaders() throws Exception {
2052e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
2053166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath        .addHeader("Location: /page2"));
2054166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    server.enqueue(new MockResponse().setBody("Page 2"));
2055166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
20563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/page1"));
2057166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    connection.setDoOutput(true);
2058166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    connection.addRequestProperty("Content-Length", "4");
2059166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    connection.addRequestProperty("Content-Type", "text/plain; charset=utf-8");
2060166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    connection.addRequestProperty("Transfer-Encoding", "identity");
2061166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    OutputStream outputStream = connection.getOutputStream();
2062166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    outputStream.write("ABCD".getBytes("UTF-8"));
2063166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    outputStream.close();
2064166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    assertEquals("Page 2", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
2065166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
2066166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    assertEquals("POST /page1 HTTP/1.1", server.takeRequest().getRequestLine());
2067166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
2068166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    RecordedRequest page2 = server.takeRequest();
2069166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath    assertEquals("GET /page2 HTTP/1.1", page2.getRequestLine());
2070e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(page2.getHeader("Content-Length"));
2071e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(page2.getHeader("Content-Type"));
2072e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(page2.getHeader("Transfer-Encoding"));
2073166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath  }
2074166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
207554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void response305UseProxy() throws Exception {
207654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_USE_PROXY)
207754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Location: " + server.getUrl("/"))
207854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("This page has moved!"));
207954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("Proxy Response"));
208054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
20813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/foo"));
208254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Fails on the RI, which gets "Proxy Response"
208354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("This page has moved!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
208454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
208554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest page1 = server.takeRequest();
208654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET /foo HTTP/1.1", page1.getRequestLine());
208754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(1, server.getRequestCount());
208854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
208954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2090faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void response307WithGet() throws Exception {
2091e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRedirect(true, "GET");
2092faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
2093faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2094faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void response307WithHead() throws Exception {
2095e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRedirect(true, "HEAD");
2096faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
2097faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2098faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void response307WithOptions() throws Exception {
2099e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRedirect(true, "OPTIONS");
2100faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
2101faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2102faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void response307WithPost() throws Exception {
2103e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRedirect(true, "POST");
2104e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2105e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2106e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void response308WithGet() throws Exception {
2107e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRedirect(false, "GET");
2108e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2109e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2110e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void response308WithHead() throws Exception {
2111e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRedirect(false, "HEAD");
2112e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2113e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2114e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void response308WithOptions() throws Exception {
2115e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRedirect(false, "OPTIONS");
2116e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2117e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2118e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void response308WithPost() throws Exception {
2119e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    testRedirect(false, "POST");
2120faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
2121faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2122e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void testRedirect(boolean temporary, String method) throws Exception {
2123faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    MockResponse response1 = new MockResponse()
2124e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(temporary ? HTTP_TEMP_REDIRECT : HTTP_PERM_REDIRECT)
2125faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("Location: /page2");
2126faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    if (!method.equals("HEAD")) {
2127faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      response1.setBody("This page has moved!");
2128faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    }
2129faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(response1);
2130faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("Page 2"));
2131faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
21323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/page1"));
2133faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    connection.setRequestMethod(method);
2134faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    byte[] requestBody = { 'A', 'B', 'C', 'D' };
2135faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    if (method.equals("POST")) {
2136faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      connection.setDoOutput(true);
2137faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      OutputStream outputStream = connection.getOutputStream();
2138faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      outputStream.write(requestBody);
2139faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      outputStream.close();
2140faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    }
2141faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2142faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    String response = readAscii(connection.getInputStream(), Integer.MAX_VALUE);
2143faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2144faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    RecordedRequest page1 = server.takeRequest();
2145faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals(method + " /page1 HTTP/1.1", page1.getRequestLine());
2146faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2147faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    if (method.equals("GET")) {
2148d7254e38efa2f20db6cac1a5cb5ac4548edc3d46Neil Fuller      assertEquals("Page 2", response);
2149e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } else if (method.equals("HEAD"))  {
2150d7254e38efa2f20db6cac1a5cb5ac4548edc3d46Neil Fuller      assertEquals("", response);
2151faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    } else {
2152faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      // Methods other than GET/HEAD shouldn't follow the redirect
2153faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      if (method.equals("POST")) {
2154faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        assertTrue(connection.getDoOutput());
2155e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        assertEquals("ABCD", page1.getBody().readUtf8());
2156faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      }
2157faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      assertEquals(1, server.getRequestCount());
2158faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      assertEquals("This page has moved!", response);
2159faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      return;
2160faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    }
2161faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2162faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    // GET/HEAD requests should have followed the redirect with the same method
2163faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertFalse(connection.getDoOutput());
2164faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals(2, server.getRequestCount());
2165faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    RecordedRequest page2 = server.takeRequest();
2166faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals(method + " /page2 HTTP/1.1", page2.getRequestLine());
2167faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
2168faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
216954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void follow20Redirects() throws Exception {
217054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 0; i < 20; i++) {
217154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
217254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          .addHeader("Location: /" + (i + 1))
217354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          .setBody("Redirecting to /" + (i + 1)));
217454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
217554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("Success!"));
217654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
21773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/0"));
217854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("Success!", connection);
217954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(server.getUrl("/20"), connection.getURL());
218054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
218154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
218254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void doesNotFollow21Redirects() throws Exception {
218354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 0; i < 21; i++) {
218454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
218554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          .addHeader("Location: /" + (i + 1))
218654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          .setBody("Redirecting to /" + (i + 1)));
218754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
218854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
21893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/0"));
219054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
219154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.getInputStream();
219254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
219354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (ProtocolException expected) {
219454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertEquals(HttpURLConnection.HTTP_MOVED_TEMP, connection.getResponseCode());
2195e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("Too many follow-up requests: 21", expected.getMessage());
219654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertContent("Redirecting to /21", connection);
219754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertEquals(server.getUrl("/20"), connection.getURL());
219854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
219954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
220054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
220154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void httpsWithCustomTrustManager() throws Exception {
220254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier();
22036c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    RecordingTrustManager trustManager = new RecordingTrustManager(sslContext);
220454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    SSLContext sc = SSLContext.getInstance("TLS");
22056c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    sc.init(null, new TrustManager[] {trustManager}, new SecureRandom());
220654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2207e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(hostnameVerifier);
2208e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSslSocketFactory(sc.getSocketFactory());
220971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), false);
221054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("ABC"));
221154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("DEF"));
221254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("GHI"));
221354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
221454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = server.getUrl("/");
221554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("ABC", client.open(url));
221654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("DEF", client.open(url));
221754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("GHI", client.open(url));
221854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
22196c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    assertEquals(Arrays.asList("verify " + server.getHostName()), hostnameVerifier.calls);
222071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals(Arrays.asList("checkServerTrusted [CN=" + server.getHostName() + " 1]"),
2221e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        trustManager.calls);
222254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
222354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
222454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void readTimeouts() throws IOException {
222554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // This relies on the fact that MockWebServer doesn't close the
222654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // connection after a response has been sent. This causes the client to
222754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // try to read more bytes than are sent, which results in a timeout.
222854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse timeout =
222954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        new MockResponse().setBody("ABC").clearHeaders().addHeader("Content-Length: 4");
223054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(timeout);
223154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("unused")); // to keep the server alive
223254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
22333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    URLConnection connection = client.open(server.getUrl("/"));
22343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.setReadTimeout(1000);
22353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    InputStream in = connection.getInputStream();
223654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals('A', in.read());
223754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals('B', in.read());
223854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals('C', in.read());
223954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
224054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      in.read(); // if Content-Length was accurate, this would return -1 immediately
224154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
2242b5f9076b16fcc41c3dad31aecfdcfd962a7a1f75Neil Fuller    } catch (SocketTimeoutException expected) {
2243e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
22446c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    in.close();
2245e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2246e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2247e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** Confirm that an unacknowledged write times out. */
2248e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void writeTimeouts() throws IOException {
2249d22dae2ff3c14d458829d7082651f3e6baa9b65dNeil Fuller    MockWebServer server = new MockWebServer();
2250e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Sockets on some platforms can have large buffers that mean writes do not block when
2251e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // required. These socket factories explicitly set the buffer sizes on sockets created.
2252e0d02d710cd1bbcc1a9065ede7f6801f72abe5b7Neil Fuller    final int SOCKET_BUFFER_SIZE = 4 * 1024;
225371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.setServerSocketFactory(
2254e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        new DelegatingServerSocketFactory(ServerSocketFactory.getDefault()) {
2255e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          @Override
225671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller          protected ServerSocket configureServerSocket(ServerSocket serverSocket)
225771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller              throws IOException {
2258e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            serverSocket.setReceiveBufferSize(SOCKET_BUFFER_SIZE);
225971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller            return serverSocket;
2260e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          }
2261e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        });
2262e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSocketFactory(new DelegatingSocketFactory(SocketFactory.getDefault()) {
2263e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override
226471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      protected Socket configureSocket(Socket socket) throws IOException {
2265e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        socket.setReceiveBufferSize(SOCKET_BUFFER_SIZE);
2266e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        socket.setSendBufferSize(SOCKET_BUFFER_SIZE);
226771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        return socket;
2268e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
2269e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    });
2270e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2271d22dae2ff3c14d458829d7082651f3e6baa9b65dNeil Fuller    server.start();
2272e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2273e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .throttleBody(1, 1, TimeUnit.SECONDS)); // Prevent the server from reading!
2274e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2275e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setWriteTimeout(500, TimeUnit.MILLISECONDS);
2276e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    connection = client.open(server.getUrl("/"));
2277e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    connection.setDoOutput(true);
2278e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    connection.setChunkedStreamingMode(0);
2279e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    OutputStream out = connection.getOutputStream();
2280e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
2281e0d02d710cd1bbcc1a9065ede7f6801f72abe5b7Neil Fuller      byte[] data = new byte[2 * 1024 * 1024]; // 2 MiB.
2282e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      out.write(data);
2283e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
2284b5f9076b16fcc41c3dad31aecfdcfd962a7a1f75Neil Fuller    } catch (SocketTimeoutException expected) {
228554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
228654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
228754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
228854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void setChunkedEncodingAsRequestProperty() throws IOException, InterruptedException {
228954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse());
229054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
22913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
22923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.setRequestProperty("Transfer-encoding", "chunked");
22933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.setDoOutput(true);
22943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.getOutputStream().write("ABC".getBytes("UTF-8"));
22953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(200, connection.getResponseCode());
229654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
229754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = server.takeRequest();
2298e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("ABC", request.getBody().readUtf8());
229954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
230054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
230154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectionCloseInRequest() throws IOException, InterruptedException {
230254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse()); // server doesn't honor the connection: close header!
230354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse());
230454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
230554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection a = client.open(server.getUrl("/"));
230654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    a.setRequestProperty("Connection", "close");
230754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(200, a.getResponseCode());
230854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
230954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection b = client.open(server.getUrl("/"));
231054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(200, b.getResponseCode());
231154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
231254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, server.takeRequest().getSequenceNumber());
231354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("When connection: close is used, each request should get its own connection", 0,
231454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        server.takeRequest().getSequenceNumber());
231554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
231654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
231754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectionCloseInResponse() throws IOException, InterruptedException {
231854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Connection: close"));
231954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse());
232054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
232154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection a = client.open(server.getUrl("/"));
232254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(200, a.getResponseCode());
232354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
232454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection b = client.open(server.getUrl("/"));
232554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(200, b.getResponseCode());
232654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
232754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, server.takeRequest().getSequenceNumber());
232854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("When connection: close is used, each request should get its own connection", 0,
232954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        server.takeRequest().getSequenceNumber());
233054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
233154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
233254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void connectionCloseWithRedirect() throws IOException, InterruptedException {
233354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response = new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
233454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Location: /foo")
233554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Connection: close");
233654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
233754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("This is the new location!"));
233854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
233954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = client.open(server.getUrl("/"));
234054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("This is the new location!",
234154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        readAscii(connection.getInputStream(), Integer.MAX_VALUE));
234254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
234354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, server.takeRequest().getSequenceNumber());
234454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("When connection: close is used, each request should get its own connection", 0,
234554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        server.takeRequest().getSequenceNumber());
234654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
234754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
234854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
234954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Retry redirects if the socket is closed.
235054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * https://code.google.com/p/android/issues/detail?id=41576
235154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
235254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void sameConnectionRedirectAndReuse() throws Exception {
235354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
235454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setSocketPolicy(SHUTDOWN_INPUT_AT_END)
235554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Location: /foo"));
235654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("This is the new page!"));
235754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
235854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent("This is the new page!", client.open(server.getUrl("/")));
235954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
236054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, server.takeRequest().getSequenceNumber());
236154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, server.takeRequest().getSequenceNumber());
236254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
236354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
236454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void responseCodeDisagreesWithHeaders() throws IOException, InterruptedException {
236554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NO_CONTENT)
236654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setBody("This body is not allowed!"));
236754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
236854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URLConnection connection = client.open(server.getUrl("/"));
236954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("This body is not allowed!",
237054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        readAscii(connection.getInputStream(), Integer.MAX_VALUE));
237154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
237254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
237354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void singleByteReadIsSigned() throws IOException {
2374e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setBody(new Buffer().writeByte(-2).writeByte(-1)));
237554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
23763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
237754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream in = connection.getInputStream();
237854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(254, in.read());
237954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(255, in.read());
238054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(-1, in.read());
238154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
238254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
238354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void flushAfterStreamTransmittedWithChunkedEncoding() throws IOException {
238454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testFlushAfterStreamTransmitted(TransferKind.CHUNKED);
238554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
238654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
238754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void flushAfterStreamTransmittedWithFixedLength() throws IOException {
238854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testFlushAfterStreamTransmitted(TransferKind.FIXED_LENGTH);
238954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
239054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
239154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void flushAfterStreamTransmittedWithNoLengthHeaders() throws IOException {
239254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testFlushAfterStreamTransmitted(TransferKind.END_OF_STREAM);
239354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
239454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
239554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
239654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * We explicitly permit apps to close the upload stream even after it has
239754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * been transmitted.  We also permit flush so that buffered streams can
239854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * do a no-op flush when they are closed. http://b/3038470
239954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
240054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testFlushAfterStreamTransmitted(TransferKind transferKind) throws IOException {
240154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("abc"));
240254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
24033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
240454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setDoOutput(true);
240554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    byte[] upload = "def".getBytes("UTF-8");
240654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
240754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (transferKind == TransferKind.CHUNKED) {
240854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.setChunkedStreamingMode(0);
240954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else if (transferKind == TransferKind.FIXED_LENGTH) {
241054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.setFixedLengthStreamingMode(upload.length);
241154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
241254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
241354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    OutputStream out = connection.getOutputStream();
241454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    out.write(upload);
241554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("abc", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
241654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
24173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    out.flush(); // Dubious but permitted.
241854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
241954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      out.write("ghi".getBytes("UTF-8"));
242054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
242154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException expected) {
242254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
242354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
242454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
242554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getHeadersThrows() throws IOException {
2426e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setSocketPolicy(DISCONNECT_AT_START));
242754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
24283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
242954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
243054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.getInputStream();
243154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
243254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException expected) {
243354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
243454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
243554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
243654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.getInputStream();
243754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
243854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException expected) {
243954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
244054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
244154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
244254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void dnsFailureThrowsIOException() throws IOException {
24433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(new URL("http://host.unlikelytld"));
244454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
244554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.connect();
244654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
244754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException expected) {
244854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
244954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
245054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
245154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void malformedUrlThrowsUnknownHostException() throws IOException {
2452ca342d6c3c54ae2417d4e61c925ba6f63b31a571jwilson    connection = client.open(new URL("http://./foo.html"));
245354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
245454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.connect();
245554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
245654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (UnknownHostException expected) {
245754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
245854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
245954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
246054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getKeepAlive() throws Exception {
246154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("ABC"));
246254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
246354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // The request should work once and then fail
246471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpURLConnection connection1 = client.open(server.getUrl("/"));
246554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection1.setReadTimeout(100);
246654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream input = connection1.getInputStream();
246754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(input, Integer.MAX_VALUE));
246871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.shutdown();
246954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
247054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      HttpURLConnection connection2 = client.open(server.getUrl(""));
247154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection2.setReadTimeout(100);
247254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection2.getInputStream();
247354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
247454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (ConnectException expected) {
247554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
247654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
247754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
247854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** http://code.google.com/p/android/issues/detail?id=14562 */
247954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void readAfterLastByte() throws Exception {
248054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("ABC")
248154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .clearHeaders()
248254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .addHeader("Connection: close")
248354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END));
248454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
24853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
248654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream in = connection.getInputStream();
248754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("ABC", readAscii(in, 3));
248854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(-1, in.read());
248954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(-1, in.read()); // throws IOException in Gingerbread
249054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
249154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
249254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getContent() throws Exception {
249354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Content-Type: text/plain").setBody("A"));
24943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
249554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream in = (InputStream) connection.getContent();
249654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(in, Integer.MAX_VALUE));
249754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
249854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
249954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getContentOfType() throws Exception {
250054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Content-Type: text/plain").setBody("A"));
25013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
250254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
250354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.getContent(null);
250454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
250554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (NullPointerException expected) {
250654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
250754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
250854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.getContent(new Class[] { null });
250954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
251054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (NullPointerException expected) {
251154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
251271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertNull(connection.getContent(new Class[] { getClass() }));
25136c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    connection.getInputStream().close();
251454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
251554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
251654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getOutputStreamOnGetFails() throws Exception {
251754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse());
25183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
251954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
252054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.getOutputStream();
252154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
252254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (ProtocolException expected) {
252354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
25246c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    connection.getInputStream().close();
252554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
252654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
252754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getOutputAfterGetInputStreamFails() throws Exception {
252854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse());
25293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
253054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setDoOutput(true);
253154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
253254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.getInputStream();
253354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.getOutputStream();
253454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
253554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (ProtocolException expected) {
253654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
253754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
253854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
253954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void setDoOutputOrDoInputAfterConnectFails() throws Exception {
254054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse());
25413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
254254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.connect();
254354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
254454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.setDoOutput(true);
254554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
254654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IllegalStateException expected) {
254754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
254854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
254954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection.setDoInput(true);
255054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      fail();
255154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IllegalStateException expected) {
255254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
25536c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    connection.getInputStream().close();
255454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
255554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
255654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void clientSendsContentLength() throws Exception {
255754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A"));
25583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
255954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.setDoOutput(true);
256054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    OutputStream out = connection.getOutputStream();
256154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    out.write(new byte[] { 'A', 'B', 'C' });
256254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    out.close();
256354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
256454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = server.takeRequest();
2565e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("3", request.getHeader("Content-Length"));
25666c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    connection.getInputStream().close();
256754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
256854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
256954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getContentLengthConnects() throws Exception {
257054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("ABC"));
25713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
257254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(3, connection.getContentLength());
25736c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    connection.getInputStream().close();
257454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
257554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
257654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getContentTypeConnects() throws Exception {
257754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Content-Type: text/plain").setBody("ABC"));
25783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
257954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("text/plain", connection.getContentType());
25806c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    connection.getInputStream().close();
258154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
258254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
258354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void getContentEncodingConnects() throws Exception {
258454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().addHeader("Content-Encoding: identity").setBody("ABC"));
25853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
258654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("identity", connection.getContentEncoding());
25876c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    connection.getInputStream().close();
258854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
258954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
259054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // http://b/4361656
259154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void urlContainsQueryButNoPath() throws Exception {
259254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(new MockResponse().setBody("A"));
2593e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
259471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    URL url = new URL("http", server.getHostName(), server.getPort(), "?query");
259554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("A", readAscii(client.open(url).getInputStream(), Integer.MAX_VALUE));
259654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RecordedRequest request = server.takeRequest();
259754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals("GET /?query HTTP/1.1", request.getRequestLine());
259854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
259954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2600e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void doOutputForMethodThatDoesntSupportOutput() throws Exception {
2601e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    connection = client.open(server.getUrl("/"));
2602e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    connection.setRequestMethod("HEAD");
2603e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    connection.setDoOutput(true);
2604e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
2605e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      connection.connect();
2606e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
2607e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IOException expected) {
2608e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
2609e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2610e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
261154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // http://code.google.com/p/android/issues/detail?id=20442
261254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void inputStreamAvailableWithChunkedEncoding() throws Exception {
261354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testInputStreamAvailable(TransferKind.CHUNKED);
261454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
261554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
261654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void inputStreamAvailableWithContentLengthHeader() throws Exception {
261754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testInputStreamAvailable(TransferKind.FIXED_LENGTH);
261854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
261954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
262054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test public void inputStreamAvailableWithNoLengthHeaders() throws Exception {
262154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    testInputStreamAvailable(TransferKind.END_OF_STREAM);
262254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
262354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
262454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void testInputStreamAvailable(TransferKind transferKind) throws IOException {
262554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String body = "ABCDEFGH";
262654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    MockResponse response = new MockResponse();
262754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transferKind.setBody(response, body, 4);
262854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    server.enqueue(response);
26293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
263054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    InputStream in = connection.getInputStream();
263154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 0; i < body.length(); i++) {
263254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertTrue(in.available() >= 0);
263354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      assertEquals(body.charAt(i), in.read());
263454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
263554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(0, in.available());
263654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(-1, in.read());
263754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
263854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2639faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void postFailsWithBufferedRequestForSmallRequest() throws Exception {
2640faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    reusedConnectionFailsWithPost(TransferKind.END_OF_STREAM, 1024);
2641faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
2642faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2643fb0eb65be9f50e75fa37c250e97914252c587cafjwilson  @Test public void postFailsWithBufferedRequestForLargeRequest() throws Exception {
2644faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    reusedConnectionFailsWithPost(TransferKind.END_OF_STREAM, 16384);
2645faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
2646faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2647faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void postFailsWithChunkedRequestForSmallRequest() throws Exception {
2648faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    reusedConnectionFailsWithPost(TransferKind.CHUNKED, 1024);
2649faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
2650faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2651fb0eb65be9f50e75fa37c250e97914252c587cafjwilson  @Test public void postFailsWithChunkedRequestForLargeRequest() throws Exception {
2652faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    reusedConnectionFailsWithPost(TransferKind.CHUNKED, 16384);
2653faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
2654faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2655faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void postFailsWithFixedLengthRequestForSmallRequest() throws Exception {
2656faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    reusedConnectionFailsWithPost(TransferKind.FIXED_LENGTH, 1024);
2657faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
2658faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2659fb0eb65be9f50e75fa37c250e97914252c587cafjwilson  @Test public void postFailsWithFixedLengthRequestForLargeRequest() throws Exception {
2660faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    reusedConnectionFailsWithPost(TransferKind.FIXED_LENGTH, 16384);
2661faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
2662faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2663faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  private void reusedConnectionFailsWithPost(TransferKind transferKind, int requestSize)
2664faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      throws Exception {
2665e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setBody("A").setSocketPolicy(DISCONNECT_AT_END));
2666faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("B"));
26673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse().setBody("C"));
2668faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2669faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertContent("A", client.open(server.getUrl("/a")));
2670faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2671faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    // If the request body is larger than OkHttp's replay buffer, the failure may still occur.
2672faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    byte[] requestBody = new byte[requestSize];
2673faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    new Random(0).nextBytes(requestBody);
2674faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2675a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    for (int j = 0; j < 2; j++) {
2676a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      try {
2677a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        connection = client.open(server.getUrl("/b"));
2678a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        connection.setRequestMethod("POST");
2679a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        transferKind.setForRequest(connection, requestBody.length);
2680a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        for (int i = 0; i < requestBody.length; i += 1024) {
2681a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller          connection.getOutputStream().write(requestBody, i, 1024);
2682a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        }
2683a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        connection.getOutputStream().close();
2684a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        assertContent("B", connection);
2685a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        break;
2686a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      } catch (IOException socketException) {
2687a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        // If there's a socket exception, this must have a streamed request body.
2688a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        assertEquals(0, j);
2689a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        assertTrue(transferKind == TransferKind.CHUNKED
2690a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller            || transferKind == TransferKind.FIXED_LENGTH);
2691a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      }
2692faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    }
2693faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2694faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    RecordedRequest requestA = server.takeRequest();
2695faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("/a", requestA.getPath());
2696faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    RecordedRequest requestB = server.takeRequest();
2697faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertEquals("/b", requestB.getPath());
2698e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(Arrays.toString(requestBody), Arrays.toString(requestB.getBody().readByteArray()));
2699e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2700e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2701e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void postBodyRetransmittedOnFailureRecovery() throws Exception {
2702e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setBody("abc"));
2703e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AFTER_REQUEST));
2704e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setBody("def"));
2705e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2706e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Seed the connection pool so we have something that can fail.
2707e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertContent("abc", client.open(server.getUrl("/")));
2708e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2709e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    HttpURLConnection post = client.open(server.getUrl("/"));
2710e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    post.setDoOutput(true);
2711e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    post.getOutputStream().write("body!".getBytes(Util.UTF_8));
2712e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertContent("def", post);
2713e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2714e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest get = server.takeRequest();
2715e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, get.getSequenceNumber());
2716e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2717e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest post1 = server.takeRequest();
2718e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("body!", post1.getBody().readUtf8());
2719e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, post1.getSequenceNumber());
2720e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2721e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest post2 = server.takeRequest();
2722e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("body!", post2.getBody().readUtf8());
2723e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, post2.getSequenceNumber());
2724faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
2725faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2726faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void fullyBufferedPostIsTooShort() throws Exception {
2727faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("A"));
2728faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
27293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/b"));
2730faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    connection.setRequestProperty("Content-Length", "4");
2731faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    connection.setRequestMethod("POST");
2732faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    OutputStream out = connection.getOutputStream();
2733faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    out.write('a');
2734faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    out.write('b');
2735faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    out.write('c');
2736faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    try {
2737faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      out.close();
2738faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      fail();
2739faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    } catch (IOException expected) {
2740faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    }
2741faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
2742faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2743faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void fullyBufferedPostIsTooLong() throws Exception {
2744faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("A"));
2745faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
27463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/b"));
2747faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    connection.setRequestProperty("Content-Length", "3");
2748faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    connection.setRequestMethod("POST");
2749faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    OutputStream out = connection.getOutputStream();
2750faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    out.write('a');
2751faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    out.write('b');
2752faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    out.write('c');
2753faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    try {
2754faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      out.write('d');
27553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      out.flush();
2756faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      fail();
2757faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    } catch (IOException expected) {
2758faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    }
2759faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
2760faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
276154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test @Ignore public void testPooledConnectionsDetectHttp10() {
276254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // TODO: write a test that shows pooled connections detect HTTP/1.0 (vs. HTTP/1.1)
276354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    fail("TODO");
276454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
276554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
276654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test @Ignore public void postBodiesRetransmittedOnAuthProblems() {
276754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    fail("TODO");
276854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
276954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
277054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test @Ignore public void cookiesAndTrailers() {
277154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Do cookie headers get processed too many times?
277254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    fail("TODO");
277354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
277454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
277554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test @Ignore public void headerNamesContainingNullCharacter() {
277654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // This is relevant for SPDY
277754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    fail("TODO");
277854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
277954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
278054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test @Ignore public void headerValuesContainingNullCharacter() {
278154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // This is relevant for SPDY
278254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    fail("TODO");
278354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
278454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2785faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void emptyRequestHeaderValueIsAllowed() throws Exception {
2786faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("body"));
27873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
27883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.addRequestProperty("B", "");
27893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertContent("body", connection);
27903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("", connection.getRequestProperty("B"));
279154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
279254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2793faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void emptyResponseHeaderValueIsAllowed() throws Exception {
2794faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().addHeader("A:").setBody("body"));
27953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
27963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertContent("body", connection);
27973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("", connection.getHeaderField("A"));
2798faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
2799faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2800faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void emptyRequestHeaderNameIsStrict() throws Exception {
2801faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("body"));
28023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
2803faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    try {
28043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      connection.setRequestProperty("", "A");
2805faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      fail();
2806faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    } catch (IllegalArgumentException expected) {
2807faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    }
2808faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
2809faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2810faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  @Test public void emptyResponseHeaderNameIsLenient() throws Exception {
2811e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Headers.Builder headers = new Headers.Builder();
2812e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Internal.instance.addLenient(headers, ":A");
2813e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setHeaders(headers.build()).setBody("body"));
28143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
28153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.getResponseCode();
28163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("A", connection.getHeaderField(""));
28176c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    connection.getInputStream().close();
281854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
281954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
282071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Test public void requestHeaderValidationIsStrict() throws Exception {
282171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    connection = client.open(server.getUrl("/"));
282271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    try {
282371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      connection.addRequestProperty("a\tb", "Value");
282471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      fail();
282571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    } catch (IllegalArgumentException expected) {
282671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    }
282771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    try {
282871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      connection.addRequestProperty("Name", "c\u007fd");
282971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      fail();
283071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    } catch (IllegalArgumentException expected) {
283171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    }
283271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    try {
283371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      connection.addRequestProperty("", "Value");
283471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      fail();
283571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    } catch (IllegalArgumentException expected) {
283671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    }
283771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    try {
283871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      connection.addRequestProperty("\ud83c\udf69", "Value");
283971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      fail();
284071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    } catch (IllegalArgumentException expected) {
284171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    }
284286a4f0d3830d797f7e915c7aa7b3fde524e5cad4Neil Fuller
284386a4f0d3830d797f7e915c7aa7b3fde524e5cad4Neil Fuller    // ANDROID-BEGIN Disabled for http://b/28867041
284486a4f0d3830d797f7e915c7aa7b3fde524e5cad4Neil Fuller    // try {
284586a4f0d3830d797f7e915c7aa7b3fde524e5cad4Neil Fuller    //   connection.addRequestProperty("Name", "\u2615\ufe0f");
284686a4f0d3830d797f7e915c7aa7b3fde524e5cad4Neil Fuller    //   fail();
284786a4f0d3830d797f7e915c7aa7b3fde524e5cad4Neil Fuller    // } catch (IllegalArgumentException expected) {
284886a4f0d3830d797f7e915c7aa7b3fde524e5cad4Neil Fuller    // }
284986a4f0d3830d797f7e915c7aa7b3fde524e5cad4Neil Fuller    // ANDROID-END
285071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  }
285171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
285271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Test public void responseHeaderParsingIsLenient() throws Exception {
285371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    Headers headers = new Headers.Builder()
285471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .add("Content-Length", "0")
285571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .addLenient("a\tb: c\u007fd")
285671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .addLenient(": ef")
285771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .addLenient("\ud83c\udf69: \u2615\ufe0f")
285871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .build();
285971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.enqueue(new MockResponse().setHeaders(headers));
286071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
286171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    connection = client.open(server.getUrl("/"));
286271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    connection.getResponseCode();
286371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("c\u007fd", connection.getHeaderField("a\tb"));
286471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("\u2615\ufe0f", connection.getHeaderField("\ud83c\udf69"));
286571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("ef", connection.getHeaderField(""));
286671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  }
286771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
286854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test @Ignore public void deflateCompression() {
286954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    fail("TODO");
287054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
287154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
287254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test @Ignore public void postBodiesRetransmittedOnIpAddressProblems() {
287354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    fail("TODO");
287454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
287554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
287654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Test @Ignore public void pooledConnectionProblemsNotReportedToProxySelector() {
287754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    fail("TODO");
287854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
287954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2880e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void customBasicAuthenticator() throws Exception {
2881faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    MockResponse pleaseAuthenticate = new MockResponse().setResponseCode(401)
2882faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .addHeader("WWW-Authenticate: Basic realm=\"protected area\"")
2883faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        .setBody("Please authenticate.");
2884faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(pleaseAuthenticate);
2885faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("A"));
2886faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2887e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String credential = Credentials.basic("jesse", "peanutbutter");
2888faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    RecordingOkAuthenticator authenticator = new RecordingOkAuthenticator(credential);
2889e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setAuthenticator(authenticator);
2890faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertContent("A", client.open(server.getUrl("/private")));
2891faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2892e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(server.takeRequest().getHeader("Authorization"));
2893e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(credential, server.takeRequest().getHeader("Authorization"));
2894faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2895f6af62d5c9bb5e15649a80ebae973463e8e2dc46Neil Fuller    assertEquals(Proxy.NO_PROXY, authenticator.onlyProxy());
2896e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = authenticator.onlyResponse();
2897e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("/private", response.request().url().getPath());
2898e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(Arrays.asList(new Challenge("Basic", "protected area")), response.challenges());
2899faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
2900faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
2901e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void customTokenAuthenticator() throws Exception {
2902e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    MockResponse pleaseAuthenticate = new MockResponse().setResponseCode(401)
2903e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            .addHeader("WWW-Authenticate: Bearer realm=\"oauthed\"")
2904e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            .setBody("Please authenticate.");
2905e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(pleaseAuthenticate);
2906e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setBody("A"));
2907e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2908e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordingOkAuthenticator authenticator = new RecordingOkAuthenticator("oauthed abc123");
2909e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setAuthenticator(authenticator);
2910e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertContent("A", client.open(server.getUrl("/private")));
2911e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2912e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(server.takeRequest().getHeader("Authorization"));
2913e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("oauthed abc123", server.takeRequest().getHeader("Authorization"));
2914e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2915e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response response = authenticator.onlyResponse();
2916e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("/private", response.request().url().getPath());
2917e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(Arrays.asList(new Challenge("Bearer", "oauthed")), response.challenges());
29183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
29193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2920e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void authenticateCallsTrackedAsRedirects() throws Exception {
2921e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2922e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(302)
2923e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("Location: /b"));
2924e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse()
2925e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setResponseCode(401)
2926e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .addHeader("WWW-Authenticate: Basic realm=\"protected area\""));
2927e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setBody("c"));
2928e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2929e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordingOkAuthenticator authenticator = new RecordingOkAuthenticator(
2930e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        Credentials.basic("jesse", "peanutbutter"));
2931e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setAuthenticator(authenticator);
2932e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertContent("c", client.open(server.getUrl("/a")));
2933e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2934e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response challengeResponse = authenticator.responses.get(0);
2935e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("/b", challengeResponse.request().url().getPath());
2936e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2937e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Response redirectedBy = challengeResponse.priorResponse();
2938e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("/a", redirectedBy.request().url().getPath());
2939e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2940e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2941e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void attemptAuthorization20Times() throws Exception {
2942e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    for (int i = 0; i < 20; i++) {
2943e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      server.enqueue(new MockResponse().setResponseCode(401));
2944e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
2945e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setBody("Success!"));
2946e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2947e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String credential = Credentials.basic("jesse", "peanutbutter");
2948e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setAuthenticator(new RecordingOkAuthenticator(credential));
2949e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2950e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    connection = client.open(server.getUrl("/0"));
2951e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertContent("Success!", connection);
29523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
29533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2954e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void doesNotAttemptAuthorization21Times() throws Exception {
2955e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    for (int i = 0; i < 21; i++) {
2956e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      server.enqueue(new MockResponse().setResponseCode(401));
2957e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
2958e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2959e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String credential = Credentials.basic("jesse", "peanutbutter");
2960e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setAuthenticator(new RecordingOkAuthenticator(credential));
2961e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2962e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    connection = client.open(server.getUrl("/"));
2963e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
2964e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      connection.getInputStream();
2965e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
2966e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (ProtocolException expected) {
2967e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals(401, connection.getResponseCode());
2968e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("Too many follow-up requests: 21", expected.getMessage());
2969e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
2970e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2971e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2972e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void setsNegotiatedProtocolHeader_SPDY_3() throws Exception {
2973e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setsNegotiatedProtocolHeader(Protocol.SPDY_3);
2974e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2975e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2976e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void setsNegotiatedProtocolHeader_HTTP_2() throws Exception {
2977e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setsNegotiatedProtocolHeader(Protocol.HTTP_2);
2978e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2979e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2980e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void setsNegotiatedProtocolHeader(Protocol protocol) throws IOException {
2981e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    enableProtocol(protocol);
2982faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    server.enqueue(new MockResponse().setBody("A"));
2983e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setProtocols(Arrays.asList(protocol, Protocol.HTTP_1_1));
29843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
29853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    List<String> protocolValues = connection.getHeaderFields().get(SELECTED_PROTOCOL);
2986e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(Arrays.asList(protocol.toString()), protocolValues);
29873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertContent("A", connection);
29883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
29893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2990e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void http10SelectedProtocol() throws Exception {
2991e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setStatus("HTTP/1.0 200 OK"));
2992e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    connection = client.open(server.getUrl("/"));
2993e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    List<String> protocolValues = connection.getHeaderFields().get(SELECTED_PROTOCOL);
2994e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(Arrays.asList("http/1.0"), protocolValues);
2995e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2996e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2997e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void http11SelectedProtocol() throws Exception {
2998e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setStatus("HTTP/1.1 200 OK"));
2999e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    connection = client.open(server.getUrl("/"));
3000e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    List<String> protocolValues = connection.getHeaderFields().get(SELECTED_PROTOCOL);
3001e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(Arrays.asList("http/1.1"), protocolValues);
3002e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
3003e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
30043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** For example, empty Protobuf RPC messages end up as a zero-length POST. */
30053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void zeroLengthPost() throws IOException, InterruptedException {
30063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    zeroLengthPayload("POST");
30073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
30083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
30093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void zeroLengthPost_SPDY_3() throws Exception {
3010e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    enableProtocol(Protocol.SPDY_3);
30113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    zeroLengthPost();
30123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
30133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
30143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void zeroLengthPost_HTTP_2() throws Exception {
3015e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    enableProtocol(Protocol.HTTP_2);
30163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    zeroLengthPost();
30173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
30183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
30193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** For example, creating an Amazon S3 bucket ends up as a zero-length POST. */
30203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void zeroLengthPut() throws IOException, InterruptedException {
30213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    zeroLengthPayload("PUT");
30223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
30233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
30243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void zeroLengthPut_SPDY_3() throws Exception {
3025e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    enableProtocol(Protocol.SPDY_3);
30263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    zeroLengthPut();
30273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
30283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
30293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void zeroLengthPut_HTTP_2() throws Exception {
3030e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    enableProtocol(Protocol.HTTP_2);
30313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    zeroLengthPut();
30323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
30333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
30343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private void zeroLengthPayload(String method)
30353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      throws IOException, InterruptedException {
30363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse());
30373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
30383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.setRequestProperty("Content-Length", "0");
30393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.setRequestMethod(method);
30403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.setFixedLengthStreamingMode(0);
30413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.setDoOutput(true);
30423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertContent("", connection);
30433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    RecordedRequest zeroLengthPayload = server.takeRequest();
30443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(method, zeroLengthPayload.getMethod());
30453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("0", zeroLengthPayload.getHeader("content-length"));
30463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(0L, zeroLengthPayload.getBodySize());
30473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
30483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
3049e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void unspecifiedRequestBodyContentTypeGetsDefault() throws Exception {
3050e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse());
3051e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
3052e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    connection = client.open(server.getUrl("/"));
3053e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    connection.setDoOutput(true);
3054e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    connection.getOutputStream().write("abc".getBytes(UTF_8));
3055e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(200, connection.getResponseCode());
3056e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
3057e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest request = server.takeRequest();
3058e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("application/x-www-form-urlencoded", request.getHeader("Content-Type"));
3059e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("3", request.getHeader("Content-Length"));
3060e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("abc", request.getBody().readUtf8());
3061e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
3062e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
30633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void setProtocols() throws Exception {
30643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse().setBody("A"));
3065e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setProtocols(Arrays.asList(Protocol.HTTP_1_1));
3066faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    assertContent("A", client.open(server.getUrl("/")));
3067faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
3068faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
30693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void setProtocolsWithoutHttp11() throws Exception {
3070faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    try {
3071e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      client.client().setProtocols(Arrays.asList(Protocol.SPDY_3));
3072faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      fail();
3073faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    } catch (IllegalArgumentException expected) {
3074faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    }
3075faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
3076faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
30773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void setProtocolsWithNull() throws Exception {
3078faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    try {
3079e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      client.client().setProtocols(Arrays.asList(Protocol.HTTP_1_1, null));
3080faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      fail();
3081faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    } catch (IllegalArgumentException expected) {
3082faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    }
3083faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
3084faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
3085a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  @Test public void veryLargeFixedLengthRequest() throws Exception {
308671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.setBodyLimit(0);
3087a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    server.enqueue(new MockResponse());
3088a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
30893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = client.open(server.getUrl("/"));
3090a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    connection.setDoOutput(true);
3091a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    long contentLength = Integer.MAX_VALUE + 1L;
3092a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    connection.setFixedLengthStreamingMode(contentLength);
3093a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    OutputStream out = connection.getOutputStream();
3094a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    byte[] buffer = new byte[1024 * 1024];
3095a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    for (long bytesWritten = 0; bytesWritten < contentLength; ) {
3096a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      int byteCount = (int) Math.min(buffer.length, contentLength - bytesWritten);
3097a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      out.write(buffer, 0, byteCount);
3098a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      bytesWritten += byteCount;
3099a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    }
3100a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    assertContent("", connection);
3101a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
3102a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    RecordedRequest request = server.takeRequest();
3103a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    assertEquals(Long.toString(contentLength), request.getHeader("Content-Length"));
3104a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  }
3105a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
31063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
31073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * We had a bug where we attempted to gunzip responses that didn't have a
31083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * body. This only came up with 304s since that response code can include
31093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * headers (like "Content-Encoding") without any content to go along with it.
31103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * https://github.com/square/okhttp/issues/358
31113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
31123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void noTransparentGzipFor304NotModified() throws Exception {
31133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse()
31143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .clearHeaders()
31153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED)
31163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("Content-Encoding: gzip"));
31173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse().setBody("b"));
31183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
31193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpURLConnection connection1 = client.open(server.getUrl("/"));
31203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, connection1.getResponseCode());
31213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertContent("", connection1);
31223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
31233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpURLConnection connection2 = client.open(server.getUrl("/"));
31243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(HttpURLConnection.HTTP_OK, connection2.getResponseCode());
31253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertContent("b", connection2);
31263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
31273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    RecordedRequest requestA = server.takeRequest();
31283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(0, requestA.getSequenceNumber());
31293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
31303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    RecordedRequest requestB = server.takeRequest();
31313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(1, requestB.getSequenceNumber());
31323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
31333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
31343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
31353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * We had a bug where we weren't closing Gzip streams on redirects.
31363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * https://github.com/square/okhttp/issues/441
31373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
31383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void gzipWithRedirectAndConnectionReuse() throws Exception {
31393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse()
31403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
31413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("Location: /foo")
31423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .addHeader("Content-Encoding: gzip")
3143e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        .setBody(gzip("Moved! Moved! Moved!")));
31443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse().setBody("This is the new page!"));
31453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
31463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpURLConnection connection = client.open(server.getUrl("/"));
31473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertContent("This is the new page!", connection);
31483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
31493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    RecordedRequest requestA = server.takeRequest();
31503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(0, requestA.getSequenceNumber());
31513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
31523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    RecordedRequest requestB = server.takeRequest();
31533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(1, requestB.getSequenceNumber());
31543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
31553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
31563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
31573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * The RFC is unclear in this regard as it only specifies that this should
31583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * invalidate the cache entry (if any).
31593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
31603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void bodyPermittedOnDelete() throws Exception {
31613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    server.enqueue(new MockResponse());
31623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
31633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    HttpURLConnection connection = client.open(server.getUrl("/"));
31643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.setRequestMethod("DELETE");
31653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.setDoOutput(true);
31663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.getOutputStream().write("BODY".getBytes(UTF_8));
31673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals(200, connection.getResponseCode());
31683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
31693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    RecordedRequest request = server.takeRequest();
31703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    assertEquals("DELETE", request.getMethod());
3171e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("BODY", request.getBody().readUtf8());
3172e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
3173e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
3174e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void userAgentPicksUpHttpAgentSystemProperty() throws Exception {
3175e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setBody("abc"));
3176e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
3177e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    System.setProperty("http.agent", "foo");
3178e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertContent("abc", client.open(server.getUrl("/")));
3179e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
3180e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest request = server.takeRequest();
3181e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("foo", request.getHeader("User-Agent"));
3182e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
3183e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
318471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  /** https://github.com/square/okhttp/issues/891 */
318571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Test public void userAgentSystemPropertyIsNotAscii() throws Exception {
3186e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setBody("abc"));
3187e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
318871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    System.setProperty("http.agent", "a\nb\ud83c\udf69c\ud83c\udf68d\u007fe");
3189e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertContent("abc", client.open(server.getUrl("/")));
3190e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
3191e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    RecordedRequest request = server.takeRequest();
319271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals("a?b?c?d?e", request.getHeader("User-Agent"));
319371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  }
319471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
319571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Test public void userAgentDefaultsToOkHttpVersion() throws Exception {
319671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.enqueue(new MockResponse().setBody("abc"));
319771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
319871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertContent("abc", client.open(server.getUrl("/")));
319971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
320071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    RecordedRequest request = server.takeRequest();
320171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals(Version.userAgent(), request.getHeader("User-Agent"));
3202e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
3203e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
3204e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void interceptorsNotInvoked() throws Exception {
3205e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Interceptor interceptor = new Interceptor() {
3206e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public Response intercept(Chain chain) throws IOException {
3207e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        throw new AssertionError();
3208e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
3209e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    };
3210e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().interceptors().add(interceptor);
3211e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().networkInterceptors().add(interceptor);
3212e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
3213e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    server.enqueue(new MockResponse().setBody("abc"));
3214e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertContent("abc", client.open(server.getUrl("/")));
32153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
32163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
321771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Test public void urlWithSpaceInHost() throws Exception {
321871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    URLConnection urlConnection = client.open(new URL("http://and roid.com/"));
321971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    try {
322071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      urlConnection.getInputStream();
322171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      fail();
322271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    } catch (UnknownHostException expected) {
322371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    }
322471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  }
322571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
322671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Test public void urlWithSpaceInHostViaHttpProxy() throws Exception {
322771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.enqueue(new MockResponse());
322871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    URLConnection urlConnection =
322971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        client.open(new URL("http://and roid.com/"), server.toProxyAddress());
323071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
323171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    try {
323271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      // This test is to check that a NullPointerException is not thrown.
323371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      urlConnection.getInputStream();
323471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      fail(); // the RI makes a bogus proxy request for "GET http://and roid.com/ HTTP/1.1"
323571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    } catch (UnknownHostException expected) {
323671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    }
323771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  }
323871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
323971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Test public void urlHostWithNul() throws Exception {
324071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    URLConnection urlConnection = client.open(new URL("http://host\u0000/"));
324171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    try {
324271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      urlConnection.getInputStream();
324371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      fail();
324471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    } catch (UnknownHostException expected) {
324571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    }
324671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  }
324771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
324871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Test public void urlRedirectToHostWithNul() throws Exception {
324971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    String redirectUrl = "http://host\u0000/";
325071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.enqueue(new MockResponse().setResponseCode(302)
325171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller        .addHeaderLenient("Location", redirectUrl));
325271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
325371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    HttpURLConnection urlConnection = client.open(server.getUrl("/"));
325471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals(302, urlConnection.getResponseCode());
325571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    assertEquals(redirectUrl, urlConnection.getHeaderField("Location"));
325671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  }
325771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
325871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  @Test public void urlWithBadAsciiHost() throws Exception {
325971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    URLConnection urlConnection = client.open(new URL("http://host\u0001/"));
326071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    try {
326171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      urlConnection.getInputStream();
326271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller      fail();
326371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    } catch (UnknownHostException expected) {
326471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    }
326571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  }
326671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller
3267c4c05ff9964ac8bca83bc751abe551f515ca499aNeil Fuller  @Test public void instanceFollowsRedirects() throws Exception {
3268ceafaf318f1176d96d18ed04355f76fa91a8e12bNeil Fuller    testInstanceFollowsRedirects("http://www.google.com/");
3269ceafaf318f1176d96d18ed04355f76fa91a8e12bNeil Fuller    testInstanceFollowsRedirects("https://www.google.com/");
3270ceafaf318f1176d96d18ed04355f76fa91a8e12bNeil Fuller  }
3271ceafaf318f1176d96d18ed04355f76fa91a8e12bNeil Fuller
3272ceafaf318f1176d96d18ed04355f76fa91a8e12bNeil Fuller  private void testInstanceFollowsRedirects(String spec) throws Exception {
3273ceafaf318f1176d96d18ed04355f76fa91a8e12bNeil Fuller    URL url = new URL(spec);
3274ceafaf318f1176d96d18ed04355f76fa91a8e12bNeil Fuller    HttpURLConnection urlConnection = client.open(url);
3275ceafaf318f1176d96d18ed04355f76fa91a8e12bNeil Fuller    urlConnection.setInstanceFollowRedirects(true);
3276ceafaf318f1176d96d18ed04355f76fa91a8e12bNeil Fuller    assertTrue(urlConnection.getInstanceFollowRedirects());
3277ceafaf318f1176d96d18ed04355f76fa91a8e12bNeil Fuller    urlConnection.setInstanceFollowRedirects(false);
3278ceafaf318f1176d96d18ed04355f76fa91a8e12bNeil Fuller    assertFalse(urlConnection.getInstanceFollowRedirects());
3279ceafaf318f1176d96d18ed04355f76fa91a8e12bNeil Fuller  }
3280ceafaf318f1176d96d18ed04355f76fa91a8e12bNeil Fuller
328154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Returns a gzipped copy of {@code bytes}. */
3282e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  public Buffer gzip(String data) throws IOException {
3283e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer result = new Buffer();
3284e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    BufferedSink gzipSink = Okio.buffer(new GzipSink(result));
3285e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    gzipSink.writeUtf8(data);
3286e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    gzipSink.close();
3287e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return result;
328854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
328954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
329054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
329154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Reads at most {@code limit} characters from {@code in} and asserts that
329254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * content equals {@code expected}.
329354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
32943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private void assertContent(String expected, HttpURLConnection connection, int limit)
329554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throws IOException {
329654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection.connect();
329754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertEquals(expected, readAscii(connection.getInputStream(), limit));
329854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
329954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
33003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private void assertContent(String expected, HttpURLConnection connection) throws IOException {
330154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    assertContent(expected, connection, Integer.MAX_VALUE);
330254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
330354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
330454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private Set<String> newSet(String... elements) {
330571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    return new LinkedHashSet<>(Arrays.asList(elements));
330654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
330754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
330854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  enum TransferKind {
330954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    CHUNKED() {
3310e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override void setBody(MockResponse response, Buffer content, int chunkSize)
331154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          throws IOException {
331254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        response.setChunkedBody(content, chunkSize);
331354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
331454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      @Override void setForRequest(HttpURLConnection connection, int contentLength) {
331554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        connection.setChunkedStreamingMode(5);
331654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
331754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    },
331854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    FIXED_LENGTH() {
3319e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override void setBody(MockResponse response, Buffer content, int chunkSize) {
332054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        response.setBody(content);
332154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
332254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      @Override void setForRequest(HttpURLConnection connection, int contentLength) {
3323faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        connection.setFixedLengthStreamingMode(contentLength);
332454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
332554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    },
332654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    END_OF_STREAM() {
3327e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override void setBody(MockResponse response, Buffer content, int chunkSize) {
332854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        response.setBody(content);
332954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        response.setSocketPolicy(DISCONNECT_AT_END);
3330e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        response.removeHeader("Content-Length");
333154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
333254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      @Override void setForRequest(HttpURLConnection connection, int contentLength) {
333354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
333454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    };
333554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
3336e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    abstract void setBody(MockResponse response, Buffer content, int chunkSize) throws IOException;
333754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
333854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    abstract void setForRequest(HttpURLConnection connection, int contentLength);
333954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
334054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    void setBody(MockResponse response, String content, int chunkSize) throws IOException {
3341e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      setBody(response, new Buffer().writeUtf8(content), chunkSize);
334254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
334354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
334454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
334554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  enum ProxyConfig {
334654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    NO_PROXY() {
3347e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public HttpURLConnection connect(
3348e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          MockWebServer server, OkUrlFactory streamHandlerFactory, URL url)
334954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          throws IOException {
3350e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        streamHandlerFactory.client().setProxy(Proxy.NO_PROXY);
3351e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        return streamHandlerFactory.open(url);
335254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
335354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    },
335454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
335554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    CREATE_ARG() {
3356e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public HttpURLConnection connect(
3357e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          MockWebServer server, OkUrlFactory streamHandlerFactory, URL url)
335854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          throws IOException {
3359e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        streamHandlerFactory.client().setProxy(server.toProxyAddress());
3360e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        return streamHandlerFactory.open(url);
336154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
336254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    },
3363c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
336454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    PROXY_SYSTEM_PROPERTY() {
3365e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public HttpURLConnection connect(
3366e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          MockWebServer server, OkUrlFactory streamHandlerFactory, URL url)
336754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          throws IOException {
3368e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        System.setProperty("proxyHost", server.getHostName());
336954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        System.setProperty("proxyPort", Integer.toString(server.getPort()));
3370e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        return streamHandlerFactory.open(url);
337154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
337254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    },
3373c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
337454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HTTP_PROXY_SYSTEM_PROPERTY() {
3375e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public HttpURLConnection connect(
3376e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          MockWebServer server, OkUrlFactory streamHandlerFactory, URL url)
337754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          throws IOException {
3378e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        System.setProperty("http.proxyHost", server.getHostName());
337954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        System.setProperty("http.proxyPort", Integer.toString(server.getPort()));
3380e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        return streamHandlerFactory.open(url);
338154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
338254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    },
3383c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
338454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HTTPS_PROXY_SYSTEM_PROPERTY() {
3385e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public HttpURLConnection connect(
3386e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          MockWebServer server, OkUrlFactory streamHandlerFactory, URL url)
338754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          throws IOException {
3388e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        System.setProperty("https.proxyHost", server.getHostName());
338954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        System.setProperty("https.proxyPort", Integer.toString(server.getPort()));
3390e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        return streamHandlerFactory.open(url);
339154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
339254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    };
3393c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
3394e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    public abstract HttpURLConnection connect(
3395e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        MockWebServer server, OkUrlFactory streamHandlerFactory, URL url)
339654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throws IOException;
339754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
3398c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
339954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static class RecordingTrustManager implements X509TrustManager {
340054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final List<String> calls = new ArrayList<String>();
34016c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    private final X509TrustManager delegate;
34026c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer
34036c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    public RecordingTrustManager(SSLContext sslContext) {
34046c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer      this.delegate = Platform.get().trustManager(sslContext.getSocketFactory());
34056c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer    }
34062231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
340754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    public X509Certificate[] getAcceptedIssuers() {
34086c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer      return delegate.getAcceptedIssuers();
3409c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
3410c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
341154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    public void checkClientTrusted(X509Certificate[] chain, String authType)
341254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throws CertificateException {
341354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      calls.add("checkClientTrusted " + certificatesToString(chain));
3414c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
3415c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
341654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    public void checkServerTrusted(X509Certificate[] chain, String authType)
341754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throws CertificateException {
341854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      calls.add("checkServerTrusted " + certificatesToString(chain));
3419c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
3420c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
342154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private String certificatesToString(X509Certificate[] certificates) {
342254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      List<String> result = new ArrayList<String>();
342354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      for (X509Certificate certificate : certificates) {
342454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        result.add(certificate.getSubjectDN() + " " + certificate.getSerialNumber());
342554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
342654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return result.toString();
3427c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
342854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
34297899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
343054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static class FakeProxySelector extends ProxySelector {
3431e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    List<Proxy> proxies = new ArrayList<>();
34327899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
343354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public List<Proxy> select(URI uri) {
343454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // Don't handle 'socket' schemes, which the RI's Socket class may request (for SOCKS).
343554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return uri.getScheme().equals("http") || uri.getScheme().equals("https") ? proxies
343654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          : Collections.singletonList(Proxy.NO_PROXY);
34377899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
34382231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
343954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
34402231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    }
344154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
34423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
34433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
34443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Tests that use this will fail unless boot classpath is set. Ex. {@code
3445e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * -Xbootclasspath/p:/tmp/alpn-boot-8.0.0.v20140317}
3446e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
3447e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void enableProtocol(Protocol protocol) {
3448e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setSslSocketFactory(sslContext.getSocketFactory());
3449e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setHostnameVerifier(new RecordingHostnameVerifier());
3450e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.client().setProtocols(Arrays.asList(protocol, Protocol.HTTP_1_1));
345171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.useHttps(sslContext.getSocketFactory(), false);
345271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.setProtocolNegotiationEnabled(true);
345371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller    server.setProtocols(client.client().getProtocols());
3454e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
3455e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
3456e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
3457e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Used during tests that involve TLS connection fallback attempts. OkHttp includes the
3458e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * TLS_FALLBACK_SCSV cipher on fallback connections. See
3459e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * {@link com.squareup.okhttp.FallbackTestClientSocketFactory} for details.
34603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
346171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  private void suppressTlsFallbackScsv(OkHttpClient client) {
3462e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    FallbackTestClientSocketFactory clientSocketFactory =
3463e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        new FallbackTestClientSocketFactory(sslContext.getSocketFactory());
3464e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    client.setSslSocketFactory(clientSocketFactory);
34653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
3466c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath}
3467