URLConnectionTest.java revision a468471e8ccc2f642d5b2ae3c75873a74267ece2
10c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson/*
20c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * Copyright (C) 2009 The Android Open Source Project
30c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson *
40c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License");
50c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * you may not use this file except in compliance with the License.
60c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * You may obtain a copy of the License at
70c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson *
80c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson *      http://www.apache.org/licenses/LICENSE-2.0
90c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson *
100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * Unless required by applicable law or agreed to in writing, software
110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * See the License for the specific language governing permissions and
140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson * limitations under the License.
150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson */
160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
17757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilsonpackage libcore.java.net;
180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport com.google.mockwebserver.MockResponse;
200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport com.google.mockwebserver.MockWebServer;
21433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilsonimport com.google.mockwebserver.RecordedRequest;
22c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilsonimport com.google.mockwebserver.SocketPolicy;
230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_END;
240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_START;
250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport static com.google.mockwebserver.SocketPolicy.SHUTDOWN_INPUT_AT_END;
260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport static com.google.mockwebserver.SocketPolicy.SHUTDOWN_OUTPUT_AT_END;
270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.ByteArrayOutputStream;
280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.IOException;
290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.InputStream;
300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.io.OutputStream;
310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.Authenticator;
320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.CacheRequest;
330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.CacheResponse;
340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.ConnectException;
350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.HttpRetryException;
360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.HttpURLConnection;
370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.PasswordAuthentication;
38953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.net.ProtocolException;
39953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.net.Proxy;
400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.ResponseCache;
410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.SocketTimeoutException;
420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.URI;
430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.URL;
440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.net.URLConnection;
450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.security.cert.CertificateException;
46953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.security.cert.X509Certificate;
470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.ArrayList;
48953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.util.Arrays;
49433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilsonimport java.util.Collections;
500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.HashSet;
510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.Iterator;
520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.List;
530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.Map;
540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.Set;
550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.concurrent.atomic.AtomicBoolean;
560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.concurrent.atomic.AtomicReference;
570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.zip.GZIPInputStream;
580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport java.util.zip.GZIPOutputStream;
590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport javax.net.ssl.HostnameVerifier;
600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport javax.net.ssl.HttpsURLConnection;
610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport javax.net.ssl.SSLContext;
62396af46f45fd302e6309fc48ac1fd68cbb739edbJesse Wilsonimport javax.net.ssl.SSLException;
630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport javax.net.ssl.SSLHandshakeException;
64396af46f45fd302e6309fc48ac1fd68cbb739edbJesse Wilsonimport javax.net.ssl.SSLSession;
650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport javax.net.ssl.SSLSocketFactory;
660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport javax.net.ssl.TrustManager;
670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonimport javax.net.ssl.X509TrustManager;
68433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilsonimport junit.framework.TestCase;
69433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilsonimport libcore.java.security.TestKeyStore;
70433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilsonimport libcore.javax.net.ssl.TestSSLContext;
71433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilsonimport tests.net.StuckServer;
72396af46f45fd302e6309fc48ac1fd68cbb739edbJesse Wilson
730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilsonpublic final class URLConnectionTest extends TestCase {
740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private static final Authenticator SIMPLE_AUTHENTICATOR = new Authenticator() {
760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        protected PasswordAuthentication getPasswordAuthentication() {
770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            return new PasswordAuthentication("username", "password".toCharArray());
78433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        }
79433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson    };
800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /** base64("username:password") */
820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private static final String BASE_64_CREDENTIALS = "dXNlcm5hbWU6cGFzc3dvcmQ=";
830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private MockWebServer server = new MockWebServer();
850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private String hostName;
860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    @Override protected void setUp() throws Exception {
880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        super.setUp();
890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        hostName = server.getHostName();
900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    @Override protected void tearDown() throws Exception {
930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        ResponseCache.setDefault(null);
940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        Authenticator.setDefault(null);
950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        System.clearProperty("proxyHost");
960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        System.clearProperty("proxyPort");
970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        System.clearProperty("http.proxyHost");
980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        System.clearProperty("http.proxyPort");
990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        System.clearProperty("https.proxyHost");
1000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        System.clearProperty("https.proxyPort");
1010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.shutdown();
1020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        super.tearDown();
10384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
1040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testRequestHeaders() throws IOException, InterruptedException {
1060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse());
1070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
1080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection();
1100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        urlConnection.addRequestProperty("D", "e");
1110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        urlConnection.addRequestProperty("D", "f");
1120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("f", urlConnection.getRequestProperty("D"));
1130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("f", urlConnection.getRequestProperty("d"));
1140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        Map<String, List<String>> requestHeaders = urlConnection.getRequestProperties();
1150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(newSet("e", "f"), new HashSet<String>(requestHeaders.get("D")));
1160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(newSet("e", "f"), new HashSet<String>(requestHeaders.get("d")));
1170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
1180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            requestHeaders.put("G", Arrays.asList("h"));
1190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail("Modified an unmodifiable view.");
1200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (UnsupportedOperationException expected) {
1210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
1230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            requestHeaders.get("D").add("i");
1240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail("Modified an unmodifiable view.");
1250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (UnsupportedOperationException expected) {
1260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
1280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            urlConnection.setRequestProperty(null, "j");
1290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail();
1300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (NullPointerException expected) {
1310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
1330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            urlConnection.addRequestProperty(null, "k");
1340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail();
1350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (NullPointerException expected) {
1360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        urlConnection.setRequestProperty("NullValue", null); // should fail silently!
1380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNull(urlConnection.getRequestProperty("NullValue"));
1390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        urlConnection.addRequestProperty("AnotherNullValue", null);  // should fail silently!
1400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNull(urlConnection.getRequestProperty("AnotherNullValue"));
1410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        urlConnection.getResponseCode();
1430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest request = server.takeRequest();
1440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContains(request.getHeaders(), "D: e");
1450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContains(request.getHeaders(), "D: f");
1460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContainsNoneMatching(request.getHeaders(), "NullValue.*");
1470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContainsNoneMatching(request.getHeaders(), "AnotherNullValue.*");
1480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContainsNoneMatching(request.getHeaders(), "G:.*");
1490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContainsNoneMatching(request.getHeaders(), "null:.*");
1500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
1520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            urlConnection.addRequestProperty("N", "o");
1530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail("Set header after connect");
1540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (IllegalStateException expected) {
1550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
1560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
1570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            urlConnection.setRequestProperty("P", "q");
1580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail("Set header after connect");
1590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (IllegalStateException expected) {
16084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        }
1610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
162433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            urlConnection.getRequestProperties();
163433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            fail();
164433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        } catch (IllegalStateException expected) {
165433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        }
166433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson    }
167433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson
168433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson    public void testGetRequestPropertyReturnsLastValue() throws Exception {
169433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        server.play();
1700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection();
1710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        urlConnection.addRequestProperty("A", "value1");
1720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        urlConnection.addRequestProperty("A", "value2");
1730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("value2", urlConnection.getRequestProperty("A"));
1740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
1750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponseHeaders() throws IOException, InterruptedException {
1770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
1780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setStatus("HTTP/1.0 200 Fantastic")
1790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("A: c")
1800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("B: d")
1810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("A: e")
1820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setChunkedBody("ABCDE\nFGHIJ\nKLMNO\nPQR", 8));
1830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
1840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
1850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection();
1860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(200, urlConnection.getResponseCode());
1870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("Fantastic", urlConnection.getResponseMessage());
1880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("HTTP/1.0 200 Fantastic", urlConnection.getHeaderField(null));
1890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        Map<String, List<String>> responseHeaders = urlConnection.getHeaderFields();
1900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(Arrays.asList("HTTP/1.0 200 Fantastic"), responseHeaders.get(null));
1910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(newSet("c", "e"), new HashSet<String>(responseHeaders.get("A")));
1920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(newSet("c", "e"), new HashSet<String>(responseHeaders.get("a")));
1930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
194953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            responseHeaders.put("N", Arrays.asList("o"));
195953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            fail("Modified an unmodifiable view.");
196953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        } catch (UnsupportedOperationException expected) {
197953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
198953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        try {
199953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            responseHeaders.get("A").add("f");
200953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            fail("Modified an unmodifiable view.");
201953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        } catch (UnsupportedOperationException expected) {
202953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
203953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", urlConnection.getHeaderFieldKey(0));
2040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("c", urlConnection.getHeaderField(0));
2050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("B", urlConnection.getHeaderFieldKey(1));
2060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("d", urlConnection.getHeaderField(1));
2070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("A", urlConnection.getHeaderFieldKey(2));
2080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("e", urlConnection.getHeaderField(2));
2090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testGetErrorStreamOnSuccessfulRequest() throws Exception {
2120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("A"));
2130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
2140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
2150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNull(connection.getErrorStream());
2160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testGetErrorStreamOnUnsuccessfulRequest() throws Exception {
2190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(404).setBody("A"));
2200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
2210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
222953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("A", readAscii(connection.getErrorStream(), Integer.MAX_VALUE));
2230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    // Check that if we don't read to the end of a response, the next request on the
2260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    // recycled connection doesn't get the unread tail of the first request's response.
2270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    // http://code.google.com/p/android/issues/detail?id=2939
2280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void test_2939() throws Exception {
2290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        MockResponse response = new MockResponse().setChunkedBody("ABCDE\nFGHIJ\nKLMNO\nPQR", 8);
2300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
2320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
2330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
2340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContent("ABCDE", server.getUrl("/").openConnection(), 5);
2360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContent("ABCDE", server.getUrl("/").openConnection(), 5);
2370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    // Check that we recognize a few basic mime types by extension.
2400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    // http://code.google.com/p/android/issues/detail?id=10100
2410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void test_10100() throws Exception {
2420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("image/jpeg", URLConnection.guessContentTypeFromName("someFile.jpg"));
2430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("application/pdf", URLConnection.guessContentTypeFromName("stuff.pdf"));
2440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testConnectionsArePooled() throws Exception {
2470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        MockResponse response = new MockResponse().setBody("ABCDEFGHIJKLMNOPQR");
2480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
2500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
2510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
2520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
2530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/foo").openConnection());
2550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(0, server.takeRequest().getSequenceNumber());
2560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/bar?baz=quux").openConnection());
2570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(1, server.takeRequest().getSequenceNumber());
2580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/z").openConnection());
2590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(2, server.takeRequest().getSequenceNumber());
2600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
261ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson
262ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson    public void testChunkedConnectionsArePooled() throws Exception {
2630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        MockResponse response = new MockResponse().setChunkedBody("ABCDEFGHIJKLMNOPQR", 5);
2640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
2660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
2670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
2680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
2690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/foo").openConnection());
2710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(0, server.takeRequest().getSequenceNumber());
272433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/bar?baz=quux").openConnection());
273ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(1, server.takeRequest().getSequenceNumber());
274ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/z").openConnection());
275ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(2, server.takeRequest().getSequenceNumber());
276ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson    }
2770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testServerClosesSocket() throws Exception {
2790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testServerClosesOutput(DISCONNECT_AT_END);
2800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testServerShutdownInput() throws Exception {
2830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testServerClosesOutput(SHUTDOWN_INPUT_AT_END);
2840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testServerShutdownOutput() throws Exception {
2870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testServerClosesOutput(SHUTDOWN_OUTPUT_AT_END);
2880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
2890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testServerClosesOutput(SocketPolicy socketPolicy) throws Exception {
2910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
2920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("This connection won't pool properly")
2930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setSocketPolicy(socketPolicy));
2940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
2950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("This comes after a busted connection"));
2960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
2970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
2980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContent("This connection won't pool properly", server.getUrl("/a").openConnection());
2990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(0, server.takeRequest().getSequenceNumber());
3000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContent("This comes after a busted connection", server.getUrl("/b").openConnection());
3010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // sequence number 0 means the HTTP socket connection was not reused
3020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(0, server.takeRequest().getSequenceNumber());
303ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson    }
304ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson
305ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson    enum WriteKind { BYTE_BY_BYTE, SMALL_BUFFERS, LARGE_BUFFERS }
3060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void test_chunkedUpload_byteByByte() throws Exception {
3080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        doUpload(TransferKind.CHUNKED, WriteKind.BYTE_BY_BYTE);
3090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
3100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void test_chunkedUpload_smallBuffers() throws Exception {
3120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        doUpload(TransferKind.CHUNKED, WriteKind.SMALL_BUFFERS);
3130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
3140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void test_chunkedUpload_largeBuffers() throws Exception {
3160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        doUpload(TransferKind.CHUNKED, WriteKind.LARGE_BUFFERS);
3170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
3180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void test_fixedLengthUpload_byteByByte() throws Exception {
3200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        doUpload(TransferKind.FIXED_LENGTH, WriteKind.BYTE_BY_BYTE);
3210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
3220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void test_fixedLengthUpload_smallBuffers() throws Exception {
3240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        doUpload(TransferKind.FIXED_LENGTH, WriteKind.SMALL_BUFFERS);
3250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
3260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void test_fixedLengthUpload_largeBuffers() throws Exception {
3280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        doUpload(TransferKind.FIXED_LENGTH, WriteKind.LARGE_BUFFERS);
3290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
3300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void doUpload(TransferKind uploadKind, WriteKind writeKind) throws Exception {
332a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        int n = 512*1024;
3330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.setBodyLimit(0);
3340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse());
3350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
3360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection conn = (HttpURLConnection) server.getUrl("/").openConnection();
3380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        conn.setDoOutput(true);
3390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        conn.setRequestMethod("POST");
3400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        if (uploadKind == TransferKind.CHUNKED) {
3410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            conn.setChunkedStreamingMode(-1);
3420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } else {
3430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            conn.setFixedLengthStreamingMode(n);
3440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
345953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        OutputStream out = conn.getOutputStream();
3460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        if (writeKind == WriteKind.BYTE_BY_BYTE) {
3470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            for (int i = 0; i < n; ++i) {
348953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                out.write('x');
3490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
3500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } else {
351ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson            byte[] buf = new byte[writeKind == WriteKind.SMALL_BUFFERS ? 256 : 64*1024];
352ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson            Arrays.fill(buf, (byte) 'x');
353ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson            for (int i = 0; i < n; i += buf.length) {
3540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                out.write(buf, 0, Math.min(buf.length, n - i));
3550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
356a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        }
357a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        out.close();
358a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals(200, conn.getResponseCode());
359a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        RecordedRequest request = server.takeRequest();
360a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals(n, request.getBodySize());
361a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        if (uploadKind == TransferKind.CHUNKED) {
362a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson            assertTrue(request.getChunkSizes().size() > 0);
363a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        } else {
364a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson            assertTrue(request.getChunkSizes().isEmpty());
365a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        }
366a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson    }
367a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
368a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson    public void testGetResponseCodeNoResponseBody() throws Exception {
369a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        server.enqueue(new MockResponse()
370a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson                .addHeader("abc: def"));
371a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        server.play();
372a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
373a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        URL url = server.getUrl("/");
374a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
375a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        conn.setDoInput(false);
376a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals("def", conn.getHeaderField("abc"));
377a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        assertEquals(200, conn.getResponseCode());
378a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        try {
379a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson            conn.getInputStream();
380a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson            fail();
381a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        } catch (ProtocolException expected) {
382a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        }
3830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
3840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testConnectViaHttps() throws IOException, InterruptedException {
3860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
3870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
3890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("this response comes via HTTPS"));
3900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
3910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/foo").openConnection();
3930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
3940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContent("this response comes via HTTPS", connection);
3960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
3970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest request = server.takeRequest();
3980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("GET /foo HTTP/1.1", request.getRequestLine());
3990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testConnectViaHttpsReusingConnections() throws IOException, InterruptedException {
4020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
4030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
4050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("this response comes via HTTPS"));
406ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        server.enqueue(new MockResponse().setBody("another response via HTTPS"));
407ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        server.play();
4080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
4100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
4110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContent("this response comes via HTTPS", connection);
4120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection = (HttpsURLConnection) server.getUrl("/").openConnection();
4140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
4150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContent("another response via HTTPS", connection);
4160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(0, server.takeRequest().getSequenceNumber());
4180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(1, server.takeRequest().getSequenceNumber());
4190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testConnectViaHttpsReusingConnectionsDifferentFactories()
4220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            throws IOException, InterruptedException {
4230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
4240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
4260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("this response comes via HTTPS"));
4270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("another response via HTTPS"));
4280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
4290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // install a custom SSL socket factory so the server can be authorized
4310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
4320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
4330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContent("this response comes via HTTPS", connection);
4340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection = (HttpsURLConnection) server.getUrl("/").openConnection();
4360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
4370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            readAscii(connection.getInputStream(), Integer.MAX_VALUE);
4380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail("without an SSL socket factory, the connection should fail");
4390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (SSLException expected) {
4400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
4410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testConnectViaHttpsWithSSLFallback() throws IOException, InterruptedException {
4440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
4450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
4470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setSocketPolicy(DISCONNECT_AT_START));
4480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("this response comes via SSL"));
4490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
4500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/foo").openConnection();
4520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
4530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContent("this response comes via SSL", connection);
4550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest request = server.takeRequest();
4570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("GET /foo HTTP/1.1", request.getRequestLine());
4580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
4610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Verify that we don't retry connections on certificate verification errors.
4620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     *
4630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * http://code.google.com/p/android/issues/detail?id=13178
4640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
465433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson    public void testConnectViaHttpsToUntrustedServer() throws IOException, InterruptedException {
466433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create(TestKeyStore.getClientCA2(),
4670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                                                              TestKeyStore.getServer());
4680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
469ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
470ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        server.enqueue(new MockResponse()); // unused
4710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
4720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
473ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/foo").openConnection();
474ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
4750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
4760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            connection.getInputStream();
4770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail();
4780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (SSLHandshakeException expected) {
4790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertTrue(expected.getCause() instanceof CertificateException);
4800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
4810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(0, server.getRequestCount());
4820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testConnectViaProxyUsingProxyArg() throws Exception {
4850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testConnectViaProxy(ProxyConfig.CREATE_ARG);
4860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testConnectViaProxyUsingProxySystemProperty() throws Exception {
4890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testConnectViaProxy(ProxyConfig.PROXY_SYSTEM_PROPERTY);
4900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testConnectViaProxyUsingHttpProxySystemProperty() throws Exception {
4930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testConnectViaProxy(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY);
4940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
4950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
4960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testConnectViaProxy(ProxyConfig proxyConfig) throws Exception {
4970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        MockResponse mockResponse = new MockResponse().setBody("this response comes via a proxy");
4980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(mockResponse);
4990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
5000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
5010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = new URL("http://android.com/foo");
5020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection connection = proxyConfig.connect(server, url);
5030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContent("this response comes via a proxy", connection);
5040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
5050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest request = server.takeRequest();
506ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals("GET http://android.com/foo HTTP/1.1", request.getRequestLine());
507ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertContains(request.getHeaders(), "Host: android.com");
5080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
50984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
510ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson    public void testContentDisagreesWithContentLengthHeader() throws IOException {
511ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        server.enqueue(new MockResponse()
5120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("abc\r\nYOU SHOULD NOT SEE THIS")
5130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .clearHeaders()
514953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("Content-Length: 3"));
51521dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.play();
51621dddca4064527116af7a1553de502c6d11138daJesse Wilson
51721dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertContent("abc", server.getUrl("/").openConnection());
51821dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
519953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
52021dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testContentDisagreesWithChunkedHeader() throws IOException {
521953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        MockResponse mockResponse = new MockResponse();
522953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        mockResponse.setChunkedBody("abc", 3);
523953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
524953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        bytesOut.write(mockResponse.getBody());
525953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        bytesOut.write("\r\nYOU SHOULD NOT SEE THIS".getBytes());
526953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        mockResponse.setBody(bytesOut.toByteArray());
527953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        mockResponse.clearHeaders();
528953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        mockResponse.addHeader("Transfer-encoding: chunked");
529953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
53021dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(mockResponse);
53121dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.play();
53221dddca4064527116af7a1553de502c6d11138daJesse Wilson
53321dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertContent("abc", server.getUrl("/").openConnection());
53421dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
53521dddca4064527116af7a1553de502c6d11138daJesse Wilson
53621dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testConnectViaHttpProxyToHttpsUsingProxyArgWithNoProxy() throws Exception {
53721dddca4064527116af7a1553de502c6d11138daJesse Wilson        testConnectViaDirectProxyToHttps(ProxyConfig.NO_PROXY);
5380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
53921dddca4064527116af7a1553de502c6d11138daJesse Wilson
54021dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testConnectViaHttpProxyToHttpsUsingHttpProxySystemProperty() throws Exception {
5410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // https should not use http proxy
5420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testConnectViaDirectProxyToHttps(ProxyConfig.HTTP_PROXY_SYSTEM_PROPERTY);
5430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
545953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private void testConnectViaDirectProxyToHttps(ProxyConfig proxyConfig) throws Exception {
546953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
547953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
548953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
549953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse().setBody("this response comes via HTTPS"));
550953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.play();
551953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
552953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URL url = server.getUrl("/foo");
553953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) proxyConfig.connect(server, url);
554953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
555953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
556953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertContent("this response comes via HTTPS", connection);
557953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
558953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        RecordedRequest request = server.takeRequest();
559953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("GET /foo HTTP/1.1", request.getRequestLine());
560953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
561953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
562953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
56321dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testConnectViaHttpProxyToHttpsUsingProxyArg() throws Exception {
56421dddca4064527116af7a1553de502c6d11138daJesse Wilson        testConnectViaHttpProxyToHttps(ProxyConfig.CREATE_ARG);
56521dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
56621dddca4064527116af7a1553de502c6d11138daJesse Wilson
56721dddca4064527116af7a1553de502c6d11138daJesse Wilson    /**
56821dddca4064527116af7a1553de502c6d11138daJesse Wilson     * We weren't honoring all of the appropriate proxy system properties when
56921dddca4064527116af7a1553de502c6d11138daJesse Wilson     * connecting via HTTPS. http://b/3097518
57021dddca4064527116af7a1553de502c6d11138daJesse Wilson     */
57121dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testConnectViaHttpProxyToHttpsUsingProxySystemProperty() throws Exception {
57221dddca4064527116af7a1553de502c6d11138daJesse Wilson        testConnectViaHttpProxyToHttps(ProxyConfig.PROXY_SYSTEM_PROPERTY);
57321dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
57421dddca4064527116af7a1553de502c6d11138daJesse Wilson
57521dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testConnectViaHttpProxyToHttpsUsingHttpsProxySystemProperty() throws Exception {
5760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testConnectViaHttpProxyToHttps(ProxyConfig.HTTPS_PROXY_SYSTEM_PROPERTY);
5770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
5780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
5790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
5800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * We were verifying the wrong hostname when connecting to an HTTPS site
5810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * through a proxy. http://b/3097277
5820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
5830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testConnectViaHttpProxyToHttps(ProxyConfig proxyConfig) throws Exception {
5840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
5850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier();
5860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
5870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), true);
5880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
5890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END)
5900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .clearHeaders());
5910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("this response comes via a secure proxy"));
5920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
5930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
5940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = new URL("https://android.com/foo");
5950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) proxyConfig.connect(server, url);
5960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
5970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setHostnameVerifier(hostnameVerifier);
5980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
5990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContent("this response comes via a secure proxy", connection);
6000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest connect = server.takeRequest();
6020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("Connect line failure on proxy",
6030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                "CONNECT android.com:443 HTTP/1.1", connect.getRequestLine());
6040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContains(connect.getHeaders(), "Host: android.com");
6050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest get = server.takeRequest();
6070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("GET /foo HTTP/1.1", get.getRequestLine());
6080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContains(get.getHeaders(), "Host: android.com");
6090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls);
6100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
61384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson     * Test which headers are sent unencrypted to the HTTP proxy.
61484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson     */
61584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testProxyConnectIncludesProxyHeadersOnly()
61684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            throws IOException, InterruptedException {
6170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier();
6180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
6190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), true);
6210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
6220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END)
6230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .clearHeaders());
6240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("encrypted response from the origin server"));
6250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
6260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = new URL("https://android.com/foo");
6280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(
6290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                server.toProxyAddress());
6300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.addRequestProperty("Private", "Secret");
6310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.addRequestProperty("Proxy-Authorization", "bar");
6320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.addRequestProperty("User-Agent", "baz");
633adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
634adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        connection.setHostnameVerifier(hostnameVerifier);
635adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertContent("encrypted response from the origin server", connection);
636adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
637adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        RecordedRequest connect = server.takeRequest();
638adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertContainsNoneMatching(connect.getHeaders(), "Private.*");
639adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertContains(connect.getHeaders(), "Proxy-Authorization: bar");
640adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertContains(connect.getHeaders(), "User-Agent: baz");
641adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertContains(connect.getHeaders(), "Host: android.com");
642adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertContains(connect.getHeaders(), "Proxy-Connection: Keep-Alive");
643adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
644adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        RecordedRequest get = server.takeRequest();
645adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertContains(get.getHeaders(), "Private: Secret");
64621dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls);
64721dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
64821dddca4064527116af7a1553de502c6d11138daJesse Wilson
64921dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testProxyAuthenticateOnConnect() throws Exception {
65021dddca4064527116af7a1553de502c6d11138daJesse Wilson        Authenticator.setDefault(SIMPLE_AUTHENTICATOR);
65121dddca4064527116af7a1553de502c6d11138daJesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
65221dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), true);
65321dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse()
65421dddca4064527116af7a1553de502c6d11138daJesse Wilson                .setResponseCode(407)
65521dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("Proxy-Authenticate: Basic realm=\"localhost\""));
65621dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse()
65721dddca4064527116af7a1553de502c6d11138daJesse Wilson                .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END)
65821dddca4064527116af7a1553de502c6d11138daJesse Wilson                .clearHeaders());
65921dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse().setBody("A"));
6600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
6610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = new URL("https://android.com/foo");
6630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(
6640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                server.toProxyAddress());
6650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
6660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setHostnameVerifier(new RecordingHostnameVerifier());
6670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContent("A", connection);
6680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest connect1 = server.takeRequest();
6700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("CONNECT android.com:443 HTTP/1.1", connect1.getRequestLine());
6710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContainsNoneMatching(connect1.getHeaders(), "Proxy\\-Authorization.*");
6720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest connect2 = server.takeRequest();
6740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("CONNECT android.com:443 HTTP/1.1", connect2.getRequestLine());
6750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContains(connect2.getHeaders(), "Proxy-Authorization: Basic " + BASE_64_CREDENTIALS);
6760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest get = server.takeRequest();
6780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("GET /foo HTTP/1.1", get.getRequestLine());
6790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContainsNoneMatching(get.getHeaders(), "Proxy\\-Authorization.*");
6800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testDisconnectedConnection() throws IOException {
6830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("ABCDEFGHIJKLMNOPQR"));
6840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
6850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
6870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        InputStream in = connection.getInputStream();
6880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals('A', (char) in.read());
6890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.disconnect();
6900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
6910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            in.read();
6920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail("Expected a connection closed exception");
6930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (IOException expected) {
6940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
6950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
6960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
6970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testDisconnectBeforeConnect() throws IOException {
6980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("A"));
6990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
7000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
7020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.disconnect();
7030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContent("A", connection);
7050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(200, connection.getResponseCode());
7060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testDefaultRequestProperty() throws Exception {
709953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URLConnection.setDefaultRequestProperty("X-testSetDefaultRequestProperty", "A");
7100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNull(URLConnection.getDefaultRequestProperty("X-setDefaultRequestProperty"));
7110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
7140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Reads {@code count} characters from the stream. If the stream is
7150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * exhausted before {@code count} characters can be read, the remaining
7160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * characters are returned and the stream is closed.
7170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
7180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private String readAscii(InputStream in, int count) throws IOException {
7190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        StringBuilder result = new StringBuilder();
7200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        for (int i = 0; i < count; i++) {
7210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            int value = in.read();
7220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            if (value == -1) {
7230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                in.close();
7240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                break;
7250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            }
7260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            result.append((char) value);
7270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
7280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        return result.toString();
7290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMarkAndResetWithContentLengthHeader() throws IOException {
7320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testMarkAndReset(TransferKind.FIXED_LENGTH);
7330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMarkAndResetWithChunkedEncoding() throws IOException {
7360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testMarkAndReset(TransferKind.CHUNKED);
7370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMarkAndResetWithNoLengthHeaders() throws IOException {
7400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testMarkAndReset(TransferKind.END_OF_STREAM);
7410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testMarkAndReset(TransferKind transferKind) throws IOException {
7440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        MockResponse response = new MockResponse();
7450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        transferKind.setBody(response, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1024);
7460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
7470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
7480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
7490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        InputStream in = server.getUrl("/").openConnection().getInputStream();
7510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertFalse("This implementation claims to support mark().", in.markSupported());
7520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        in.mark(5);
7530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCDE", readAscii(in, 5));
7540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
7550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            in.reset();
7560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail();
7570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (IOException expected) {
7580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
7590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("FGHIJKLMNOPQRSTUVWXYZ", readAscii(in, Integer.MAX_VALUE));
7600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContent("ABCDEFGHIJKLMNOPQRSTUVWXYZ", server.getUrl("/").openConnection());
7610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
7640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * We've had a bug where we forget the HTTP response when we see response
7650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * code 401. This causes a new HTTP request to be issued for every call into
7660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * the URLConnection.
7670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
7680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testUnauthorizedResponseHandling() throws IOException {
7690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        MockResponse response = new MockResponse()
7700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("WWW-Authenticate: challenge")
7710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setResponseCode(401) // UNAUTHORIZED
7720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("Unauthorized");
7730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
7740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
7750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(response);
7760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
7770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URL url = server.getUrl("/");
7790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
7800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(401, conn.getResponseCode());
7820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(401, conn.getResponseCode());
7830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(401, conn.getResponseCode());
7840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(1, server.getRequestCount());
7850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
7860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testNonHexChunkSize() throws IOException {
7880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
7890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("5\r\nABCDE\r\nG\r\nFGHIJKLMNOPQRSTU\r\n0\r\n\r\n")
7900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .clearHeaders()
7910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Transfer-encoding: chunked"));
7920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
7930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
7940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
7950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
7960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            readAscii(connection.getInputStream(), Integer.MAX_VALUE);
7970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail();
7980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (IOException e) {
7990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
8000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testMissingChunkBody() throws IOException {
8030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
8040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("5")
8050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .clearHeaders()
8060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Transfer-encoding: chunked")
8070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setSocketPolicy(DISCONNECT_AT_END));
8080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
8090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
8110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
8120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            readAscii(connection.getInputStream(), Integer.MAX_VALUE);
8130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail();
8140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (IOException e) {
8150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
8160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
8190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * This test checks whether connections are gzipped by default. This
8200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * behavior in not required by the API, so a failure of this test does not
8210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * imply a bug in the implementation.
8220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
8230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testGzipEncodingEnabledByDefault() throws IOException, InterruptedException {
8240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
8250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody(gzip("ABCABCABC".getBytes("UTF-8")))
8260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Content-Encoding: gzip"));
8270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
8280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
8300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCABCABC", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
8310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertNull(connection.getContentEncoding());
8320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest request = server.takeRequest();
8340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContains(request.getHeaders(), "Accept-Encoding: gzip");
8350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testClientConfiguredGzipContentEncoding() throws Exception {
8380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
8390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody(gzip("ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes("UTF-8")))
8400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Content-Encoding: gzip"));
8410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
8420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
8440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.addRequestProperty("Accept-Encoding", "gzip");
8450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        InputStream gunzippedIn = new GZIPInputStream(connection.getInputStream());
8460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZ", readAscii(gunzippedIn, Integer.MAX_VALUE));
8470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest request = server.takeRequest();
8490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContains(request.getHeaders(), "Accept-Encoding: gzip");
8500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testGzipAndConnectionReuseWithFixedLength() throws Exception {
8530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.FIXED_LENGTH);
8540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testGzipAndConnectionReuseWithChunkedEncoding() throws Exception {
8570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.CHUNKED);
8580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testClientConfiguredCustomContentEncoding() throws Exception {
8610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
8620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("ABCDE")
8630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Content-Encoding: custom"));
8640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
8650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
8670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.addRequestProperty("Accept-Encoding", "custom");
8680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABCDE", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
8690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest request = server.takeRequest();
8710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContains(request.getHeaders(), "Accept-Encoding: custom");
8720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
8730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
8740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    /**
8750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * Test a bug where gzip input streams weren't exhausting the input stream,
8760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * which corrupted the request that followed.
8770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     * http://code.google.com/p/android/issues/detail?id=7059
8780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson     */
8790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testClientConfiguredGzipContentEncodingAndConnectionReuse(
880adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson            TransferKind transferKind) throws Exception {
881adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        MockResponse responseOne = new MockResponse();
882adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        responseOne.addHeader("Content-Encoding: gzip");
883adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        transferKind.setBody(responseOne, gzip("one (gzipped)".getBytes("UTF-8")), 5);
884adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.enqueue(responseOne);
885adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        MockResponse responseTwo = new MockResponse();
886adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        transferKind.setBody(responseTwo, "two (identity)", 5);
887adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.enqueue(responseTwo);
888adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.play();
889adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
890adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
891adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        connection.addRequestProperty("Accept-Encoding", "gzip");
892adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        InputStream gunzippedIn = new GZIPInputStream(connection.getInputStream());
893adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals("one (gzipped)", readAscii(gunzippedIn, Integer.MAX_VALUE));
894adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals(0, server.takeRequest().getSequenceNumber());
895adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
896adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        connection = server.getUrl("/").openConnection();
897adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals("two (identity)", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
898adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        assertEquals(1, server.takeRequest().getSequenceNumber());
899adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    }
900adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson
901adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    /**
902adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson     * Obnoxiously test that the chunk sizes transmitted exactly equal the
903adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson     * requested data+chunk header size. Although setChunkedStreamingMode()
904adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson     * isn't specific about whether the size applies to the data or the
905adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson     * complete chunk, the RI interprets it as a complete chunk.
906adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson     */
907adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson    public void testSetChunkedStreamingMode() throws IOException, InterruptedException {
908adb64fbba2b781467e055706c3de0873dfc01166Jesse Wilson        server.enqueue(new MockResponse());
909c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
910c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
911c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection();
912c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        urlConnection.setChunkedStreamingMode(8);
913c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        urlConnection.setDoOutput(true);
914c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        OutputStream outputStream = urlConnection.getOutputStream();
915c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        outputStream.write("ABCDEFGHIJKLMNOPQ".getBytes("US-ASCII"));
916c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals(200, urlConnection.getResponseCode());
917c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
918c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        RecordedRequest request = server.takeRequest();
919c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals("ABCDEFGHIJKLMNOPQ", new String(request.getBody(), "US-ASCII"));
920c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals(Arrays.asList(3, 3, 3, 3, 3, 2), request.getChunkSizes());
921953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
922953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
923c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testAuthenticateWithFixedLengthStreaming() throws Exception {
924c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        testAuthenticateWithStreamingPost(StreamingMode.FIXED_LENGTH);
9252f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    }
9262f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
9272f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    public void testAuthenticateWithChunkedStreaming() throws Exception {
9282f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        testAuthenticateWithStreamingPost(StreamingMode.CHUNKED);
9292f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    }
9302f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
9312f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    private void testAuthenticateWithStreamingPost(StreamingMode streamingMode) throws Exception {
9322f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        MockResponse pleaseAuthenticate = new MockResponse()
9332f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .setResponseCode(401)
9342f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .addHeader("WWW-Authenticate: Basic realm=\"protected area\"")
9352f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .setBody("Please authenticate.");
9362f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(pleaseAuthenticate);
9372f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.play();
9382f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
939c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        Authenticator.setDefault(SIMPLE_AUTHENTICATOR);
940c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
941c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.setDoOutput(true);
942c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        byte[] requestBody = { 'A', 'B', 'C', 'D' };
943953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (streamingMode == StreamingMode.FIXED_LENGTH) {
944c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson            connection.setFixedLengthStreamingMode(requestBody.length);
945c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        } else if (streamingMode == StreamingMode.CHUNKED) {
946c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson            connection.setChunkedStreamingMode(0);
947c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        }
948c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        OutputStream outputStream = connection.getOutputStream();
949c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        outputStream.write(requestBody);
950c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        outputStream.close();
951c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        try {
952c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson            connection.getInputStream();
953c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson            fail();
954c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        } catch (HttpRetryException expected) {
955c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        }
956c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
957c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        // no authorization header for the request...
958c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        RecordedRequest request = server.takeRequest();
959c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertContainsNoneMatching(request.getHeaders(), "Authorization: Basic .*");
960c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertEquals(Arrays.toString(requestBody), Arrays.toString(request.getBody()));
961c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
962c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
963c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testSetValidRequestMethod() throws Exception {
964c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
965c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertValidRequestMethod("GET");
966c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertValidRequestMethod("DELETE");
967953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertValidRequestMethod("HEAD");
968c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertValidRequestMethod("OPTIONS");
969c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertValidRequestMethod("POST");
970c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertValidRequestMethod("PUT");
971c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        assertValidRequestMethod("TRACE");
972c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
973c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
974c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    private void assertValidRequestMethod(String requestMethod) throws Exception {
975c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
976c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.setRequestMethod(requestMethod);
977953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals(requestMethod, connection.getRequestMethod());
978c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    }
979c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
980c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testSetInvalidRequestMethodLowercase() throws Exception {
981c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        server.play();
98284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertInvalidRequestMethod("get");
98384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
98484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
98584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testSetInvalidRequestMethodConnect() throws Exception {
98684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
98784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertInvalidRequestMethod("CONNECT");
98884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
98984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
99084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    private void assertInvalidRequestMethod(String requestMethod) throws Exception {
99184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
99284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        try {
99384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            connection.setRequestMethod(requestMethod);
99484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            fail();
99584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        } catch (ProtocolException expected) {
99684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        }
99784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
99884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
99984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testCannotSetNegativeFixedLengthStreamingMode() throws Exception {
100084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
100184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
100284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        try {
100384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            connection.setFixedLengthStreamingMode(-2);
100484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            fail();
100584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        } catch (IllegalArgumentException expected) {
100684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        }
100784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
100884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
100984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testCanSetNegativeChunkedStreamingMode() throws Exception {
101084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
101184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
101284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.setChunkedStreamingMode(-2);
101384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
101484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
101584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testCannotSetFixedLengthStreamingModeAfterConnect() throws Exception {
101684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse().setBody("A"));
101784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
101884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
101984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
102084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        try {
102184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            connection.setFixedLengthStreamingMode(1);
102284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            fail();
102384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        } catch (IllegalStateException expected) {
102484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        }
102584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
102684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
102784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testCannotSetChunkedStreamingModeAfterConnect() throws Exception {
102884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse().setBody("A"));
102984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
103084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
103184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
103284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        try {
103384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            connection.setChunkedStreamingMode(1);
103484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            fail();
103584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        } catch (IllegalStateException expected) {
103684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        }
103784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
103884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
103984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testCannotSetFixedLengthStreamingModeAfterChunkedStreamingMode() throws Exception {
104084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
104184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
104284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.setChunkedStreamingMode(1);
104384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        try {
104484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            connection.setFixedLengthStreamingMode(1);
104584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            fail();
104684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        } catch (IllegalStateException expected) {
104784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        }
104884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    }
104984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
105084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    public void testCannotSetChunkedStreamingModeAfterFixedLengthStreamingMode() throws Exception {
105184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
105284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
105384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.setFixedLengthStreamingMode(1);
105484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        try {
105584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            connection.setChunkedStreamingMode(1);
105684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            fail();
10579531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        } catch (IllegalStateException expected) {
10589531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        }
10599531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    }
10609531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson
10619531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    public void testSecureFixedLengthStreaming() throws Exception {
10629531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        testSecureStreamingPost(StreamingMode.FIXED_LENGTH);
10639531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    }
10649531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson
10659531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    public void testSecureChunkedStreaming() throws Exception {
10669531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        testSecureStreamingPost(StreamingMode.CHUNKED);
10679531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    }
10689531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson
10699531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    /**
107084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson     * Users have reported problems using HTTPS with streaming request bodies.
107184f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson     * http://code.google.com/p/android/issues/detail?id=12860
107284f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson     */
107384f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson    private void testSecureStreamingPost(StreamingMode streamingMode) throws Exception {
107484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
107584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
107684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.enqueue(new MockResponse().setBody("Success!"));
107784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        server.play();
107884f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
107984f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
108084f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
1081c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        connection.setDoOutput(true);
108221dddca4064527116af7a1553de502c6d11138daJesse Wilson        byte[] requestBody = { 'A', 'B', 'C', 'D' };
108321dddca4064527116af7a1553de502c6d11138daJesse Wilson        if (streamingMode == StreamingMode.FIXED_LENGTH) {
108421dddca4064527116af7a1553de502c6d11138daJesse Wilson            connection.setFixedLengthStreamingMode(requestBody.length);
108521dddca4064527116af7a1553de502c6d11138daJesse Wilson        } else if (streamingMode == StreamingMode.CHUNKED) {
108621dddca4064527116af7a1553de502c6d11138daJesse Wilson            connection.setChunkedStreamingMode(0);
108721dddca4064527116af7a1553de502c6d11138daJesse Wilson        }
108821dddca4064527116af7a1553de502c6d11138daJesse Wilson        OutputStream outputStream = connection.getOutputStream();
108921dddca4064527116af7a1553de502c6d11138daJesse Wilson        outputStream.write(requestBody);
109021dddca4064527116af7a1553de502c6d11138daJesse Wilson        outputStream.close();
1091953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("Success!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
109221dddca4064527116af7a1553de502c6d11138daJesse Wilson
109321dddca4064527116af7a1553de502c6d11138daJesse Wilson        RecordedRequest request = server.takeRequest();
109421dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals("POST / HTTP/1.1", request.getRequestLine());
109521dddca4064527116af7a1553de502c6d11138daJesse Wilson        if (streamingMode == StreamingMode.FIXED_LENGTH) {
109621dddca4064527116af7a1553de502c6d11138daJesse Wilson            assertEquals(Collections.<Integer>emptyList(), request.getChunkSizes());
109721dddca4064527116af7a1553de502c6d11138daJesse Wilson        } else if (streamingMode == StreamingMode.CHUNKED) {
109821dddca4064527116af7a1553de502c6d11138daJesse Wilson            assertEquals(Arrays.asList(4), request.getChunkSizes());
109921dddca4064527116af7a1553de502c6d11138daJesse Wilson        }
110021dddca4064527116af7a1553de502c6d11138daJesse Wilson        assertEquals(Arrays.toString(requestBody), Arrays.toString(request.getBody()));
110121dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
110221dddca4064527116af7a1553de502c6d11138daJesse Wilson
110321dddca4064527116af7a1553de502c6d11138daJesse Wilson    enum StreamingMode {
110421dddca4064527116af7a1553de502c6d11138daJesse Wilson        FIXED_LENGTH, CHUNKED
110521dddca4064527116af7a1553de502c6d11138daJesse Wilson    }
110621dddca4064527116af7a1553de502c6d11138daJesse Wilson
110721dddca4064527116af7a1553de502c6d11138daJesse Wilson    public void testAuthenticateWithPost() throws Exception {
110821dddca4064527116af7a1553de502c6d11138daJesse Wilson        MockResponse pleaseAuthenticate = new MockResponse()
110921dddca4064527116af7a1553de502c6d11138daJesse Wilson                .setResponseCode(401)
111021dddca4064527116af7a1553de502c6d11138daJesse Wilson                .addHeader("WWW-Authenticate: Basic realm=\"protected area\"")
111121dddca4064527116af7a1553de502c6d11138daJesse Wilson                .setBody("Please authenticate.");
111221dddca4064527116af7a1553de502c6d11138daJesse Wilson        // fail auth three times...
111321dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(pleaseAuthenticate);
111421dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(pleaseAuthenticate);
111521dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(pleaseAuthenticate);
111621dddca4064527116af7a1553de502c6d11138daJesse Wilson        // ...then succeed the fourth time
111721dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.enqueue(new MockResponse().setBody("Successful auth!"));
111821dddca4064527116af7a1553de502c6d11138daJesse Wilson        server.play();
111921dddca4064527116af7a1553de502c6d11138daJesse Wilson
1120953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        Authenticator.setDefault(SIMPLE_AUTHENTICATOR);
112121dddca4064527116af7a1553de502c6d11138daJesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
112221dddca4064527116af7a1553de502c6d11138daJesse Wilson        connection.setDoOutput(true);
112321dddca4064527116af7a1553de502c6d11138daJesse Wilson        byte[] requestBody = { 'A', 'B', 'C', 'D' };
112421dddca4064527116af7a1553de502c6d11138daJesse Wilson        OutputStream outputStream = connection.getOutputStream();
112521dddca4064527116af7a1553de502c6d11138daJesse Wilson        outputStream.write(requestBody);
1126953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        outputStream.close();
1127953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
1128953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
1129953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        // no authorization header for the first request...
1130953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        RecordedRequest request = server.takeRequest();
1131953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertContainsNoneMatching(request.getHeaders(), "Authorization: Basic .*");
1132953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
1133953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        // ...but the three requests that follow include an authorization header
1134953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        for (int i = 0; i < 3; i++) {
1135953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            request = server.takeRequest();
1136953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            assertEquals("POST / HTTP/1.1", request.getRequestLine());
1137953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            assertContains(request.getHeaders(), "Authorization: Basic " + BASE_64_CREDENTIALS);
1138953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            assertEquals(Arrays.toString(requestBody), Arrays.toString(request.getBody()));
1139953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
1140953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
1141953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
1142953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public void testAuthenticateWithGet() throws Exception {
1143953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        MockResponse pleaseAuthenticate = new MockResponse()
1144953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .setResponseCode(401)
1145953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .addHeader("WWW-Authenticate: Basic realm=\"protected area\"")
1146953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                .setBody("Please authenticate.");
1147953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        // fail auth three times...
1148953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(pleaseAuthenticate);
1149953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(pleaseAuthenticate);
1150953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(pleaseAuthenticate);
1151953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        // ...then succeed the fourth time
1152953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.enqueue(new MockResponse().setBody("Successful auth!"));
1153953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        server.play();
1154953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
1155953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        Authenticator.setDefault(SIMPLE_AUTHENTICATOR);
115619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
115719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
115819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
115919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        // no authorization header for the first request...
116019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        RecordedRequest request = server.takeRequest();
116119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertContainsNoneMatching(request.getHeaders(), "Authorization: Basic .*");
116219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
116319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        // ...but the three requests that follow requests include an authorization header
116419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        for (int i = 0; i < 3; i++) {
116519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            request = server.takeRequest();
116619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            assertEquals("GET / HTTP/1.1", request.getRequestLine());
116719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson            assertContains(request.getHeaders(), "Authorization: Basic " + BASE_64_CREDENTIALS);
116819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        }
116919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    }
117019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
117119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    public void testRedirectedWithChunkedEncoding() throws Exception {
117219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        testRedirected(TransferKind.CHUNKED, true);
117319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    }
117419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
117519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    public void testRedirectedWithContentLengthHeader() throws Exception {
117619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        testRedirected(TransferKind.FIXED_LENGTH, true);
117719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    }
117819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
117919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    public void testRedirectedWithNoLengthHeaders() throws Exception {
118019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        testRedirected(TransferKind.END_OF_STREAM, false);
118119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    }
118219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
118319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson    private void testRedirected(TransferKind transferKind, boolean reuse) throws Exception {
118419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        MockResponse response = new MockResponse()
118519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
118619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson                .addHeader("Location: /foo");
118719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        transferKind.setBody(response, "This page has moved!", 10);
118819c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.enqueue(response);
118919c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.enqueue(new MockResponse().setBody("This is the new location!"));
119019c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        server.play();
119119c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
119219c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
119319c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertEquals("This is the new location!",
119419c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson                readAscii(connection.getInputStream(), Integer.MAX_VALUE));
119519c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson
119619c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        RecordedRequest first = server.takeRequest();
119719c77c6a6da8cea7327ccbb741963ac76d3fae53Jesse Wilson        assertEquals("GET / HTTP/1.1", first.getRequestLine());
11982f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        RecordedRequest retry = server.takeRequest();
11992f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        assertEquals("GET /foo HTTP/1.1", retry.getRequestLine());
12002f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        if (reuse) {
12012f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson            assertEquals("Expected connection reuse", 1, retry.getSequenceNumber());
12022f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        }
12032f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    }
12042f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
12052f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson    public void testRedirectedOnHttps() throws IOException, InterruptedException {
12062f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
12072f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
12082f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse()
12092f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
12102f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .addHeader("Location: /foo")
12112f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson                .setBody("This page has moved!"));
12122f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.enqueue(new MockResponse().setBody("This is the new location!"));
12132f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        server.play();
12142f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson
12152f16d502415452f808b5f077ad07b941645d14e4Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
1216757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
1217757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("This is the new location!",
1218757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                readAscii(connection.getInputStream(), Integer.MAX_VALUE));
1219757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1220757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        RecordedRequest first = server.takeRequest();
1221757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("GET / HTTP/1.1", first.getRequestLine());
1222757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        RecordedRequest retry = server.takeRequest();
1223757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("GET /foo HTTP/1.1", retry.getRequestLine());
1224757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("Expected connection reuse", 1, retry.getSequenceNumber());
1225757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    }
1226ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson
1227ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson    public void testNotRedirectedFromHttpsToHttp() throws IOException, InterruptedException {
1228ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
1229757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
1230757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse()
1231ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
1232ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson                .addHeader("Location: http://anyhost/foo")
1233ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson                .setBody("This page has moved!"));
1234757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.play();
1235757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1236757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
1237757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
1238757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("This page has moved!",
1239757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                readAscii(connection.getInputStream(), Integer.MAX_VALUE));
1240757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    }
1241757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1242757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    public void testNotRedirectedFromHttpToHttps() throws IOException, InterruptedException {
1243757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse()
1244757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
1245757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Location: https://anyhost/foo")
1246ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson                .setBody("This page has moved!"));
1247ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        server.play();
1248ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson
1249757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
1250757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("This page has moved!",
1251ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson                readAscii(connection.getInputStream(), Integer.MAX_VALUE));
1252ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson    }
1253ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson
1254757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    public void testRedirectToAnotherOriginServer() throws Exception {
1255757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        MockWebServer server2 = new MockWebServer();
1256757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server2.enqueue(new MockResponse().setBody("This is the 2nd server!"));
1257757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server2.play();
1258757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
1259757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        server.enqueue(new MockResponse()
1260757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
1261757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .addHeader("Location: " + server2.getUrl("/").toString())
1262757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                .setBody("This page has moved!"));
1263ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        server.enqueue(new MockResponse().setBody("This is the first server again!"));
1264ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        server.play();
1265ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson
1266757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
1267757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("This is the 2nd server!",
1268ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson                readAscii(connection.getInputStream(), Integer.MAX_VALUE));
1269ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        assertEquals(server2.getUrl("/"), connection.getURL());
1270ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson
1271757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        // make sure the first server was careful to recycle the connection
1272757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        assertEquals("This is the first server again!",
12730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                readAscii(server.getUrl("/").openStream(), Integer.MAX_VALUE));
12740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
12750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest first = server.takeRequest();
12760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertContains(first.getHeaders(), "Host: " + hostName + ":" + server.getPort());
12770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest second = server2.takeRequest();
1278953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        assertContains(second.getHeaders(), "Host: " + hostName + ":" + server2.getPort());
12799531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        RecordedRequest third = server.takeRequest();
12809531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        assertEquals("Expected connection reuse", 1, third.getSequenceNumber());
12819531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson
12829531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        server2.shutdown();
1283953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
1284953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
1285953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public void testResponse300MultipleChoiceWithPost() throws Exception {
12860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // Chrome doesn't follow the redirect, but Firefox and the RI both do
12870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testResponseRedirectedWithPost(HttpURLConnection.HTTP_MULT_CHOICE);
12880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
12890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
12900c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponse301MovedPermanentlyWithPost() throws Exception {
12910c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_PERM);
12920c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
12930c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
12940c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponse302MovedTemporarilyWithPost() throws Exception {
12950c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_TEMP);
12960c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
12970c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
12980c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponse303SeeOtherWithPost() throws Exception {
12990c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        testResponseRedirectedWithPost(HttpURLConnection.HTTP_SEE_OTHER);
13000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
13010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    private void testResponseRedirectedWithPost(int redirectCode) throws Exception {
13030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
13040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setResponseCode(redirectCode)
13050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Location: /page2")
13060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("This page has moved!"));
13070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("Page 2"));
13080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
13090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/page1").openConnection();
13110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        connection.setDoOutput(true);
13120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        byte[] requestBody = { 'A', 'B', 'C', 'D' };
13130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        OutputStream outputStream = connection.getOutputStream();
13140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        outputStream.write(requestBody);
13150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        outputStream.close();
13160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("Page 2", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
13170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertTrue(connection.getDoOutput());
13180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest page1 = server.takeRequest();
13200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("POST /page1 HTTP/1.1", page1.getRequestLine());
13210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(Arrays.toString(requestBody), Arrays.toString(page1.getBody()));
13220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest page2 = server.takeRequest();
13240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("GET /page2 HTTP/1.1", page2.getRequestLine());
13250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
13260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testResponse305UseProxy() throws Exception {
13280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
13290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()
13300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setResponseCode(HttpURLConnection.HTTP_USE_PROXY)
13310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Location: " + server.getUrl("/"))
13320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("This page has moved!"));
13330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("Proxy Response"));
13340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/foo").openConnection();
13360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        // Fails on the RI, which gets "Proxy Response"
13370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("This page has moved!",
13380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                readAscii(connection.getInputStream(), Integer.MAX_VALUE));
13390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest page1 = server.takeRequest();
13410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("GET /foo HTTP/1.1", page1.getRequestLine());
13420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(1, server.getRequestCount());
13430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
13440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testHttpsWithCustomTrustManager() throws Exception {
13460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier();
13470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordingTrustManager trustManager = new RecordingTrustManager();
13480c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        SSLContext sc = SSLContext.getInstance("TLS");
13490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        sc.init(null, new TrustManager[] { trustManager }, new java.security.SecureRandom());
13500c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HostnameVerifier defaultHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
13520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
13530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        SSLSocketFactory defaultSSLSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
13540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
13550c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
13560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            TestSSLContext testSSLContext = TestSSLContext.create();
13570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
13580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            server.enqueue(new MockResponse().setBody("ABC"));
13590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            server.enqueue(new MockResponse().setBody("DEF"));
13600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            server.enqueue(new MockResponse().setBody("GHI"));
13610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            server.play();
13620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            URL url = server.getUrl("/");
136484f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            assertEquals("ABC", readAscii(url.openStream(), Integer.MAX_VALUE));
136584f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            assertEquals("DEF", readAscii(url.openStream(), Integer.MAX_VALUE));
136684f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson            assertEquals("GHI", readAscii(url.openStream(), Integer.MAX_VALUE));
136784f1fd18b9db6fc6f2bb65694bee99d42f88bb79Jesse Wilson
13680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertEquals(Arrays.asList("verify " + hostName), hostnameVerifier.calls);
13690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            assertEquals(Arrays.asList("checkServerTrusted ["
13700c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    + "CN=" + hostName + " 1, "
13710c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    + "CN=Test Intermediate Certificate Authority 1, "
13720c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    + "CN=Test Root Certificate Authority 1"
13730c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    + "] RSA"),
13740c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                    trustManager.calls);
13750c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } finally {
13760c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            HttpsURLConnection.setDefaultHostnameVerifier(defaultHostnameVerifier);
13770c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            HttpsURLConnection.setDefaultSSLSocketFactory(defaultSSLSocketFactory);
13780c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
13790c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
13800c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
13810c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testConnectTimeouts() throws IOException {
13820c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        StuckServer ss = new StuckServer();
13830c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        int serverPort = ss.getLocalPort();
13840c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection urlConnection = new URL("http://localhost:" + serverPort).openConnection();
13850c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        int timeout = 1000;
13860c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        urlConnection.setConnectTimeout(timeout);
13870c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        long start = System.currentTimeMillis();
13880c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
13890c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            urlConnection.getInputStream();
1390953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            fail();
1391c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        } catch (SocketTimeoutException expected) {
1392c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson            long actual = System.currentTimeMillis() - start;
1393c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson            assertTrue(Math.abs(timeout - actual) < 500);
1394c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson        } finally {
1395c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson            ss.close();
1396953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
1397953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
1398c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson
1399c9e12f729cb962eb60754e4500312421c46e71ddJesse Wilson    public void testReadTimeouts() throws IOException {
14000c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        /*
14010c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * This relies on the fact that MockWebServer doesn't close the
14020c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * connection after a response has been sent. This causes the client to
14030c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         * try to read more bytes than are sent, which results in a timeout.
14040c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson         */
14050c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        MockResponse timeout = new MockResponse()
14060c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .setBody("ABC")
14070c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .clearHeaders()
14080c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                .addHeader("Content-Length: 4");
14090c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(timeout);
14100c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().setBody("unused")); // to keep the server alive
14110c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
14120c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14130c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        URLConnection urlConnection = server.getUrl("/").openConnection();
14140c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        urlConnection.setReadTimeout(1000);
14150c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        InputStream in = urlConnection.getInputStream();
14160c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals('A', in.read());
14170c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals('B', in.read());
14180c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals('C', in.read());
14190c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        try {
14200c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            in.read(); // if Content-Length was accurate, this would return -1 immediately
14210c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson            fail();
14220c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        } catch (SocketTimeoutException expected) {
14230c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        }
14240c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
14250c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14260c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testSetChunkedEncodingAsRequestProperty() throws IOException, InterruptedException {
14270c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse());
14280c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
14290c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14300c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection();
14310c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        urlConnection.setRequestProperty("Transfer-encoding", "chunked");
14320c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        urlConnection.setDoOutput(true);
14330c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        urlConnection.getOutputStream().write("ABC".getBytes("UTF-8"));
14340c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(200, urlConnection.getResponseCode());
14350c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14360c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        RecordedRequest request = server.takeRequest();
14370c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("ABC", new String(request.getBody(), "UTF-8"));
14380c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    }
14390c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14400c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testConnectionCloseInRequest() throws IOException, InterruptedException {
14410c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse()); // server doesn't honor the connection: close header!
14420c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse());
14430c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
14440c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14450c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection a = (HttpURLConnection) server.getUrl("/").openConnection();
14460c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        a.setRequestProperty("Connection", "close");
14470c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(200, a.getResponseCode());
1448433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson
14490c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection b = (HttpURLConnection) server.getUrl("/").openConnection();
1450433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        assertEquals(200, b.getResponseCode());
14510c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14520c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(0, server.takeRequest().getSequenceNumber());
14530c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("When connection: close is used, each request should get its own connection",
14540c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson                0, server.takeRequest().getSequenceNumber());
1455433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson    }
14560c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14570c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson    public void testConnectionCloseInResponse() throws IOException, InterruptedException {
14580c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse().addHeader("Connection: close"));
14590c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.enqueue(new MockResponse());
14600c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        server.play();
14610c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14620c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection a = (HttpURLConnection) server.getUrl("/").openConnection();
14630c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(200, a.getResponseCode());
14640c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14650c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        HttpURLConnection b = (HttpURLConnection) server.getUrl("/").openConnection();
14660c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(200, b.getResponseCode());
14670c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson
14680c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals(0, server.takeRequest().getSequenceNumber());
14690c59055dd24e1659f85d9ff7e2148883f663bd62Jesse Wilson        assertEquals("When connection: close is used, each request should get its own connection",
1470                0, server.takeRequest().getSequenceNumber());
1471    }
1472
1473    public void testConnectionCloseWithRedirect() throws IOException, InterruptedException {
1474        MockResponse response = new MockResponse()
1475                .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
1476                .addHeader("Location: /foo")
1477                .addHeader("Connection: close");
1478        server.enqueue(response);
1479        server.enqueue(new MockResponse().setBody("This is the new location!"));
1480        server.play();
1481
1482        URLConnection connection = server.getUrl("/").openConnection();
1483        assertEquals("This is the new location!",
1484                readAscii(connection.getInputStream(), Integer.MAX_VALUE));
1485
1486        assertEquals(0, server.takeRequest().getSequenceNumber());
1487        assertEquals("When connection: close is used, each request should get its own connection",
1488                0, server.takeRequest().getSequenceNumber());
1489    }
1490
1491    public void testResponseCodeDisagreesWithHeaders() throws IOException, InterruptedException {
1492        server.enqueue(new MockResponse()
1493                .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT)
1494                .setBody("This body is not allowed!"));
1495        server.play();
1496
1497        URLConnection connection = server.getUrl("/").openConnection();
1498        assertEquals("This body is not allowed!",
1499                readAscii(connection.getInputStream(), Integer.MAX_VALUE));
1500    }
1501
1502    public void testSingleByteReadIsSigned() throws IOException {
1503        server.enqueue(new MockResponse().setBody(new byte[] { -2, -1 }));
1504        server.play();
1505
1506        URLConnection connection = server.getUrl("/").openConnection();
1507        InputStream in = connection.getInputStream();
1508        assertEquals(254, in.read());
1509        assertEquals(255, in.read());
1510        assertEquals(-1, in.read());
1511    }
1512
1513    public void testFlushAfterStreamTransmittedWithChunkedEncoding() throws IOException {
1514        testFlushAfterStreamTransmitted(TransferKind.CHUNKED);
1515    }
1516
1517    public void testFlushAfterStreamTransmittedWithFixedLength() throws IOException {
1518        testFlushAfterStreamTransmitted(TransferKind.FIXED_LENGTH);
1519    }
1520
1521    public void testFlushAfterStreamTransmittedWithNoLengthHeaders() throws IOException {
1522        testFlushAfterStreamTransmitted(TransferKind.END_OF_STREAM);
1523    }
1524
1525    /**
1526     * We explicitly permit apps to close the upload stream even after it has
1527     * been transmitted.  We also permit flush so that buffered streams can
1528     * do a no-op flush when they are closed. http://b/3038470
1529     */
1530    private void testFlushAfterStreamTransmitted(TransferKind transferKind) throws IOException {
1531        server.enqueue(new MockResponse().setBody("abc"));
1532        server.play();
1533
1534        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
1535        connection.setDoOutput(true);
1536        byte[] upload = "def".getBytes("UTF-8");
1537
1538        if (transferKind == TransferKind.CHUNKED) {
1539            connection.setChunkedStreamingMode(0);
1540        } else if (transferKind == TransferKind.FIXED_LENGTH) {
1541            connection.setFixedLengthStreamingMode(upload.length);
1542        }
1543
1544        OutputStream out = connection.getOutputStream();
1545        out.write(upload);
1546        assertEquals("abc", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
1547
1548        out.flush(); // dubious but permitted
1549        try {
1550            out.write("ghi".getBytes("UTF-8"));
1551            fail();
1552        } catch (IOException expected) {
1553        }
1554    }
1555
1556    public void testGetHeadersThrows() throws IOException {
1557        server.enqueue(new MockResponse().setSocketPolicy(DISCONNECT_AT_START));
1558        server.play();
1559
1560        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
1561        try {
1562            connection.getInputStream();
1563            fail();
1564        } catch (IOException expected) {
1565        }
1566
1567        try {
1568            connection.getInputStream();
1569            fail();
1570        } catch (IOException expected) {
1571        }
1572    }
1573
1574    public void testGetKeepAlive() throws Exception {
1575        MockWebServer server = new MockWebServer();
1576        server.enqueue(new MockResponse().setBody("ABC"));
1577        server.play();
1578
1579        // The request should work once and then fail
1580        URLConnection connection = server.getUrl("").openConnection();
1581        InputStream input = connection.getInputStream();
1582        assertEquals("ABC", readAscii(input, Integer.MAX_VALUE));
1583        input.close();
1584        try {
1585            server.getUrl("").openConnection().getInputStream();
1586            fail();
1587        } catch (ConnectException expected) {
1588        }
1589    }
1590
1591    /**
1592     * This test goes through the exhaustive set of interesting ASCII characters
1593     * because most of those characters are interesting in some way according to
1594     * RFC 2396 and RFC 2732. http://b/1158780
1595     */
1596    public void testLenientUrlToUri() throws Exception {
1597        // alphanum
1598        testUrlToUriMapping("abzABZ09", "abzABZ09", "abzABZ09", "abzABZ09", "abzABZ09");
1599
1600        // control characters
1601        testUrlToUriMapping("\u0001", "%01", "%01", "%01", "%01");
1602        testUrlToUriMapping("\u001f", "%1F", "%1F", "%1F", "%1F");
1603
1604        // ascii characters
1605        testUrlToUriMapping("%20", "%20", "%20", "%20", "%20");
1606        testUrlToUriMapping("%20", "%20", "%20", "%20", "%20");
1607        testUrlToUriMapping(" ", "%20", "%20", "%20", "%20");
1608        testUrlToUriMapping("!", "!", "!", "!", "!");
1609        testUrlToUriMapping("\"", "%22", "%22", "%22", "%22");
1610        testUrlToUriMapping("#", null, null, null, "%23");
1611        testUrlToUriMapping("$", "$", "$", "$", "$");
1612        testUrlToUriMapping("&", "&", "&", "&", "&");
1613        testUrlToUriMapping("'", "'", "'", "'", "'");
1614        testUrlToUriMapping("(", "(", "(", "(", "(");
1615        testUrlToUriMapping(")", ")", ")", ")", ")");
1616        testUrlToUriMapping("*", "*", "*", "*", "*");
1617        testUrlToUriMapping("+", "+", "+", "+", "+");
1618        testUrlToUriMapping(",", ",", ",", ",", ",");
1619        testUrlToUriMapping("-", "-", "-", "-", "-");
1620        testUrlToUriMapping(".", ".", ".", ".", ".");
1621        testUrlToUriMapping("/", null, "/", "/", "/");
1622        testUrlToUriMapping(":", null, ":", ":", ":");
1623        testUrlToUriMapping(";", ";", ";", ";", ";");
1624        testUrlToUriMapping("<", "%3C", "%3C", "%3C", "%3C");
1625        testUrlToUriMapping("=", "=", "=", "=", "=");
1626        testUrlToUriMapping(">", "%3E", "%3E", "%3E", "%3E");
1627        testUrlToUriMapping("?", null, null, "?", "?");
1628        testUrlToUriMapping("@", "@", "@", "@", "@");
1629        testUrlToUriMapping("[", null, "%5B", null, "%5B");
1630        testUrlToUriMapping("\\", "%5C", "%5C", "%5C", "%5C");
1631        testUrlToUriMapping("]", null, "%5D", null, "%5D");
1632        testUrlToUriMapping("^", "%5E", "%5E", "%5E", "%5E");
1633        testUrlToUriMapping("_", "_", "_", "_", "_");
1634        testUrlToUriMapping("`", "%60", "%60", "%60", "%60");
1635        testUrlToUriMapping("{", "%7B", "%7B", "%7B", "%7B");
1636        testUrlToUriMapping("|", "%7C", "%7C", "%7C", "%7C");
1637        testUrlToUriMapping("}", "%7D", "%7D", "%7D", "%7D");
1638        testUrlToUriMapping("~", "~", "~", "~", "~");
1639        testUrlToUriMapping("~", "~", "~", "~", "~");
1640        testUrlToUriMapping("\u007f", "%7F", "%7F", "%7F", "%7F");
1641
1642        // beyond ascii
1643        testUrlToUriMapping("\u0080", "%C2%80", "%C2%80", "%C2%80", "%C2%80");
1644        testUrlToUriMapping("\u20ac", "\u20ac", "\u20ac", "\u20ac", "\u20ac");
1645        testUrlToUriMapping("\ud842\udf9f",
1646                "\ud842\udf9f", "\ud842\udf9f", "\ud842\udf9f", "\ud842\udf9f");
1647    }
1648
1649    public void testLenientUrlToUriNul() throws Exception {
1650        testUrlToUriMapping("\u0000", "%00", "%00", "%00", "%00"); // RI fails this
1651    }
1652
1653    private void testUrlToUriMapping(String string, String asAuthority, String asFile,
1654            String asQuery, String asFragment) throws Exception {
1655        if (asAuthority != null) {
1656            assertEquals("http://host" + asAuthority + ".tld/",
1657                    backdoorUrlToUri(new URL("http://host" + string + ".tld/")).toString());
1658        }
1659        if (asFile != null) {
1660            assertEquals("http://host.tld/file" + asFile + "/",
1661                    backdoorUrlToUri(new URL("http://host.tld/file" + string + "/")).toString());
1662        }
1663        if (asQuery != null) {
1664            assertEquals("http://host.tld/file?q" + asQuery + "=x",
1665                    backdoorUrlToUri(new URL("http://host.tld/file?q" + string + "=x")).toString());
1666        }
1667        assertEquals("http://host.tld/file#" + asFragment + "-x",
1668                backdoorUrlToUri(new URL("http://host.tld/file#" + asFragment + "-x")).toString());
1669    }
1670
1671    /**
1672     * Exercises HttpURLConnection to convert URL to a URI. Unlike URL#toURI,
1673     * HttpURLConnection recovers from URLs with unescaped but unsupported URI
1674     * characters like '{' and '|' by escaping these characters.
1675     */
1676    private URI backdoorUrlToUri(URL url) throws Exception {
1677        final AtomicReference<URI> uriReference = new AtomicReference<URI>();
1678
1679        ResponseCache.setDefault(new ResponseCache() {
1680            @Override public CacheRequest put(URI uri, URLConnection connection) throws IOException {
1681                return null;
1682            }
1683            @Override public CacheResponse get(URI uri, String requestMethod,
1684                    Map<String, List<String>> requestHeaders) throws IOException {
1685                uriReference.set(uri);
1686                throw new UnsupportedOperationException();
1687            }
1688        });
1689
1690        try {
1691            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
1692            connection.getResponseCode();
1693        } catch (Exception expected) {
1694        }
1695
1696        return uriReference.get();
1697    }
1698
1699    /**
1700     * Don't explode if the cache returns a null body. http://b/3373699
1701     */
1702    public void testResponseCacheReturnsNullOutputStream() throws Exception {
1703        final AtomicBoolean aborted = new AtomicBoolean();
1704        ResponseCache.setDefault(new ResponseCache() {
1705            @Override public CacheResponse get(URI uri, String requestMethod,
1706                    Map<String, List<String>> requestHeaders) throws IOException {
1707                return null;
1708            }
1709            @Override public CacheRequest put(URI uri, URLConnection connection) throws IOException {
1710                return new CacheRequest() {
1711                    @Override public void abort() {
1712                        aborted.set(true);
1713                    }
1714                    @Override public OutputStream getBody() throws IOException {
1715                        return null;
1716                    }
1717                };
1718            }
1719        });
1720
1721        server.enqueue(new MockResponse().setBody("abcdef"));
1722        server.play();
1723
1724        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
1725        InputStream in = connection.getInputStream();
1726        assertEquals("abc", readAscii(in, 3));
1727        in.close();
1728        assertFalse(aborted.get()); // The best behavior is ambiguous, but RI 6 doesn't abort here
1729    }
1730
1731
1732    /**
1733     * http://code.google.com/p/android/issues/detail?id=14562
1734     */
1735    public void testReadAfterLastByte() throws Exception {
1736        server.enqueue(new MockResponse()
1737                .setBody("ABC")
1738                .clearHeaders()
1739                .addHeader("Connection: close")
1740                .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END));
1741        server.play();
1742
1743        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
1744        InputStream in = connection.getInputStream();
1745        assertEquals("ABC", readAscii(in, 3));
1746        assertEquals(-1, in.read());
1747        assertEquals(-1, in.read()); // throws IOException in Gingerbread
1748    }
1749
1750    public void testGetContent() throws Exception {
1751        server.enqueue(new MockResponse().setBody("A"));
1752        server.play();
1753        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
1754        InputStream in = (InputStream) connection.getContent();
1755        assertEquals("A", readAscii(in, Integer.MAX_VALUE));
1756    }
1757
1758    public void testGetContentOfType() throws Exception {
1759        server.enqueue(new MockResponse().setBody("A"));
1760        server.play();
1761        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
1762        try {
1763            connection.getContent(null);
1764            fail();
1765        } catch (NullPointerException expected) {
1766        }
1767        try {
1768            connection.getContent(new Class[] { null });
1769            fail();
1770        } catch (NullPointerException expected) {
1771        }
1772        assertNull(connection.getContent(new Class[] { getClass() }));
1773        connection.disconnect();
1774    }
1775
1776    public void testGetOutputStreamOnGetFails() throws Exception {
1777        server.enqueue(new MockResponse());
1778        server.play();
1779        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
1780        try {
1781            connection.getOutputStream();
1782            fail();
1783        } catch (ProtocolException expected) {
1784        }
1785    }
1786
1787    public void testGetOutputAfterGetInputStreamFails() throws Exception {
1788        server.enqueue(new MockResponse());
1789        server.play();
1790        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
1791        connection.setDoOutput(true);
1792        try {
1793            connection.getInputStream();
1794            connection.getOutputStream();
1795            fail();
1796        } catch (ProtocolException expected) {
1797        }
1798    }
1799
1800    public void testSetDoOutputOrDoInputAfterConnectFails() throws Exception {
1801        server.enqueue(new MockResponse());
1802        server.play();
1803        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
1804        connection.connect();
1805        try {
1806            connection.setDoOutput(true);
1807            fail();
1808        } catch (IllegalStateException expected) {
1809        }
1810        try {
1811            connection.setDoInput(true);
1812            fail();
1813        } catch (IllegalStateException expected) {
1814        }
1815        connection.disconnect();
1816    }
1817
1818    public void testClientSendsContentLength() throws Exception {
1819        server.enqueue(new MockResponse().setBody("A"));
1820        server.play();
1821        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
1822        connection.setDoOutput(true);
1823        OutputStream out = connection.getOutputStream();
1824        out.write(new byte[] { 'A', 'B', 'C' });
1825        out.close();
1826        assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
1827        RecordedRequest request = server.takeRequest();
1828        assertContains(request.getHeaders(), "Content-Length: 3");
1829    }
1830
1831    public void testGetContentLengthConnects() throws Exception {
1832        server.enqueue(new MockResponse().setBody("ABC"));
1833        server.play();
1834        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
1835        assertEquals(3, connection.getContentLength());
1836        connection.disconnect();
1837    }
1838
1839    public void testGetContentTypeConnects() throws Exception {
1840        server.enqueue(new MockResponse()
1841                .addHeader("Content-Type: text/plain")
1842                .setBody("ABC"));
1843        server.play();
1844        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
1845        assertEquals("text/plain", connection.getContentType());
1846        connection.disconnect();
1847    }
1848
1849    public void testGetContentEncodingConnects() throws Exception {
1850        server.enqueue(new MockResponse()
1851                .addHeader("Content-Encoding: identity")
1852                .setBody("ABC"));
1853        server.play();
1854        HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
1855        assertEquals("identity", connection.getContentEncoding());
1856        connection.disconnect();
1857    }
1858
1859    // http://b/4361656
1860    public void testUrlContainsQueryButNoPath() throws Exception {
1861        server.enqueue(new MockResponse().setBody("A"));
1862        server.play();
1863        URL url = new URL("http", server.getHostName(), server.getPort(), "?query");
1864        assertEquals("A", readAscii(url.openConnection().getInputStream(), Integer.MAX_VALUE));
1865        RecordedRequest request = server.takeRequest();
1866        assertEquals("GET /?query HTTP/1.1", request.getRequestLine());
1867    }
1868
1869    // http://code.google.com/p/android/issues/detail?id=20442
1870    public void testInputStreamAvailableWithChunkedEncoding() throws Exception {
1871        testInputStreamAvailable(TransferKind.CHUNKED);
1872    }
1873
1874    public void testInputStreamAvailableWithContentLengthHeader() throws Exception {
1875        testInputStreamAvailable(TransferKind.FIXED_LENGTH);
1876    }
1877
1878    public void testInputStreamAvailableWithNoLengthHeaders() throws Exception {
1879        testInputStreamAvailable(TransferKind.END_OF_STREAM);
1880    }
1881
1882    private void testInputStreamAvailable(TransferKind transferKind) throws IOException {
1883        String body = "ABCDEFGH";
1884        MockResponse response = new MockResponse();
1885        transferKind.setBody(response, body, 4);
1886        server.enqueue(response);
1887        server.play();
1888        URLConnection connection = server.getUrl("/").openConnection();
1889        InputStream in = connection.getInputStream();
1890        for (int i = 0; i < body.length(); i++) {
1891            assertTrue(in.available() >= 0);
1892            assertEquals(body.charAt(i), in.read());
1893        }
1894        assertEquals(0, in.available());
1895        assertEquals(-1, in.read());
1896    }
1897
1898    public void testInspectSslBeforeConnect() throws Exception {
1899        TestSSLContext testSSLContext = TestSSLContext.create();
1900        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
1901        server.enqueue(new MockResponse());
1902        server.play();
1903
1904        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
1905        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
1906        assertNotNull(connection.getHostnameVerifier());
1907        try {
1908            connection.getLocalCertificates();
1909            fail();
1910        } catch (IllegalStateException expected) {
1911        }
1912        try {
1913            connection.getServerCertificates();
1914            fail();
1915        } catch (IllegalStateException expected) {
1916        }
1917        try {
1918            connection.getCipherSuite();
1919            fail();
1920        } catch (IllegalStateException expected) {
1921        }
1922        try {
1923            connection.getPeerPrincipal();
1924            fail();
1925        } catch (IllegalStateException expected) {
1926        }
1927    }
1928
1929    /**
1930     * Test that we can inspect the SSL session after connect().
1931     * http://code.google.com/p/android/issues/detail?id=24431
1932     */
1933    public void testInspectSslAfterConnect() throws Exception {
1934        TestSSLContext testSSLContext = TestSSLContext.create();
1935        server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
1936        server.enqueue(new MockResponse());
1937        server.play();
1938
1939        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
1940        connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
1941        connection.connect();
1942        assertNotNull(connection.getHostnameVerifier());
1943        assertNull(connection.getLocalCertificates());
1944        assertNotNull(connection.getServerCertificates());
1945        assertNotNull(connection.getCipherSuite());
1946        assertNotNull(connection.getPeerPrincipal());
1947    }
1948
1949    /**
1950     * Returns a gzipped copy of {@code bytes}.
1951     */
1952    public byte[] gzip(byte[] bytes) throws IOException {
1953        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
1954        OutputStream gzippedOut = new GZIPOutputStream(bytesOut);
1955        gzippedOut.write(bytes);
1956        gzippedOut.close();
1957        return bytesOut.toByteArray();
1958    }
1959
1960    /**
1961     * Reads at most {@code limit} characters from {@code in} and asserts that
1962     * content equals {@code expected}.
1963     */
1964    private void assertContent(String expected, URLConnection connection, int limit)
1965            throws IOException {
1966        connection.connect();
1967        assertEquals(expected, readAscii(connection.getInputStream(), limit));
1968        ((HttpURLConnection) connection).disconnect();
1969    }
1970
1971    private void assertContent(String expected, URLConnection connection) throws IOException {
1972        assertContent(expected, connection, Integer.MAX_VALUE);
1973    }
1974
1975    private void assertContains(List<String> headers, String header) {
1976        assertTrue(headers.toString(), headers.contains(header));
1977    }
1978
1979    private void assertContainsNoneMatching(List<String> headers, String pattern) {
1980        for (String header : headers) {
1981            if (header.matches(pattern)) {
1982                fail("Header " + header + " matches " + pattern);
1983            }
1984        }
1985    }
1986
1987    private Set<String> newSet(String... elements) {
1988        return new HashSet<String>(Arrays.asList(elements));
1989    }
1990
1991    enum TransferKind {
1992        CHUNKED() {
1993            @Override void setBody(MockResponse response, byte[] content, int chunkSize)
1994                    throws IOException {
1995                response.setChunkedBody(content, chunkSize);
1996            }
1997        },
1998        FIXED_LENGTH() {
1999            @Override void setBody(MockResponse response, byte[] content, int chunkSize) {
2000                response.setBody(content);
2001            }
2002        },
2003        END_OF_STREAM() {
2004            @Override void setBody(MockResponse response, byte[] content, int chunkSize) {
2005                response.setBody(content);
2006                response.setSocketPolicy(DISCONNECT_AT_END);
2007                for (Iterator<String> h = response.getHeaders().iterator(); h.hasNext(); ) {
2008                    if (h.next().startsWith("Content-Length:")) {
2009                        h.remove();
2010                        break;
2011                    }
2012                }
2013            }
2014        };
2015
2016        abstract void setBody(MockResponse response, byte[] content, int chunkSize)
2017                throws IOException;
2018
2019        void setBody(MockResponse response, String content, int chunkSize) throws IOException {
2020            setBody(response, content.getBytes("UTF-8"), chunkSize);
2021        }
2022    }
2023
2024    enum ProxyConfig {
2025        NO_PROXY() {
2026            @Override public HttpURLConnection connect(MockWebServer server, URL url)
2027                    throws IOException {
2028                return (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
2029            }
2030        },
2031
2032        CREATE_ARG() {
2033            @Override public HttpURLConnection connect(MockWebServer server, URL url)
2034                    throws IOException {
2035                return (HttpURLConnection) url.openConnection(server.toProxyAddress());
2036            }
2037        },
2038
2039        PROXY_SYSTEM_PROPERTY() {
2040            @Override public HttpURLConnection connect(MockWebServer server, URL url)
2041                    throws IOException {
2042                System.setProperty("proxyHost", "localhost");
2043                System.setProperty("proxyPort", Integer.toString(server.getPort()));
2044                return (HttpURLConnection) url.openConnection();
2045            }
2046        },
2047
2048        HTTP_PROXY_SYSTEM_PROPERTY() {
2049            @Override public HttpURLConnection connect(MockWebServer server, URL url)
2050                    throws IOException {
2051                System.setProperty("http.proxyHost", "localhost");
2052                System.setProperty("http.proxyPort", Integer.toString(server.getPort()));
2053                return (HttpURLConnection) url.openConnection();
2054            }
2055        },
2056
2057        HTTPS_PROXY_SYSTEM_PROPERTY() {
2058            @Override public HttpURLConnection connect(MockWebServer server, URL url)
2059                    throws IOException {
2060                System.setProperty("https.proxyHost", "localhost");
2061                System.setProperty("https.proxyPort", Integer.toString(server.getPort()));
2062                return (HttpURLConnection) url.openConnection();
2063            }
2064        };
2065
2066        public abstract HttpURLConnection connect(MockWebServer server, URL url) throws IOException;
2067    }
2068
2069    private static class RecordingTrustManager implements X509TrustManager {
2070        private final List<String> calls = new ArrayList<String>();
2071
2072        public X509Certificate[] getAcceptedIssuers() {
2073            calls.add("getAcceptedIssuers");
2074            return new X509Certificate[] {};
2075        }
2076
2077        public void checkClientTrusted(X509Certificate[] chain, String authType)
2078                throws CertificateException {
2079            calls.add("checkClientTrusted " + certificatesToString(chain) + " " + authType);
2080        }
2081
2082        public void checkServerTrusted(X509Certificate[] chain, String authType)
2083                throws CertificateException {
2084            calls.add("checkServerTrusted " + certificatesToString(chain) + " " + authType);
2085        }
2086
2087        private String certificatesToString(X509Certificate[] certificates) {
2088            List<String> result = new ArrayList<String>();
2089            for (X509Certificate certificate : certificates) {
2090                result.add(certificate.getSubjectDN() + " " + certificate.getSerialNumber());
2091            }
2092            return result.toString();
2093        }
2094    }
2095
2096    private static class RecordingHostnameVerifier implements HostnameVerifier {
2097        private final List<String> calls = new ArrayList<String>();
2098
2099        public boolean verify(String hostname, SSLSession session) {
2100            calls.add("verify " + hostname);
2101            return true;
2102        }
2103    }
2104}
2105