URLConnectionTest.java revision 096aac7b8a607d3da237900f52cab1c5066bf992
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;
31deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilsonimport java.util.zip.GZIPInputStream;
32deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilsonimport java.util.zip.GZIPOutputStream;
3360476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilsonimport javax.net.ssl.HostnameVerifier;
3460476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilsonimport javax.net.ssl.HttpsURLConnection;
35096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilsonimport javax.net.ssl.SSLException;
36096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilsonimport javax.net.ssl.SSLHandshakeException;
3760476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilsonimport javax.net.ssl.SSLSession;
3860476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilsonimport javax.net.ssl.TestSSLContext;
3951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilsonimport tests.http.DefaultResponseCache;
4060476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilsonimport tests.http.MockResponse;
4160476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilsonimport tests.http.MockWebServer;
4260476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilsonimport tests.http.RecordedRequest;
43e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes
44e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughespublic class URLConnectionTest extends junit.framework.TestCase {
45b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson
4651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    private MockWebServer server = new MockWebServer();
4751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
4851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    @Override protected void tearDown() throws Exception {
4951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        ResponseCache.setDefault(null);
5051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.shutdown();
5151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
5251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
53e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes    // Check that if we don't read to the end of a response, the next request on the
54e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes    // recycled connection doesn't get the unread tail of the first request's response.
55e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes    // http://code.google.com/p/android/issues/detail?id=2939
56e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes    public void test_2939() throws Exception {
57b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        MockResponse response = new MockResponse().setChunkedBody("ABCDE\nFGHIJ\nKLMNO\nPQR", 8);
58b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson
59b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.enqueue(response);
60b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.enqueue(response);
61b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.play();
62b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson
63c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("ABCDE", server.getUrl("/").openConnection(), 5);
64c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("ABCDE", server.getUrl("/").openConnection(), 5);
658baf143a7c8921d07b54adbc66ac1e5b42de5fe6Jesse Wilson    }
668baf143a7c8921d07b54adbc66ac1e5b42de5fe6Jesse Wilson
678baf143a7c8921d07b54adbc66ac1e5b42de5fe6Jesse Wilson    public void testConnectionsArePooled() throws Exception {
68b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        MockResponse response = new MockResponse().setBody("ABCDEFGHIJKLMNOPQR");
69b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson
70b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.enqueue(response);
71b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.enqueue(response);
72b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.enqueue(response);
73b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.play();
74b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson
75c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/").openConnection());
76c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertEquals(0, server.takeRequest().getSequenceNumber());
77c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/").openConnection());
78c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertEquals(1, server.takeRequest().getSequenceNumber());
79c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/").openConnection());
80c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertEquals(2, server.takeRequest().getSequenceNumber());
81c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    }
82c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
83c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    public void testChunkedConnectionsArePooled() throws Exception {
84c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        MockResponse response = new MockResponse().setChunkedBody("ABCDEFGHIJKLMNOPQR", 5);
85c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
86c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        server.enqueue(response);
87c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        server.enqueue(response);
88c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        server.enqueue(response);
89c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        server.play();
90c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
91c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/").openConnection());
92b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        assertEquals(0, server.takeRequest().getSequenceNumber());
93c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/").openConnection());
94b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        assertEquals(1, server.takeRequest().getSequenceNumber());
95c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("ABCDEFGHIJKLMNOPQR", server.getUrl("/").openConnection());
96b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        assertEquals(2, server.takeRequest().getSequenceNumber());
97e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes    }
9802f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes
99b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson    enum WriteKind { BYTE_BY_BYTE, SMALL_BUFFERS, LARGE_BUFFERS }
10002f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes
10102f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    public void test_chunkedUpload_byteByByte() throws Exception {
10251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        doUpload(TransferKind.CHUNKED, WriteKind.BYTE_BY_BYTE);
10302f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    }
10402f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes
10502f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    public void test_chunkedUpload_smallBuffers() throws Exception {
10651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        doUpload(TransferKind.CHUNKED, WriteKind.SMALL_BUFFERS);
10702f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    }
10802f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes
10902f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    public void test_chunkedUpload_largeBuffers() throws Exception {
11051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        doUpload(TransferKind.CHUNKED, WriteKind.LARGE_BUFFERS);
11102f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    }
11202f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes
11302f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    public void test_fixedLengthUpload_byteByByte() throws Exception {
11451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        doUpload(TransferKind.FIXED_LENGTH, WriteKind.BYTE_BY_BYTE);
11502f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    }
11602f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes
11702f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    public void test_fixedLengthUpload_smallBuffers() throws Exception {
11851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        doUpload(TransferKind.FIXED_LENGTH, WriteKind.SMALL_BUFFERS);
11902f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    }
12002f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes
12102f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    public void test_fixedLengthUpload_largeBuffers() throws Exception {
12251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        doUpload(TransferKind.FIXED_LENGTH, WriteKind.LARGE_BUFFERS);
12302f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    }
12402f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes
12551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    private void doUpload(TransferKind uploadKind, WriteKind writeKind) throws Exception {
12602f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        int n = 512*1024;
127b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.setBodyLimit(0);
128b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.enqueue(new MockResponse());
129b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.play();
130b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson
131b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        HttpURLConnection conn = (HttpURLConnection) server.getUrl("/").openConnection();
13202f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        conn.setDoOutput(true);
13302f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        conn.setRequestMethod("POST");
13451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        if (uploadKind == TransferKind.CHUNKED) {
13502f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes            conn.setChunkedStreamingMode(-1);
13602f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        } else {
13702f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes            conn.setFixedLengthStreamingMode(n);
13802f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        }
13902f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        OutputStream out = conn.getOutputStream();
14002f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        if (writeKind == WriteKind.BYTE_BY_BYTE) {
14102f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes            for (int i = 0; i < n; ++i) {
14202f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes                out.write('x');
14302f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes            }
14402f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        } else {
14502f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes            byte[] buf = new byte[writeKind == WriteKind.SMALL_BUFFERS ? 256 : 64*1024];
14602f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes            Arrays.fill(buf, (byte) 'x');
14702f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes            for (int i = 0; i < n; i += buf.length) {
14802f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes                out.write(buf, 0, Math.min(buf.length, n - i));
14902f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes            }
15002f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        }
15102f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes        out.close();
1524cb7f05dc68abb23ae54a5891c369062185f2210Elliott Hughes        assertEquals(200, conn.getResponseCode());
153b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        RecordedRequest request = server.takeRequest();
154b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        assertEquals(n, request.getBodySize());
15551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        if (uploadKind == TransferKind.CHUNKED) {
156b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson            assertTrue(request.getChunkSizes().size() > 0);
157b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        } else {
158b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson            assertTrue(request.getChunkSizes().isEmpty());
159b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        }
16002f0cb2eb84a112fcf644d7d1fd0b5f94ea2f03bElliott Hughes    }
1616247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes
16251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    /**
16351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * Test that response caching is consistent with the RI and the spec.
16451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4
16551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     */
1666247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes    public void test_responseCaching() throws Exception {
1676247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        // Test each documented HTTP/1.1 code, plus the first unused value in each range.
1686247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
1696247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes
1706247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        // We can't test 100 because it's not really a response.
1716247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        // assertCached(false, 100);
1726247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 101);
1736247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 102);
1746247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(true,  200);
1756247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 201);
1766247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 202);
1776247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(true,  203);
1786247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 204);
1796247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 205);
1806247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(true,  206);
1816247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 207);
18251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        // (See test_responseCaching_300.)
1836247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(true,  301);
1846247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        for (int i = 302; i <= 308; ++i) {
1856247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes            assertCached(false, i);
1866247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        }
1876247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        for (int i = 400; i <= 406; ++i) {
1886247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes            assertCached(false, i);
1896247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        }
1906247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        // (See test_responseCaching_407.)
1916247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 408);
1926247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 409);
19351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        // (See test_responseCaching_410.)
1946247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        for (int i = 411; i <= 418; ++i) {
1956247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes            assertCached(false, i);
1966247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        }
1976247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        for (int i = 500; i <= 506; ++i) {
1986247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes            assertCached(false, i);
1996247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        }
2006247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes    }
2016247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes
20251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void test_responseCaching_300() throws Exception {
20351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        // TODO: fix this for android
20451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertCached(false, 300);
20551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
20651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
2076247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes    public void test_responseCaching_407() throws Exception {
2086247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        // This test will fail on Android because we throw if we're not using a proxy.
2096247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        // This isn't true of the RI, but it seems like useful debugging behavior.
2106247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertCached(false, 407);
2116247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes    }
2126247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes
21351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void test_responseCaching_410() throws Exception {
21451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        // the HTTP spec permits caching 410s, but the RI doesn't.
21551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertCached(false, 410);
21651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
21751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
2186247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes    private void assertCached(boolean shouldPut, int responseCode) throws Exception {
21951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server = new MockWebServer();
220b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.enqueue(new MockResponse()
221b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson                .setResponseCode(responseCode)
22251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                .setBody("ABCDE")
223b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson                .addHeader("WWW-Authenticate: challenge"));
224b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson        server.play();
225b1b5baac449d2725002338735f4db34bec8fd001Jesse Wilson
22651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        DefaultResponseCache responseCache = new DefaultResponseCache();
22751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        ResponseCache.setDefault(responseCache);
22851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        URL url = server.getUrl("/");
22951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
2306247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes        assertEquals(responseCode, conn.getResponseCode());
2316247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes
23251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        // exhaust the content stream
23351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        try {
23451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            // TODO: remove special case once testUnauthorizedResponseHandling() is fixed
23551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            if (responseCode != 401) {
23651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                readAscii(conn.getInputStream(), Integer.MAX_VALUE);
23751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            }
23851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        } catch (IOException ignored) {
23951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        }
24051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
24151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        Set<URI> expectedCachedUris = shouldPut
24251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                ? Collections.singleton(url.toURI())
24351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                : Collections.<URI>emptySet();
24451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(Integer.toString(responseCode),
24551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                expectedCachedUris, responseCache.getContents().keySet());
24651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.shutdown(); // tearDown() isn't sufficient; this test starts multiple servers
2476247987eb505a482a67f5f19678260d9e7240a5fElliott Hughes    }
24860476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
24960476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson    public void testConnectViaHttps() throws IOException, InterruptedException {
25060476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
25160476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
25260476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        server.useHttps(testSSLContext.sslContext.getSocketFactory(), false);
25360476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        server.enqueue(new MockResponse()
25460476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson                .setResponseCode(200)
25560476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson                .setBody("this response comes via HTTPS"));
25660476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        server.play();
25760476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
258096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/foo").openConnection();
25960476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.sslContext.getSocketFactory());
26060476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
261c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("this response comes via HTTPS", connection);
26260476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
26360476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        RecordedRequest request = server.takeRequest();
26460476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        assertEquals("GET /foo HTTP/1.1", request.getRequestLine());
26560476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson    }
26660476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
267096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson    public void testConnectViaHttpsReusingConnections() throws IOException, InterruptedException {
268096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
269096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson
270096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson        server.useHttps(testSSLContext.sslContext.getSocketFactory(), false);
271096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson        server.enqueue(new MockResponse().setBody("this response comes via HTTPS"));
272096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson        server.enqueue(new MockResponse().setBody("another response via HTTPS"));
273096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson        server.play();
274096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson
275096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson        // install a custom SSL socket factory so the server can be authorized
276096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
277096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.sslContext.getSocketFactory());
278096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson        assertContent("this response comes via HTTPS", connection);
279096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson
280096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson        // without an SSL socket factory, the connection should fail
281096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson        connection = (HttpsURLConnection) server.getUrl("/").openConnection();
282096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson        try {
283096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson            readAscii(connection.getInputStream(), Integer.MAX_VALUE);
284096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson            fail();
285096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson        } catch (SSLException expected) {
286096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson        }
287096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson    }
288096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson
28960476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson    public void testConnectViaProxy() throws IOException, InterruptedException {
29060476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        MockResponse mockResponse = new MockResponse()
29160476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson                .setResponseCode(200)
29260476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson                .setBody("this response comes via a proxy");
29351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(mockResponse);
29451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.play();
29560476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
29660476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        URLConnection connection = new URL("http://android.com/foo").openConnection(
29751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                server.toProxyAddress());
298c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("this response comes via a proxy", connection);
29960476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
30051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        RecordedRequest request = server.takeRequest();
30160476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        assertEquals("GET http://android.com/foo HTTP/1.1", request.getRequestLine());
30260476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        assertContains(request.getHeaders(), "Host: android.com");
30360476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson    }
30460476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
305c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    public void testContentDisagreesWithContentLengthHeader() throws IOException {
306c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        server.enqueue(new MockResponse()
307c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson                .setResponseCode(200)
308c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson                .setBody("abc\r\nYOU SHOULD NOT SEE THIS")
309c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson                .clearHeaders()
310c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson                .addHeader("Content-Length: 3"));
311c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        server.play();
312c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
313c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("abc", server.getUrl("/").openConnection());
314c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    }
315c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
316c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    public void testContentDisagreesWithChunkedHeader() throws IOException {
317c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        MockResponse mockResponse = new MockResponse();
318c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        mockResponse.setResponseCode(200);
319c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        mockResponse.setChunkedBody("abc", 3);
320c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
321c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        bytesOut.write(mockResponse.getBody());
322c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        bytesOut.write("\r\nYOU SHOULD NOT SEE THIS".getBytes());
323c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        mockResponse.setBody(bytesOut.toByteArray());
324c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        mockResponse.clearHeaders();
325c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        mockResponse.addHeader("Transfer-encoding: chunked");
326c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
327c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        server.enqueue(mockResponse);
328c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        server.play();
329c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
330c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("abc", server.getUrl("/").openConnection());
331c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    }
332c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
33360476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson    public void testConnectViaHttpProxyToHttps() throws IOException, InterruptedException {
33460476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        TestSSLContext testSSLContext = TestSSLContext.create();
33560476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
33651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.useHttps(testSSLContext.sslContext.getSocketFactory(), true);
33751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(new MockResponse().setResponseCode(200).clearHeaders()); // for CONNECT
33851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(new MockResponse()
33960476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson                .setResponseCode(200)
34060476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson                .setBody("this response comes via a secure proxy"));
34151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.play();
34260476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
34360476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        URL url = new URL("https://android.com/foo");
34460476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(
34551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                server.toProxyAddress());
34660476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        connection.setSSLSocketFactory(testSSLContext.sslContext.getSocketFactory());
34760476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        connection.setHostnameVerifier(new HostnameVerifier() {
34860476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson            public boolean verify(String hostname, SSLSession session) {
34960476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson                return true;
35060476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson            }
35160476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        });
35260476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
353c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent("this response comes via a secure proxy", connection);
35460476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
35551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        RecordedRequest connect = server.takeRequest();
35660476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        assertEquals("Connect line failure on proxy",
35760476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson                "CONNECT android.com:443 HTTP/1.1", connect.getRequestLine());
35860476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        assertContains(connect.getHeaders(), "Host: android.com");
35960476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
36051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        RecordedRequest get = server.takeRequest();
36160476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        assertEquals("GET /foo HTTP/1.1", get.getRequestLine());
36260476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        assertContains(get.getHeaders(), "Host: android.com");
36360476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson    }
36460476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson
36551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testResponseCachingAndInputStreamSkipWithFixedLength() throws IOException {
36651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testResponseCaching(TransferKind.FIXED_LENGTH);
36751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
36851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
36951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testResponseCachingAndInputStreamSkipWithChunkedEncoding() throws IOException {
37051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testResponseCaching(TransferKind.CHUNKED);
37151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
37251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
37351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testResponseCachingAndInputStreamSkipWithNoLengthHeaders() throws IOException {
37451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testResponseCaching(TransferKind.END_OF_STREAM);
37551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
37651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
37751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    /**
37851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * HttpURLConnection.getInputStream().skip(long) causes ResponseCache corruption
37951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * http://code.google.com/p/android/issues/detail?id=8175
38051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     */
38151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    private void testResponseCaching(TransferKind transferKind) throws IOException {
38251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        MockResponse response = new MockResponse();
38351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        transferKind.setBody(response, "I love puppies but hate spiders", 1);
38451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(response);
38551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.play();
38651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
38751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        DefaultResponseCache cache = new DefaultResponseCache();
38851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        ResponseCache.setDefault(cache);
38951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
39051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        // Make sure that calling skip() doesn't omit bytes from the cache.
39151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        URLConnection urlConnection = server.getUrl("/").openConnection();
39251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        InputStream in = urlConnection.getInputStream();
39351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals("I love ", readAscii(in, "I love ".length()));
39451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        reliableSkip(in, "puppies but hate ".length());
39551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals("spiders", readAscii(in, "spiders".length()));
39651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(-1, in.read());
39751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        in.close();
39851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getSuccessCount());
399096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson        assertEquals(0, cache.getAbortCount());
40051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
40151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        urlConnection = server.getUrl("/").openConnection(); // this response is cached!
40251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        in = urlConnection.getInputStream();
40351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals("I love puppies but hate spiders",
40451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                readAscii(in, "I love puppies but hate spiders".length()));
40551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(-1, in.read());
40651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getMissCount());
40751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getHitCount());
408096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson        assertEquals(1, cache.getSuccessCount());
409096aac7b8a607d3da237900f52cab1c5066bf992Jesse Wilson        assertEquals(0, cache.getAbortCount());
41051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
41151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
41251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    private void reliableSkip(InputStream in, int length) throws IOException {
41351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        while (length > 0) {
41451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            length -= in.skip(length);
41551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        }
41651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
41751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
41851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    /**
41951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * Reads {@code count} characters from the stream. If the stream is
42051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * exhausted before {@code count} characters can be read, the remaining
42151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * characters are returned and the stream is closed.
42251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     */
42351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    private String readAscii(InputStream in, int count) throws IOException {
42451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        StringBuilder result = new StringBuilder();
42551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        for (int i = 0; i < count; i++) {
42651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            int value = in.read();
42751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            if (value == -1) {
42851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                in.close();
42951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                break;
43051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            }
43151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            result.append((char) value);
43251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        }
43351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        return result.toString();
43451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
43551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
43651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testServerDisconnectsPrematurelyWithContentLengthHeader() throws IOException {
43751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testServerPrematureDisconnect(TransferKind.FIXED_LENGTH);
43851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
43951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
44051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testServerDisconnectsPrematurelyWithChunkedEncoding() throws IOException {
44151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testServerPrematureDisconnect(TransferKind.CHUNKED);
44251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
44351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
44451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testServerDisconnectsPrematurelyWithNoLengthHeaders() throws IOException {
44551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        /*
44651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson         * Intentionally empty. This case doesn't make sense because there's no
44751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson         * such thing as a premature disconnect when the disconnect itself
44851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson         * indicates the end of the data stream.
44951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson         */
45051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
45151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
45251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    private void testServerPrematureDisconnect(TransferKind transferKind) throws IOException {
45351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        MockResponse response = new MockResponse();
45451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        transferKind.setBody(response, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 16);
45551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(truncateViolently(response, 16));
45651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(new MockResponse().setBody("Request #2"));
45751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.play();
45851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
45951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        DefaultResponseCache cache = new DefaultResponseCache();
46051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        ResponseCache.setDefault(cache);
46151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
46251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        BufferedReader reader = new BufferedReader(new InputStreamReader(
46351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                server.getUrl("/").openConnection().getInputStream()));
46451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals("ABCDE", reader.readLine());
46551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        try {
46651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            reader.readLine();
46751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            fail("This implementation silently ignored a truncated HTTP body.");
46851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        } catch (IOException expected) {
46951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        }
47051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
47151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getAbortCount());
47251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(0, cache.getSuccessCount());
47351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertContent("Request #2", server.getUrl("/").openConnection());
47451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getAbortCount());
47551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getSuccessCount());
47651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
47751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
47851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testClientPrematureDisconnectWithContentLengthHeader() throws IOException {
47951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testClientPrematureDisconnect(TransferKind.FIXED_LENGTH);
48051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
48151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
48251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testClientPrematureDisconnectWithChunkedEncoding() throws IOException {
48351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testClientPrematureDisconnect(TransferKind.CHUNKED);
48451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
48551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
48651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testClientPrematureDisconnectWithNoLengthHeaders() throws IOException {
48751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testClientPrematureDisconnect(TransferKind.END_OF_STREAM);
48851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
48951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
49051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    private void testClientPrematureDisconnect(TransferKind transferKind) throws IOException {
49151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        MockResponse response = new MockResponse();
49251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        transferKind.setBody(response, "ABCDE\nFGHIJKLMNOPQRSTUVWXYZ", 1024);
49351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(response);
49451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(new MockResponse().setBody("Request #2"));
49551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.play();
49651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
49751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        DefaultResponseCache cache = new DefaultResponseCache();
49851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        ResponseCache.setDefault(cache);
49951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
50051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        InputStream in = server.getUrl("/").openConnection().getInputStream();
50151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals("ABCDE", readAscii(in, 5));
50251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        in.close();
50351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        try {
50451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            in.read();
50551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            fail("Expected an IOException because the stream is closed.");
50651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        } catch (IOException expected) {
50751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        }
50851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
50951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getAbortCount());
51051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(0, cache.getSuccessCount());
51151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertContent("Request #2", server.getUrl("/").openConnection());
51251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getAbortCount());
51351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getSuccessCount());
51451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
51551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
51651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    /**
51751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * Shortens the body of {@code response} but not the corresponding headers.
51851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * Only useful to test how clients respond to the premature conclusion of
51951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * the HTTP body.
52051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     */
52151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    private MockResponse truncateViolently(MockResponse response, int numBytesToKeep) {
52251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        response.setDisconnectAtEnd(true);
52351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        List<String> headers = new ArrayList<String>(response.getHeaders());
52451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        response.setBody(Arrays.copyOfRange(response.getBody(), 0, numBytesToKeep));
52551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        response.getHeaders().clear();
52651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        response.getHeaders().addAll(headers);
52751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        return response;
52851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
52951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
53051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testMarkAndResetWithContentLengthHeader() throws IOException {
53151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testMarkAndReset(TransferKind.FIXED_LENGTH);
53251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
53351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
53451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testMarkAndResetWithChunkedEncoding() throws IOException {
53551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testMarkAndReset(TransferKind.CHUNKED);
53651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
53751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
53851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testMarkAndResetWithNoLengthHeaders() throws IOException {
53951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        testMarkAndReset(TransferKind.END_OF_STREAM);
54051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
54151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
54251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testMarkAndReset(TransferKind transferKind) throws IOException {
54351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        MockResponse response = new MockResponse();
54451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        transferKind.setBody(response, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1024);
54551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(response);
54651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.play();
54751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
54851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        DefaultResponseCache cache = new DefaultResponseCache();
54951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        ResponseCache.setDefault(cache);
55051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
55151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        InputStream in = server.getUrl("/").openConnection().getInputStream();
55251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertFalse("This implementation claims to support mark().", in.markSupported());
55351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        in.mark(5);
55451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals("ABCDE", readAscii(in, 5));
55551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        try {
55651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            in.reset();
55751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            fail();
55851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        } catch (IOException expected) {
55951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        }
56051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals("FGHIJKLMNOPQRSTUVWXYZ", readAscii(in, Integer.MAX_VALUE));
56151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
56251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertContent("ABCDEFGHIJKLMNOPQRSTUVWXYZ", server.getUrl("/").openConnection());
56351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getSuccessCount());
56451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, cache.getHitCount());
56551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
56651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
56751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    /**
56851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * We've had a bug where we forget the HTTP response when we see response
56951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * code 401. This causes a new HTTP request to be issued for every call into
57051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     * the URLConnection.
57151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson     */
57251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    public void testUnauthorizedResponseHandling() throws IOException {
57351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        MockResponse response = new MockResponse()
57451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                .addHeader("WWW-Authenticate: challenge")
57551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                .setResponseCode(401) // UNAUTHORIZED
57651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                .setBody("Unauthorized");
57751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(response);
57851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(response);
57951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.enqueue(response);
58051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        server.play();
58151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
58251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        URL url = server.getUrl("/");
58351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
58451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
58551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(401, conn.getResponseCode());
58651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(401, conn.getResponseCode());
58751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(401, conn.getResponseCode());
58851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(1, server.getRequestCount());
58951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
59051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
5916906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson    public void testNonHexChunkSize() throws IOException {
5926906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        server.enqueue(new MockResponse()
5936906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson                .setBody("5\r\nABCDE\r\nG\r\nFGHIJKLMNOPQRSTU\r\n0\r\n\r\n")
5946906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson                .clearHeaders()
5956906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson                .addHeader("Transfer-encoding: chunked"));
5966906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        server.play();
5976906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson
5986906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
5996906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        try {
6006906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson            readAscii(connection.getInputStream(), Integer.MAX_VALUE);
6016906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson            fail();
6026906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        } catch (IOException e) {
6036906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        }
6046906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson    }
6056906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson
6066906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson    public void testMissingChunkBody() throws IOException {
6076906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        server.enqueue(new MockResponse()
6086906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson                .setBody("5")
6096906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson                .clearHeaders()
6106906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson                .addHeader("Transfer-encoding: chunked")
6116906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson                .setDisconnectAtEnd(true));
6126906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        server.play();
6136906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson
6146906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
6156906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        try {
6166906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson            readAscii(connection.getInputStream(), Integer.MAX_VALUE);
6176906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson            fail();
6186906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        } catch (IOException e) {
6196906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson        }
6206906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson    }
6216906b0c12dcf3216883d0373973a252812a20d32Jesse Wilson
622deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson    public void testClientConfiguredGzipContentEncoding() throws Exception {
623deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        server.enqueue(new MockResponse()
624deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson                .setBody(gzip("ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes("UTF-8")))
625deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson                .addHeader("Content-Encoding: gzip"));
626deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        server.play();
627deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson
628deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
629deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        connection.addRequestProperty("Accept-Encoding", "gzip");
630deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        InputStream gunzippedIn = new GZIPInputStream(connection.getInputStream());
631deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZ", readAscii(gunzippedIn, Integer.MAX_VALUE));
632deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson
633deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        RecordedRequest request = server.takeRequest();
634deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        assertContains(request.getHeaders(), "Accept-Encoding: gzip");
635deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson    }
636deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson
637deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson    public void testGzipAndConnectionReuseWithFixedLength() throws Exception {
638deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.FIXED_LENGTH);
639deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson    }
640deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson
641deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson    public void testGzipAndConnectionReuseWithChunkedEncoding() throws Exception {
642deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        testClientConfiguredGzipContentEncodingAndConnectionReuse(TransferKind.CHUNKED);
643deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson    }
644deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson
645deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson    /**
646deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson     * Test a bug where gzip input streams weren't exhausting the input stream,
647deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson     * which corrupted the request that followed.
648deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson     * http://code.google.com/p/android/issues/detail?id=7059
649deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson     */
650deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson    private void testClientConfiguredGzipContentEncodingAndConnectionReuse(
651deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson            TransferKind transferKind) throws Exception {
652deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        MockResponse responseOne = new MockResponse();
653deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        responseOne.addHeader("Content-Encoding: gzip");
654deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        transferKind.setBody(responseOne, gzip("one (gzipped)".getBytes("UTF-8")), 5);
655deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        server.enqueue(responseOne);
656deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        MockResponse responseTwo = new MockResponse();
657deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        transferKind.setBody(responseTwo, "two (identity)", 5);
658deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        server.enqueue(responseTwo);
659deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        server.play();
660deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson
661deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        URLConnection connection = server.getUrl("/").openConnection();
662deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        connection.addRequestProperty("Accept-Encoding", "gzip");
663deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        InputStream gunzippedIn = new GZIPInputStream(connection.getInputStream());
664deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        assertEquals("one (gzipped)", readAscii(gunzippedIn, Integer.MAX_VALUE));
665deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        assertEquals(0, server.takeRequest().getSequenceNumber());
666deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson
667deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        connection = server.getUrl("/").openConnection();
668deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        assertEquals("two (identity)", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
669deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        assertEquals(1, server.takeRequest().getSequenceNumber());
670deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson    }
671deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson
672deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson    /**
673deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson     * Encodes the response body using GZIP and adds the corresponding header.
674deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson     */
675deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson    public byte[] gzip(byte[] bytes) throws IOException {
676deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
677deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        OutputStream gzippedOut = new GZIPOutputStream(bytesOut);
678deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        gzippedOut.write(bytes);
679deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        gzippedOut.close();
680deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        return bytesOut.toByteArray();
681deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson    }
682deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson
683c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    /**
684c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson     * Reads at most {@code limit} characters from {@code in} and asserts that
685c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson     * content equals {@code expected}.
686c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson     */
687c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    private void assertContent(String expected, URLConnection connection, int limit)
688c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson            throws IOException {
68951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        assertEquals(expected, readAscii(connection.getInputStream(), limit));
690c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        ((HttpURLConnection) connection).disconnect();
691c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    }
692c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
693c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    private void assertContent(String expected, URLConnection connection) throws IOException {
694c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson        assertContent(expected, connection, Integer.MAX_VALUE);
695c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson    }
696c8977f474b30c5f3807398859a6b16687af6fc7bJesse Wilson
69760476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson    private void assertContains(List<String> headers, String header) {
69860476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson        assertTrue(headers.toString(), headers.contains(header));
69960476787f0e0f052366d8031c74e507ffd3d16a3Jesse Wilson    }
70051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
70151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    enum TransferKind {
70251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        CHUNKED() {
703deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson            @Override void setBody(MockResponse response, byte[] content, int chunkSize)
70451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                    throws IOException {
70551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                response.setChunkedBody(content, chunkSize);
70651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            }
70751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        },
70851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        FIXED_LENGTH() {
709deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson            @Override void setBody(MockResponse response, byte[] content, int chunkSize) {
71051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                response.setBody(content);
71151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            }
71251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        },
71351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        END_OF_STREAM() {
714deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson            @Override void setBody(MockResponse response, byte[] content, int chunkSize) {
71551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                response.setBody(content);
71651e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                response.setDisconnectAtEnd(true);
71751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                for (Iterator<String> h = response.getHeaders().iterator(); h.hasNext(); ) {
71851e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                    if (h.next().startsWith("Content-Length:")) {
71951e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                        h.remove();
72051e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                        break;
72151e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                    }
72251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                }
72351e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson            }
72451e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson        };
72551e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson
726deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        abstract void setBody(MockResponse response, byte[] content, int chunkSize)
72751e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson                throws IOException;
728deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson
729deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        void setBody(MockResponse response, String content, int chunkSize) throws IOException {
730deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson            setBody(response, content.getBytes("UTF-8"), chunkSize);
731deb236fb06f2a14861e7d40dea959f181cd5cf28Jesse Wilson        }
73251e468abf2628ce964d3657042f3ac8f2c947504Jesse Wilson    }
733e40c9e3935a5024c0f3ebfb3f1441fcd5c48ed86Elliott Hughes}
734