1c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/*
2c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Copyright (C) 2012 The Android Open Source Project
3c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
4c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Licensed under the Apache License, Version 2.0 (the "License");
5c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * you may not use this file except in compliance with the License.
6c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * You may obtain a copy of the License at
7c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
8c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *      http://www.apache.org/licenses/LICENSE-2.0
9c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
10c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Unless required by applicable law or agreed to in writing, software
11c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS,
12c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * See the License for the specific language governing permissions and
14c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * limitations under the License.
15c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */
16c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
17c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathpackage libcore.net.http;
18c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
19c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.BufferedOutputStream;
20c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.ByteArrayOutputStream;
21c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.IOException;
22c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.InputStream;
23c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.OutputStream;
24c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CacheRequest;
25c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CookieHandler;
26c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URL;
27c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport libcore.io.Streams;
28c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport libcore.util.Libcore;
29c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
30c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathfinal class HttpTransport implements Transport {
31c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
32c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * The maximum number of bytes to buffer when sending headers and a request
33c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * body. When the headers and body can be sent in a single write, the
34c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * request completes sooner. In one WiFi benchmark, using a large enough
35c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * buffer sped up some uploads by half.
36c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
37c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private static final int MAX_REQUEST_BUFFER_LENGTH = 32768;
38c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
39c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private final HttpEngine httpEngine;
40c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private final InputStream socketIn;
41c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private final OutputStream socketOut;
42c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
43c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
44c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * This stream buffers the request headers and the request body when their
45c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * combined size is less than MAX_REQUEST_BUFFER_LENGTH. By combining them
46c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * we can save socket writes, which in turn saves a packet transmission.
47c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * This is socketOut if the request size is large or unknown.
48c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
49c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private OutputStream requestOut;
50c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
51c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public HttpTransport(HttpEngine httpEngine,
52c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            OutputStream outputStream, InputStream inputStream) {
53c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        this.httpEngine = httpEngine;
54c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        this.socketOut = outputStream;
55c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        this.requestOut = outputStream;
56c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        this.socketIn = inputStream;
57c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
58c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
59c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    @Override public OutputStream createRequestBody() throws IOException {
60c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        boolean chunked = httpEngine.requestHeaders.isChunked();
61c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (!chunked
62c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                && httpEngine.policy.getChunkLength() > 0
63c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                && httpEngine.connection.httpMinorVersion != 0) {
64c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            httpEngine.requestHeaders.setChunked();
65c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            chunked = true;
66c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
67c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
68c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        // Stream a request body of unknown length.
69c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (chunked) {
70c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            int chunkLength = httpEngine.policy.getChunkLength();
71c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (chunkLength == -1) {
72c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                chunkLength = HttpEngine.DEFAULT_CHUNK_LENGTH;
73c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
74c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            writeRequestHeaders();
75c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return new ChunkedOutputStream(requestOut, chunkLength);
76c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
77c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
78c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        // Stream a request body of a known length.
79c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        int fixedContentLength = httpEngine.policy.getFixedContentLength();
80c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (fixedContentLength != -1) {
81c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            httpEngine.requestHeaders.setContentLength(fixedContentLength);
82c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            writeRequestHeaders();
83c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return new FixedLengthOutputStream(requestOut, fixedContentLength);
84c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
85c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
86c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        // Buffer a request body of a known length.
87c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        int contentLength = httpEngine.requestHeaders.getContentLength();
88c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (contentLength != -1) {
89c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            writeRequestHeaders();
90c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return new RetryableOutputStream(contentLength);
91c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
92c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
93c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        // Buffer a request body of an unknown length. Don't write request
94c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        // headers until the entire body is ready; otherwise we can't set the
95c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        // Content-Length header correctly.
96c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return new RetryableOutputStream();
97c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
98c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
99c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    @Override public void flushRequest() throws IOException {
100c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        requestOut.flush();
101c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        requestOut = socketOut;
102c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
103c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
104c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    @Override public void writeRequestBody(RetryableOutputStream requestBody) throws IOException {
105c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        requestBody.writeToSocket(requestOut);
106c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
107c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
108c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
109c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * Prepares the HTTP headers and sends them to the server.
110c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     *
111c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * <p>For streaming requests with a body, headers must be prepared
112c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * <strong>before</strong> the output stream has been written to. Otherwise
113c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * the body would need to be buffered!
114c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     *
115c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * <p>For non-streaming requests with a body, headers must be prepared
116c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * <strong>after</strong> the output stream has been written to and closed.
117c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * This ensures that the {@code Content-Length} header field receives the
118c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * proper value.
119c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
120c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public void writeRequestHeaders() throws IOException {
121c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (httpEngine.sentRequestMillis != -1) {
122c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            throw new IllegalStateException();
123c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
124c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        httpEngine.sentRequestMillis = System.currentTimeMillis();
125c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
126c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        int contentLength = httpEngine.requestHeaders.getContentLength();
127c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        RawHeaders headersToSend = getNetworkRequestHeaders();
128c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        byte[] bytes = headersToSend.toHeaderString().getBytes("ISO-8859-1");
129c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
130c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (contentLength != -1 && bytes.length + contentLength <= MAX_REQUEST_BUFFER_LENGTH) {
131c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            requestOut = new BufferedOutputStream(socketOut, bytes.length + contentLength);
132c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
133c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
134c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        requestOut.write(bytes);
135c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
136c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
137c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private RawHeaders getNetworkRequestHeaders() {
138c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return httpEngine.method == HttpEngine.CONNECT
139c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                ? getTunnelNetworkRequestHeaders()
140c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                : httpEngine.requestHeaders.getHeaders();
141c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
142c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
143c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
144c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * If we're establishing an HTTPS tunnel with CONNECT (RFC 2817 5.2), send
145c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * only the minimum set of headers. This avoids sending potentially
146c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * sensitive data like HTTP cookies to the proxy unencrypted.
147c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
148c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private RawHeaders getTunnelNetworkRequestHeaders() {
149c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        RequestHeaders privateHeaders = httpEngine.requestHeaders;
150c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        URL url = httpEngine.policy.getURL();
151c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
152c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        RawHeaders result = new RawHeaders();
153c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        result.setStatusLine("CONNECT " + url.getHost() + ":" + Libcore.getEffectivePort(url)
154c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                + " HTTP/1.1");
155c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
156c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        // Always set Host and User-Agent.
157c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        String host = privateHeaders.getHost();
158c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (host == null) {
159c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            host = httpEngine.getOriginAddress(url);
160c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
161c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        result.set("Host", host);
162c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
163c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        String userAgent = privateHeaders.getUserAgent();
164c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (userAgent == null) {
165c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            userAgent = httpEngine.getDefaultUserAgent();
166c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
167c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        result.set("User-Agent", userAgent);
168c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
169c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        // Copy over the Proxy-Authorization header if it exists.
170c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        String proxyAuthorization = privateHeaders.getProxyAuthorization();
171c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (proxyAuthorization != null) {
172c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            result.set("Proxy-Authorization", proxyAuthorization);
173c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
174c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
175c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        // Always set the Proxy-Connection to Keep-Alive for the benefit of
176c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        // HTTP/1.0 proxies like Squid.
177c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        result.set("Proxy-Connection", "Keep-Alive");
178c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return result;
179c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
180c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
181c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    @Override public ResponseHeaders readResponseHeaders() throws IOException {
182c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        RawHeaders headers;
183c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        do {
184c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            headers = new RawHeaders();
185c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            headers.setStatusLine(Streams.readAsciiLine(socketIn));
186c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            httpEngine.connection.httpMinorVersion = headers.getHttpMinorVersion();
187c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            readHeaders(headers);
188c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        } while (headers.getResponseCode() == HttpEngine.HTTP_CONTINUE);
189c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return new ResponseHeaders(httpEngine.uri, headers);
190c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
191c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
192c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
193c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * Reads headers or trailers and updates the cookie store.
194c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
195c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private void readHeaders(RawHeaders headers) throws IOException {
196c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        // parse the result headers until the first blank line
197c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        String line;
198c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        while ((line = Streams.readAsciiLine(socketIn)).length() != 0) {
199c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            headers.addLine(line);
200c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
201c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
202c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        CookieHandler cookieHandler = CookieHandler.getDefault();
203c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (cookieHandler != null) {
204c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            cookieHandler.put(httpEngine.uri, headers.toMultimap());
205c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
206c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
207c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
208c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public boolean makeReusable(OutputStream requestBodyOut, InputStream responseBodyIn) {
209c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        // We cannot reuse sockets that have incomplete output.
210c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (requestBodyOut != null && !((AbstractHttpOutputStream) requestBodyOut).closed) {
211c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return false;
212c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
213c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
214c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        // If the headers specify that the connection shouldn't be reused, don't reuse it.
215c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (httpEngine.requestHeaders.hasConnectionClose()
216c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                || (httpEngine.responseHeaders != null
217c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                && httpEngine.responseHeaders.hasConnectionClose())) {
218c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return false;
219c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
220c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
221c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (responseBodyIn instanceof UnknownLengthHttpInputStream) {
222c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return false;
223c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
224c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
225c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (responseBodyIn != null) {
226c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            // Discard the response body before the connection can be reused.
227c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            try {
228c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                Streams.skipAll(responseBodyIn);
229c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            } catch (IOException e) {
230c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                return false;
231c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
232c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
233c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
234c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return true;
235c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
236c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
237c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    @Override public InputStream getTransferStream(CacheRequest cacheRequest) throws IOException {
238c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (!httpEngine.hasResponseBody()) {
239c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return new FixedLengthInputStream(socketIn, cacheRequest, httpEngine, 0);
240c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
241c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
242c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (httpEngine.responseHeaders.isChunked()) {
243c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return new ChunkedInputStream(socketIn, cacheRequest, this);
244c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
245c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
246c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (httpEngine.responseHeaders.getContentLength() != -1) {
247c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return new FixedLengthInputStream(socketIn, cacheRequest, httpEngine,
248c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                    httpEngine.responseHeaders.getContentLength());
249c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
250c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
251c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        /*
252c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath         * Wrap the input stream from the HttpConnection (rather than
253c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath         * just returning "socketIn" directly here), so that we can control
254c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath         * its use after the reference escapes.
255c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath         */
256c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return new UnknownLengthHttpInputStream(socketIn, cacheRequest, httpEngine);
257c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
258c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
259c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
260c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * An HTTP body with a fixed length known in advance.
261c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
262c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private static final class FixedLengthOutputStream extends AbstractHttpOutputStream {
263c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private final OutputStream socketOut;
264c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private int bytesRemaining;
265c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
266c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private FixedLengthOutputStream(OutputStream socketOut, int bytesRemaining) {
267c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            this.socketOut = socketOut;
268c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            this.bytesRemaining = bytesRemaining;
269c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
270c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
271c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        @Override public void write(byte[] buffer, int offset, int count) throws IOException {
272c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            checkNotClosed();
273c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            Libcore.checkOffsetAndCount(buffer.length, offset, count);
274c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (count > bytesRemaining) {
275c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                throw new IOException("expected " + bytesRemaining
276c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                        + " bytes but received " + count);
277c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
278c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            socketOut.write(buffer, offset, count);
279c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            bytesRemaining -= count;
280c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
281c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
282c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        @Override public void flush() throws IOException {
283c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (closed) {
284c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                return; // don't throw; this stream might have been closed on the caller's behalf
285c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
286c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            socketOut.flush();
287c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
288c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
289c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        @Override public void close() throws IOException {
290c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (closed) {
291c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                return;
292c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
293c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            closed = true;
294c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (bytesRemaining > 0) {
295c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                throw new IOException("unexpected end of stream");
296c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
297c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
298c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
299c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
300c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
301c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * An HTTP body with alternating chunk sizes and chunk bodies. Chunks are
302c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * buffered until {@code maxChunkLength} bytes are ready, at which point the
303c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * chunk is written and the buffer is cleared.
304c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
305c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private static final class ChunkedOutputStream extends AbstractHttpOutputStream {
306c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private static final byte[] CRLF = {'\r', '\n'};
307c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private static final byte[] HEX_DIGITS = {
308c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
309c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        };
310c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private static final byte[] FINAL_CHUNK = new byte[] {'0', '\r', '\n', '\r', '\n'};
311c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
312c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        /** Scratch space for up to 8 hex digits, and then a constant CRLF. */
313c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private final byte[] hex = {0, 0, 0, 0, 0, 0, 0, 0, '\r', '\n'};
314c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
315c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private final OutputStream socketOut;
316c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private final int maxChunkLength;
317c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private final ByteArrayOutputStream bufferedChunk;
318c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
319c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private ChunkedOutputStream(OutputStream socketOut, int maxChunkLength) {
320c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            this.socketOut = socketOut;
321c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            this.maxChunkLength = Math.max(1, dataLength(maxChunkLength));
322c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            this.bufferedChunk = new ByteArrayOutputStream(maxChunkLength);
323c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
324c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
325c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        /**
326c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath         * Returns the amount of data that can be transmitted in a chunk whose total
327c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath         * length (data+headers) is {@code dataPlusHeaderLength}. This is presumably
328c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath         * useful to match sizes with wire-protocol packets.
329c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath         */
330c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private int dataLength(int dataPlusHeaderLength) {
331c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            int headerLength = 4; // "\r\n" after the size plus another "\r\n" after the data
332c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            for (int i = dataPlusHeaderLength - headerLength; i > 0; i >>= 4) {
333c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                headerLength++;
334c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
335c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return dataPlusHeaderLength - headerLength;
336c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
337c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
338c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        @Override public synchronized void write(byte[] buffer, int offset, int count)
339c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                throws IOException {
340c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            checkNotClosed();
341c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            Libcore.checkOffsetAndCount(buffer.length, offset, count);
342c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
343c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            while (count > 0) {
344c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                int numBytesWritten;
345c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
346c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                if (bufferedChunk.size() > 0 || count < maxChunkLength) {
347c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                    // fill the buffered chunk and then maybe write that to the stream
348c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                    numBytesWritten = Math.min(count, maxChunkLength - bufferedChunk.size());
349c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                    // TODO: skip unnecessary copies from buffer->bufferedChunk?
350c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                    bufferedChunk.write(buffer, offset, numBytesWritten);
351c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                    if (bufferedChunk.size() == maxChunkLength) {
352c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                        writeBufferedChunkToSocket();
353c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                    }
354c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
355c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                } else {
356c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                    // write a single chunk of size maxChunkLength to the stream
357c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                    numBytesWritten = maxChunkLength;
358c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                    writeHex(numBytesWritten);
359c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                    socketOut.write(buffer, offset, numBytesWritten);
360c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                    socketOut.write(CRLF);
361c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                }
362c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
363c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                offset += numBytesWritten;
364c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                count -= numBytesWritten;
365c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
366c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
367c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
368c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        /**
369c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath         * Equivalent to, but cheaper than writing Integer.toHexString().getBytes()
370c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath         * followed by CRLF.
371c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath         */
372c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private void writeHex(int i) throws IOException {
373c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            int cursor = 8;
374c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            do {
375c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                hex[--cursor] = HEX_DIGITS[i & 0xf];
376c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            } while ((i >>>= 4) != 0);
377c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            socketOut.write(hex, cursor, hex.length - cursor);
378c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
379c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
380c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        @Override public synchronized void flush() throws IOException {
381c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (closed) {
382c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                return; // don't throw; this stream might have been closed on the caller's behalf
383c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
384c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            writeBufferedChunkToSocket();
385c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            socketOut.flush();
386c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
387c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
388c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        @Override public synchronized void close() throws IOException {
389c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (closed) {
390c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                return;
391c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
392c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            closed = true;
393c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            writeBufferedChunkToSocket();
394c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            socketOut.write(FINAL_CHUNK);
395c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
396c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
397c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private void writeBufferedChunkToSocket() throws IOException {
398c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            int size = bufferedChunk.size();
399c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (size <= 0) {
400c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                return;
401c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
402c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
403c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            writeHex(size);
404c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            bufferedChunk.writeTo(socketOut);
405c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            bufferedChunk.reset();
406c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            socketOut.write(CRLF);
407c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
408c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
409c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
410c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
411c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * An HTTP body with a fixed length specified in advance.
412c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
413c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private static class FixedLengthInputStream extends AbstractHttpInputStream {
414c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private int bytesRemaining;
415c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
416c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        public FixedLengthInputStream(InputStream is, CacheRequest cacheRequest,
417c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                HttpEngine httpEngine, int length) throws IOException {
418c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            super(is, httpEngine, cacheRequest);
419c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            bytesRemaining = length;
420c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (bytesRemaining == 0) {
421c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                endOfInput(true);
422c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
423c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
424c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
425c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        @Override public int read(byte[] buffer, int offset, int count) throws IOException {
426c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            Libcore.checkOffsetAndCount(buffer.length, offset, count);
427c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            checkNotClosed();
428c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (bytesRemaining == 0) {
429c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                return -1;
430c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
431c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            int read = in.read(buffer, offset, Math.min(count, bytesRemaining));
432c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (read == -1) {
433c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                unexpectedEndOfInput(); // the server didn't supply the promised content length
434c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                throw new IOException("unexpected end of stream");
435c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
436c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            bytesRemaining -= read;
437c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            cacheWrite(buffer, offset, read);
438c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (bytesRemaining == 0) {
439c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                endOfInput(true);
440c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
441c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return read;
442c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
443c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
444c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        @Override public int available() throws IOException {
445c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            checkNotClosed();
446c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return bytesRemaining == 0 ? 0 : Math.min(in.available(), bytesRemaining);
447c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
448c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
449c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        @Override public void close() throws IOException {
450c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (closed) {
451c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                return;
452c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
453c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            closed = true;
454c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (bytesRemaining != 0) {
455c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                unexpectedEndOfInput();
456c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
457c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
458c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
459c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
460c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
461c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * An HTTP body with alternating chunk sizes and chunk bodies.
462c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
463c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private static class ChunkedInputStream extends AbstractHttpInputStream {
464c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private static final int MIN_LAST_CHUNK_LENGTH = "\r\n0\r\n\r\n".length();
465c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private static final int NO_CHUNK_YET = -1;
466c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private final HttpTransport transport;
467c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private int bytesRemainingInChunk = NO_CHUNK_YET;
468c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private boolean hasMoreChunks = true;
469c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
470c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        ChunkedInputStream(InputStream is, CacheRequest cacheRequest,
471c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                HttpTransport transport) throws IOException {
472c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            super(is, transport.httpEngine, cacheRequest);
473c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            this.transport = transport;
474c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
475c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
476c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        @Override public int read(byte[] buffer, int offset, int count) throws IOException {
477c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            Libcore.checkOffsetAndCount(buffer.length, offset, count);
478c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            checkNotClosed();
479c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
480c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (!hasMoreChunks) {
481c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                return -1;
482c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
483c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (bytesRemainingInChunk == 0 || bytesRemainingInChunk == NO_CHUNK_YET) {
484c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                readChunkSize();
485c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                if (!hasMoreChunks) {
486c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                    return -1;
487c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                }
488c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
489c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            int read = in.read(buffer, offset, Math.min(count, bytesRemainingInChunk));
490c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (read == -1) {
491c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                unexpectedEndOfInput(); // the server didn't supply the promised chunk length
492c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                throw new IOException("unexpected end of stream");
493c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
494c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            bytesRemainingInChunk -= read;
495c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            cacheWrite(buffer, offset, read);
496c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
497c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            /*
498c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            * If we're at the end of a chunk and the next chunk size is readable,
499c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            * read it! Reading the last chunk causes the underlying connection to
500c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            * be recycled and we want to do that as early as possible. Otherwise
501c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            * self-delimiting streams like gzip will never be recycled.
502c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            * http://code.google.com/p/android/issues/detail?id=7059
503c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            */
504c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (bytesRemainingInChunk == 0 && in.available() >= MIN_LAST_CHUNK_LENGTH) {
505c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                readChunkSize();
506c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
507c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
508c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return read;
509c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
510c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
511c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private void readChunkSize() throws IOException {
512c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            // read the suffix of the previous chunk
513c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (bytesRemainingInChunk != NO_CHUNK_YET) {
514c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                Streams.readAsciiLine(in);
515c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
516c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            String chunkSizeString = Streams.readAsciiLine(in);
517c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            int index = chunkSizeString.indexOf(";");
518c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (index != -1) {
519c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                chunkSizeString = chunkSizeString.substring(0, index);
520c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
521c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            try {
522c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                bytesRemainingInChunk = Integer.parseInt(chunkSizeString.trim(), 16);
523c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            } catch (NumberFormatException e) {
524c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                throw new IOException("Expected a hex chunk size, but was " + chunkSizeString);
525c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
526c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (bytesRemainingInChunk == 0) {
527c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                hasMoreChunks = false;
528c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                transport.readHeaders(httpEngine.responseHeaders.getHeaders());
529c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                endOfInput(true);
530c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
531c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
532c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
533c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        @Override public int available() throws IOException {
534c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            checkNotClosed();
535c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (!hasMoreChunks || bytesRemainingInChunk == NO_CHUNK_YET) {
536c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                return 0;
537c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
538c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return Math.min(in.available(), bytesRemainingInChunk);
539c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
540c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
541c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        @Override public void close() throws IOException {
542c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (closed) {
543c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                return;
544c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
545c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
546c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            closed = true;
547c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (hasMoreChunks) {
548c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                unexpectedEndOfInput();
549c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
550c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
551c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
552c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
553c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
554c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * An HTTP payload terminated by the end of the socket stream.
555c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
556c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private static final class UnknownLengthHttpInputStream extends AbstractHttpInputStream {
557c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private boolean inputExhausted;
558c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
559c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        private UnknownLengthHttpInputStream(InputStream is, CacheRequest cacheRequest,
560c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                HttpEngine httpEngine) throws IOException {
561c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            super(is, httpEngine, cacheRequest);
562c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
563c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
564c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        @Override public int read(byte[] buffer, int offset, int count) throws IOException {
565c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            Libcore.checkOffsetAndCount(buffer.length, offset, count);
566c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            checkNotClosed();
567c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (in == null || inputExhausted) {
568c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                return -1;
569c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
570c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            int read = in.read(buffer, offset, count);
571c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (read == -1) {
572c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                inputExhausted = true;
573c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                endOfInput(false);
574c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                return -1;
575c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
576c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            cacheWrite(buffer, offset, read);
577c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return read;
578c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
579c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
580c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        @Override public int available() throws IOException {
581c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            checkNotClosed();
582c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return in == null ? 0 : in.available();
583c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
584c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
585c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        @Override public void close() throws IOException {
586c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (closed) {
587c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                return;
588c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
589c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            closed = true;
590c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (!inputExhausted) {
591c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                unexpectedEndOfInput();
592c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
593c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
594c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
595c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath}
596