URLConnectionTest.java revision 6906b0c12dcf3216883d0373973a252812a20d32
1e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes/*
2e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes * Copyright (C) 2009 The Android Open Source Project
3f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes *
4e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes * Licensed under the Apache License, Version 2.0 (the "License");
5e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes * you may not use this file except in compliance with the License.
6e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes * You may obtain a copy of the License at
7f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes *
8e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes *      http://www.apache.org/licenses/LICENSE-2.0
9f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes *
10e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes * Unless required by applicable law or agreed to in writing, software
11e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes * distributed under the License is distributed on an "AS IS" BASIS,
12e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes * See the License for the specific language governing permissions and
14e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes * limitations under the License.
15e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes */
16e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes
17e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughespackage java.net;
18e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes
1951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilsonimport java.io.BufferedReader;
20c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilsonimport java.io.ByteArrayOutputStream;
216247987eb505a482a67f5f19678260d9e7240a5fElliott Hughesimport java.io.IOException;
2251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilsonimport java.io.InputStream;
23e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughesimport java.io.InputStreamReader;
2402f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughesimport java.io.OutputStream;
2551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilsonimport java.util.ArrayList;
2602f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughesimport java.util.Arrays;
2751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilsonimport java.util.Collections;
2851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilsonimport java.util.Iterator;
296247987eb505a482a67f5f19678260d9e7240a5fElliott Hughesimport java.util.List;
3051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilsonimport java.util.Set;
3160476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilsonimport javax.net.ssl.HostnameVerifier;
3260476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilsonimport javax.net.ssl.HttpsURLConnection;
3360476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilsonimport javax.net.ssl.SSLSession;
3460476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilsonimport javax.net.ssl.TestSSLContext;
3551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilsonimport tests.http.DefaultResponseCache;
3660476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilsonimport tests.http.MockResponse;
3760476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilsonimport tests.http.MockWebServer;
3860476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilsonimport tests.http.RecordedRequest;
39e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes
40e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughespublic class URLConnectionTest extends junit.framework.TestCase {
41b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson
4251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    private MockWebServer server = new MockWebServer();
4351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
4451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    @Override protected void tearDown() throws Exception {
4551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        ResponseCache.setDefault(null);
4651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.shutdown();
4751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
4851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
49e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes    // Check that if we don't read to the end of a response, the next request on the
50e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes    // recycled connection doesn't get the unread tail of the first request's response.
51e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes    // http://code.google.com/p/android/issues/detail?id=2939
52e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes    public void test_2939() throws Exception {
53b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        MockResponse response = new MockResponse().setChunkedBody("ABCDE\nFGHIJ\nKLMNO\nPQR", 8);
54b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson
55b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.enqueue(response);
56b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.enqueue(response);
57b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.play();
58b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson
59c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("ABCDE", server.getUrl("/").openConnection(), 5);
60c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("ABCDE", server.getUrl("/").openConnection(), 5);
618baf143a7c8921d07b54adbc66ac1e5b42de5fe6Jesse Wilson    }
628baf143a7c8921d07b54adbc66ac1e5b42de5fe6Jesse Wilson
638baf143a7c8921d07b54adbc66ac1e5b42de5fe6Jesse Wilson    public void testConnectionsArePooled() throws Exception {
64b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        MockResponse response = new MockResponse().setBody("ABCDEFGHIJKLMNOPQR");
65b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson
66b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.enqueue(response);
67b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.enqueue(response);
68b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.enqueue(response);
69b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.play();
70b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson
71c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/").openConnection());
72c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertEquals(0, server.takeRequest().getSequenceNumber());
73c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/").openConnection());
74c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertEquals(1, server.takeRequest().getSequenceNumber());
75c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/").openConnection());
76c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertEquals(2, server.takeRequest().getSequenceNumber());
77c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    }
78c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
79c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    public void testChunkedConnectionsArePooled() throws Exception {
80c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        MockResponse response = new MockResponse().setChunkedBody("ABCDEFGHIJKLMNOPQR", 5);
81c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
82c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        server.enqueue(response);
83c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        server.enqueue(response);
84c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        server.enqueue(response);
85c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        server.play();
86c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
87c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/").openConnection());
88b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        assertEquals(0, server.takeRequest().getSequenceNumber());
89c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/").openConnection());
90b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        assertEquals(1, server.takeRequest().getSequenceNumber());
91c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/").openConnection());
92b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        assertEquals(2, server.takeRequest().getSequenceNumber());
93e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes    }
9402f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes
95b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson    enum WriteKind { BYTE_BY_BYTE, SMALL_BUFFERS, LARGE_BUFFERS }
9602f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes
9702f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    public void test_chunkedUpload_byteByByte() throws Exception {
9851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        doUpload(TransferKind.CHUNKED, WriteKind.BYTE_BY_BYTE);
9902f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    }
10002f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes
10102f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    public void test_chunkedUpload_smallBuffers() throws Exception {
10251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        doUpload(TransferKind.CHUNKED, WriteKind.SMALL_BUFFERS);
10302f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    }
10402f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes
10502f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    public void test_chunkedUpload_largeBuffers() throws Exception {
10651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        doUpload(TransferKind.CHUNKED, WriteKind.LARGE_BUFFERS);
10702f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    }
10802f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes
10902f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    public void test_fixedLengthUpload_byteByByte() throws Exception {
11051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        doUpload(TransferKind.FIXED_LENGTH, WriteKind.BYTE_BY_BYTE);
11102f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    }
11202f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes
11302f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    public void test_fixedLengthUpload_smallBuffers() throws Exception {
11451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        doUpload(TransferKind.FIXED_LENGTH, WriteKind.SMALL_BUFFERS);
11502f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    }
11602f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes
11702f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    public void test_fixedLengthUpload_largeBuffers() throws Exception {
11851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        doUpload(TransferKind.FIXED_LENGTH, WriteKind.LARGE_BUFFERS);
11902f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    }
12002f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes
12151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    private void doUpload(TransferKind uploadKind, WriteKind writeKind) throws Exception {
12202f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        int n = 512*1024;
123b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.setBodyLimit(0);
124b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.enqueue(new MockResponse());
125b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.play();
126b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson
127b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        HttpURLConnection conn = (HttpURLConnection) server.getUrl("/").openConnection();
12802f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        conn.setDoOutput(true);
12902f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        conn.setRequestMethod("POST");
13051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        if (uploadKind == TransferKind.CHUNKED) {
13102f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes            conn.setChunkedStreamingMode(-1);
13202f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        } else {
13302f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes            conn.setFixedLengthStreamingMode(n);
13402f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        }
13502f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        OutputStream out = conn.getOutputStream();
13602f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        if (writeKind == WriteKind.BYTE_BY_BYTE) {
13702f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes            for (int i = 0; i < n; ++i) {
13802f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes                out.write('x');
13902f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes            }
14002f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        } else {
14102f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes            byte[] buf = new byte[writeKind == WriteKind.SMALL_BUFFERS ? 256 : 64*1024];
14202f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes            Arrays.fill(buf, (byte) 'x');
14302f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes            for (int i = 0; i < n; i += buf.length) {
14402f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes                out.write(buf, 0, Math.min(buf.length, n - i));
14502f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes            }
14602f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        }
14702f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        out.close();
1484cb7f05dc68abb23ae54a5891c369062185f2210Elliott Hughes        assertEquals(200, conn.getResponseCode());
149b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        RecordedRequest request = server.takeRequest();
150b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        assertEquals(n, request.getBodySize());
15151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        if (uploadKind == TransferKind.CHUNKED) {
152b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson            assertTrue(request.getChunkSizes().size() > 0);
153b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        } else {
154b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson            assertTrue(request.getChunkSizes().isEmpty());
155b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        }
15602f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    }
1576247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes
15851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    /**
15951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * Test that response caching is consistent with the RI and the spec.
16051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4
16151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     */
1626247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes    public void test_responseCaching() throws Exception {
1636247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        // Test each documented HTTP/1.1 code, plus the first unused value in each range.
1646247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
1656247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes
1666247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        // We can't test 100 because it's not really a response.
1676247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        // assertCached(false, 100);
1686247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 101);
1696247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 102);
1706247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(true,  200);
1716247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 201);
1726247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 202);
1736247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(true,  203);
1746247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 204);
1756247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 205);
1766247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(true,  206);
1776247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 207);
17851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        // (See test_responseCaching_300.)
1796247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(true,  301);
1806247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        for (int i = 302; i <= 308; ++i) {
1816247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes            assertCached(false, i);
1826247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        }
1836247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        for (int i = 400; i <= 406; ++i) {
1846247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes            assertCached(false, i);
1856247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        }
1866247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        // (See test_responseCaching_407.)
1876247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 408);
1886247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 409);
18951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        // (See test_responseCaching_410.)
1906247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        for (int i = 411; i <= 418; ++i) {
1916247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes            assertCached(false, i);
1926247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        }
1936247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        for (int i = 500; i <= 506; ++i) {
1946247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes            assertCached(false, i);
1956247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        }
1966247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes    }
1976247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes
19851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void test_responseCaching_300() throws Exception {
19951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        // TODO: fix this for android
20051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertCached(false, 300);
20151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
20251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
2036247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes    public void test_responseCaching_407() throws Exception {
2046247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        // This test will fail on Android because we throw if we're not using a proxy.
2056247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        // This isn't true of the RI, but it seems like useful debugging behavior.
2066247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 407);
2076247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes    }
2086247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes
20951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void test_responseCaching_410() throws Exception {
21051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        // the HTTP spec permits caching 410s, but the RI doesn't.
21151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertCached(false, 410);
21251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
21351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
2146247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes    private void assertCached(boolean shouldPut, int responseCode) throws Exception {
21551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server = new MockWebServer();
216b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.enqueue(new MockResponse()
217b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson                .setResponseCode(responseCode)
21851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                .setBody("ABCDE")
219b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson                .addHeader("WWW-Authenticate: challenge"));
220b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.play();
221b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson
22251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        DefaultResponseCache responseCache = new DefaultResponseCache();
22351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        ResponseCache.setDefault(responseCache);
22451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        URL url = server.getUrl("/");
22551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
2266247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertEquals(responseCode, conn.getResponseCode());
2276247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes
22851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        // exhaust the content stream
22951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        try {
23051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            // TODO: remove special case once testUnauthorizedResponseHandling() is fixed
23151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            if (responseCode != 401) {
23251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                readAscii(conn.getInputStream(), Integer.MAX_VALUE);
23351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            }
23451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        } catch (IOException ignored) {
23551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        }
23651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
23751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        Set<URI> expectedCachedUris = shouldPut
23851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                ? Collections.singleton(url.toURI())
23951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                : Collections.<URI>emptySet();
24051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(Integer.toString(responseCode),
24151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                expectedCachedUris, responseCache.getContents().keySet());
24251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.shutdown(); // tearDown() isn't sufficient; this test starts multiple servers
2436247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes    }
24460476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
24560476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson    public void testConnectViaHttps() throws IOException, InterruptedException {
24660476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
24760476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
24860476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        server.useHttps(testSSLContext.sslContext.getSocketFactory(), false);
24960476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        server.enqueue(new MockResponse()
25060476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson                .setResponseCode(200)
25160476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson                .setBody("this response comes via HTTPS"));
25260476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        server.play();
25360476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
25460476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        URL url = new URL("https://localhost:" + server.getPort() + "/foo");
25560476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
25660476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.sslContext.getSocketFactory());
25760476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
258c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("this response comes via HTTPS", connection);
25960476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
26060476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        RecordedRequest request = server.takeRequest();
26160476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        assertEquals("GET /foo HTTP/1.1", request.getRequestLine());
26260476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson    }
26360476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
26460476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson    public void testConnectViaProxy() throws IOException, InterruptedException {
26560476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        MockResponse mockResponse = new MockResponse()
26660476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson                .setResponseCode(200)
26760476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson                .setBody("this response comes via a proxy");
26851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(mockResponse);
26951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.play();
27060476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
27160476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        URLConnection connection = new URL("http://android.com/foo").openConnection(
27251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                server.toProxyAddress());
273c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("this response comes via a proxy", connection);
27460476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
27551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        RecordedRequest request = server.takeRequest();
27660476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        assertEquals("GET http://android.com/foo HTTP/1.1", request.getRequestLine());
27760476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        assertContains(request.getHeaders(), "Host: android.com");
27860476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson    }
27960476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
280c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    public void testContentDisagreesWithContentLengthHeader() throws IOException {
281c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        server.enqueue(new MockResponse()
282c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson                .setResponseCode(200)
283c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson                .setBody("abc\r\nYOU SHOULD NOT SEE THIS")
284c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson                .clearHeaders()
285c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson                .addHeader("Content-Length: 3"));
286c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        server.play();
287c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
288c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("abc", server.getUrl("/").openConnection());
289c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    }
290c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
291c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    public void testContentDisagreesWithChunkedHeader() throws IOException {
292c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        MockResponse mockResponse = new MockResponse();
293c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        mockResponse.setResponseCode(200);
294c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        mockResponse.setChunkedBody("abc", 3);
295c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
296c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        bytesOut.write(mockResponse.getBody());
297c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        bytesOut.write("\r\nYOU SHOULD NOT SEE THIS".getBytes());
298c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        mockResponse.setBody(bytesOut.toByteArray());
299c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        mockResponse.clearHeaders();
300c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        mockResponse.addHeader("Transfer-encoding: chunked");
301c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
302c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        server.enqueue(mockResponse);
303c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        server.play();
304c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
305c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("abc", server.getUrl("/").openConnection());
306c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    }
307c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
30860476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson    public void testConnectViaHttpProxyToHttps() throws IOException, InterruptedException {
30960476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
31060476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
31151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.useHttps(testSSLContext.sslContext.getSocketFactory(), true);
31251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(200).clearHeaders()); // for CONNECT
31351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(new MockResponse()
31460476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson                .setResponseCode(200)
31560476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson                .setBody("this response comes via a secure proxy"));
31651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.play();
31760476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
31860476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        URL url = new URL("https://android.com/foo");
31960476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(
32051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                server.toProxyAddress());
32160476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.sslContext.getSocketFactory());
32260476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        connection.setHostnameVerifier(new HostnameVerifier() {
32360476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson            public boolean verify(String hostname, SSLSession session) {
32460476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson                return true;
32560476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson            }
32660476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        });
32760476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
328c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("this response comes via a secure proxy", connection);
32960476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
33051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        RecordedRequest connect = server.takeRequest();
33160476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        assertEquals("Connect line failure on proxy",
33260476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson                "CONNECT android.com:443 HTTP/1.1", connect.getRequestLine());
33360476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        assertContains(connect.getHeaders(), "Host: android.com");
33460476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
33551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        RecordedRequest get = server.takeRequest();
33660476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        assertEquals("GET /foo HTTP/1.1", get.getRequestLine());
33760476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        assertContains(get.getHeaders(), "Host: android.com");
33860476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson    }
33960476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
34051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testResponseCachingAndInputStreamSkipWithFixedLength() throws IOException {
34151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testResponseCaching(TransferKind.FIXED_LENGTH);
34251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
34351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
34451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testResponseCachingAndInputStreamSkipWithChunkedEncoding() throws IOException {
34551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testResponseCaching(TransferKind.CHUNKED);
34651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
34751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
34851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testResponseCachingAndInputStreamSkipWithNoLengthHeaders() throws IOException {
34951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testResponseCaching(TransferKind.END_OF_STREAM);
35051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
35151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
35251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    /**
35351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * HttpURLConnection.getInputStream().skip(long) causes ResponseCache corruption
35451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * http://code.google.com/p/android/issues/detail?id=8175
35551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     */
35651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    private void testResponseCaching(TransferKind transferKind) throws IOException {
35751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        MockResponse response = new MockResponse();
35851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        transferKind.setBody(response, "I love puppies but hate spiders", 1);
35951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(response);
36051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.play();
36151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
36251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        DefaultResponseCache cache = new DefaultResponseCache();
36351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        ResponseCache.setDefault(cache);
36451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
36551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        // Make sure that calling skip() doesn't omit bytes from the cache.
36651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        URLConnection urlConnection = server.getUrl("/").openConnection();
36751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        InputStream in = urlConnection.getInputStream();
36851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals("I love ", readAscii(in, "I love ".length()));
36951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        reliableSkip(in, "puppies but hate ".length());
37051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals("spiders", readAscii(in, "spiders".length()));
37151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(-1, in.read());
37251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        in.close();
37351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getSuccessCount());
37451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
37551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        urlConnection = server.getUrl("/").openConnection(); // this response is cached!
37651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        in = urlConnection.getInputStream();
37751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals("I love puppies but hate spiders",
37851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                readAscii(in, "I love puppies but hate spiders".length()));
37951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(-1, in.read());
38051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getMissCount());
38151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getHitCount());
38251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
38351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
38451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    private void reliableSkip(InputStream in, int length) throws IOException {
38551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        while (length > 0) {
38651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            length -= in.skip(length);
38751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        }
38851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
38951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
39051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    /**
39151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * Reads {@code count} characters from the stream. If the stream is
39251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * exhausted before {@code count} characters can be read, the remaining
39351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * characters are returned and the stream is closed.
39451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     */
39551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    private String readAscii(InputStream in, int count) throws IOException {
39651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        StringBuilder result = new StringBuilder();
39751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        for (int i = 0; i < count; i++) {
39851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            int value = in.read();
39951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            if (value == -1) {
40051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                in.close();
40151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                break;
40251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            }
40351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            result.append((char) value);
40451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        }
40551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        return result.toString();
40651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
40751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
40851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testServerDisconnectsPrematurelyWithContentLengthHeader() throws IOException {
40951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testServerPrematureDisconnect(TransferKind.FIXED_LENGTH);
41051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
41151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
41251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testServerDisconnectsPrematurelyWithChunkedEncoding() throws IOException {
41351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testServerPrematureDisconnect(TransferKind.CHUNKED);
41451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
41551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
41651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testServerDisconnectsPrematurelyWithNoLengthHeaders() throws IOException {
41751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        /*
41851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson         * Intentionally empty. This case doesn't make sense because there's no
41951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson         * such thing as a premature disconnect when the disconnect itself
42051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson         * indicates the end of the data stream.
42151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson         */
42251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
42351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
42451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    private void testServerPrematureDisconnect(TransferKind transferKind) throws IOException {
42551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        MockResponse response = new MockResponse();
42651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        transferKind.setBody(response, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 16);
42751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(truncateViolently(response, 16));
42851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(new MockResponse().setBody("Request #2"));
42951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.play();
43051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
43151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        DefaultResponseCache cache = new DefaultResponseCache();
43251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        ResponseCache.setDefault(cache);
43351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
43451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        BufferedReader reader = new BufferedReader(new InputStreamReader(
43551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                server.getUrl("/").openConnection().getInputStream()));
43651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals("ABCDE", reader.readLine());
43751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        try {
43851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            reader.readLine();
43951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            fail("This implementation silently ignored a truncated HTTP body.");
44051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        } catch (IOException expected) {
44151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        }
44251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
44351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getAbortCount());
44451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(0, cache.getSuccessCount());
44551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertContent("Request #2", server.getUrl("/").openConnection());
44651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getAbortCount());
44751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getSuccessCount());
44851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
44951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
45051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testClientPrematureDisconnectWithContentLengthHeader() throws IOException {
45151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testClientPrematureDisconnect(TransferKind.FIXED_LENGTH);
45251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
45351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
45451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testClientPrematureDisconnectWithChunkedEncoding() throws IOException {
45551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testClientPrematureDisconnect(TransferKind.CHUNKED);
45651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
45751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
45851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testClientPrematureDisconnectWithNoLengthHeaders() throws IOException {
45951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testClientPrematureDisconnect(TransferKind.END_OF_STREAM);
46051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
46151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
46251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    private void testClientPrematureDisconnect(TransferKind transferKind) throws IOException {
46351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        MockResponse response = new MockResponse();
46451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        transferKind.setBody(response, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 1024);
46551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(response);
46651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(new MockResponse().setBody("Request #2"));
46751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.play();
46851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
46951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        DefaultResponseCache cache = new DefaultResponseCache();
47051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        ResponseCache.setDefault(cache);
47151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
47251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        InputStream in = server.getUrl("/").openConnection().getInputStream();
47351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals("ABCDE", readAscii(in, 5));
47451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        in.close();
47551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        try {
47651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            in.read();
47751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            fail("Expected an IOException because the stream is closed.");
47851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        } catch (IOException expected) {
47951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        }
48051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
48151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getAbortCount());
48251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(0, cache.getSuccessCount());
48351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertContent("Request #2", server.getUrl("/").openConnection());
48451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getAbortCount());
48551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getSuccessCount());
48651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
48751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
48851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    /**
48951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * Shortens the body of {@code response} but not the corresponding headers.
49051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * Only useful to test how clients respond to the premature conclusion of
49151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * the HTTP body.
49251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     */
49351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    private MockResponse truncateViolently(MockResponse response, int numBytesToKeep) {
49451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        response.setDisconnectAtEnd(true);
49551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        List<String> headers = new ArrayList<String>(response.getHeaders());
49651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        response.setBody(Arrays.copyOfRange(response.getBody(), 0, numBytesToKeep));
49751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        response.getHeaders().clear();
49851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        response.getHeaders().addAll(headers);
49951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        return response;
50051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
50151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
50251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testMarkAndResetWithContentLengthHeader() throws IOException {
50351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testMarkAndReset(TransferKind.FIXED_LENGTH);
50451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
50551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
50651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testMarkAndResetWithChunkedEncoding() throws IOException {
50751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testMarkAndReset(TransferKind.CHUNKED);
50851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
50951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
51051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testMarkAndResetWithNoLengthHeaders() throws IOException {
51151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testMarkAndReset(TransferKind.END_OF_STREAM);
51251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
51351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
51451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testMarkAndReset(TransferKind transferKind) throws IOException {
51551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        MockResponse response = new MockResponse();
51651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        transferKind.setBody(response, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1024);
51751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(response);
51851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.play();
51951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
52051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        DefaultResponseCache cache = new DefaultResponseCache();
52151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        ResponseCache.setDefault(cache);
52251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
52351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        InputStream in = server.getUrl("/").openConnection().getInputStream();
52451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertFalse("This implementation claims to support mark().", in.markSupported());
52551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        in.mark(5);
52651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals("ABCDE", readAscii(in, 5));
52751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        try {
52851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            in.reset();
52951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            fail();
53051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        } catch (IOException expected) {
53151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        }
53251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals("FGHIJKLMNOPQRSTUVWXYZ", readAscii(in, Integer.MAX_VALUE));
53351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
53451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertContent("ABCDEFGHIJKLMNOPQRSTUVWXYZ", server.getUrl("/").openConnection());
53551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getSuccessCount());
53651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getHitCount());
53751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
53851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
53951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    /**
54051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * We've had a bug where we forget the HTTP response when we see response
54151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * code 401. This causes a new HTTP request to be issued for every call into
54251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * the URLConnection.
54351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     */
54451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testUnauthorizedResponseHandling() throws IOException {
54551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        MockResponse response = new MockResponse()
54651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                .addHeader("WWW-Authenticate: challenge")
54751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                .setResponseCode(401) // UNAUTHORIZED
54851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                .setBody("Unauthorized");
54951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(response);
55051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(response);
55151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(response);
55251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.play();
55351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
55451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        URL url = server.getUrl("/");
55551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
55651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
55751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(401, conn.getResponseCode());
55851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(401, conn.getResponseCode());
55951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(401, conn.getResponseCode());
56051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, server.getRequestCount());
56151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
56251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
5636906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson    public void testNonHexChunkSize() throws IOException {
5646906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        server.enqueue(new MockResponse()
5656906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson                .setBody("5\r\nABCDE\r\nG\r\nFGHIJKLMNOPQRSTU\r\n0\r\n\r\n")
5666906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson                .clearHeaders()
5676906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson                .addHeader("Transfer-encoding: chunked"));
5686906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        server.play();
5696906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson
5706906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
5716906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        try {
5726906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson            readAscii(connection.getInputStream(), Integer.MAX_VALUE);
5736906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson            fail();
5746906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        } catch (IOException e) {
5756906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        }
5766906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson    }
5776906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson
5786906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson    public void testMissingChunkBody() throws IOException {
5796906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        server.enqueue(new MockResponse()
5806906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson                .setBody("5")
5816906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson                .clearHeaders()
5826906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson                .addHeader("Transfer-encoding: chunked")
5836906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson                .setDisconnectAtEnd(true));
5846906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        server.play();
5856906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson
5866906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
5876906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        try {
5886906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson            readAscii(connection.getInputStream(), Integer.MAX_VALUE);
5896906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson            fail();
5906906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        } catch (IOException e) {
5916906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        }
5926906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson    }
5936906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson
594c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    /**
595c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson     * Reads at most {@code limit} characters from {@code in} and asserts that
596c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson     * content equals {@code expected}.
597c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson     */
598c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    private void assertContent(String expected, URLConnection connection, int limit)
599c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson            throws IOException {
60051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(expected, readAscii(connection.getInputStream(), limit));
601c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        ((HttpURLConnection) connection).disconnect();
602c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    }
603c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
604c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    private void assertContent(String expected, URLConnection connection) throws IOException {
605c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent(expected, connection, Integer.MAX_VALUE);
606c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    }
607c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
60860476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson    private void assertContains(List<String> headers, String header) {
60960476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        assertTrue(headers.toString(), headers.contains(header));
61060476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson    }
61151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
61251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    enum TransferKind {
61351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        CHUNKED() {
61451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            @Override void setBody(MockResponse response, String content, int chunkSize)
61551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                    throws IOException {
61651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                response.setChunkedBody(content, chunkSize);
61751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            }
61851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        },
61951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        FIXED_LENGTH() {
62051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            @Override void setBody(MockResponse response, String content, int chunkSize) {
62151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                response.setBody(content);
62251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            }
62351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        },
62451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        END_OF_STREAM() {
62551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            @Override void setBody(MockResponse response, String content, int chunkSize) {
62651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                response.setBody(content);
62751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                response.setDisconnectAtEnd(true);
62851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                for (Iterator<String> h = response.getHeaders().iterator(); h.hasNext(); ) {
62951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                    if (h.next().startsWith("Content-Length:")) {
63051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                        h.remove();
63151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                        break;
63251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                    }
63351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                }
63451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            }
63551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        };
63651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
63751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        abstract void setBody(MockResponse response, String content, int chunkSize)
63851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                throws IOException;
63951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
640e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes}
641