HttpEngine.java revision c3f6f16bd4a2338e88275641b9f2f56e816ca377
1c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/*
2c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  Licensed to the Apache Software Foundation (ASF) under one or more
3c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  contributor license agreements.  See the NOTICE file distributed with
4c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  this work for additional information regarding copyright ownership.
5c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  The ASF licenses this file to You under the Apache License, Version 2.0
6c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  (the "License"); you may not use this file except in compliance with
7c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  the License.  You may obtain a copy of the License at
8c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
9c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *     http://www.apache.org/licenses/LICENSE-2.0
10c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
11c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  Unless required by applicable law or agreed to in writing, software
12c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  distributed under the License is distributed on an "AS IS" BASIS,
13c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  See the License for the specific language governing permissions and
15c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  limitations under the License.
16c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */
17c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
18c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathpackage libcore.net.http;
19c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
20c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport com.squareup.okhttp.OkHttpConnection;
21c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.ByteArrayInputStream;
22c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.IOException;
23c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.InputStream;
24c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.OutputStream;
25c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CacheRequest;
26c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CacheResponse;
27c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CookieHandler;
28c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.Proxy;
29c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.ResponseCache;
30c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URI;
31c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URISyntaxException;
32c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URL;
33c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Collections;
34c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Date;
35c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.HashMap;
36c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.List;
37c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Map;
38c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.zip.GZIPInputStream;
39c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport javax.net.ssl.SSLSocketFactory;
40c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport libcore.io.IoUtils;
41c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport libcore.util.EmptyArray;
42c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport libcore.util.ExtendedResponseCache;
43c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport libcore.util.Libcore;
44c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport libcore.util.ResponseSource;
45c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
46c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/**
47c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Handles a single HTTP request/response pair. Each HTTP engine follows this
48c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * lifecycle:
49c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <ol>
50c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *     <li>It is created.
51c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *     <li>The HTTP request message is sent with sendRequest(). Once the request
52c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *         is sent it is an error to modify the request headers. After
53c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *         sendRequest() has been called the request body can be written to if
54c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *         it exists.
55c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *     <li>The HTTP response message is read with readResponse(). After the
56c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *         response has been read the response headers and body can be read.
57c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *         All responses have a response body input stream, though in some
58c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *         instances this stream is empty.
59c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * </ol>
60c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
61c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>The request and response may be served by the HTTP response cache, by the
62c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * network, or by both in the event of a conditional GET.
63c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
64c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>This class may hold a socket connection that needs to be released or
65c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * recycled. By default, this socket connection is held when the last byte of
66c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * the response is consumed. To release the connection when it is no longer
67c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * required, use {@link #automaticallyReleaseConnectionToPool()}.
68c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */
69c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathpublic class HttpEngine {
70c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private static final CacheResponse BAD_GATEWAY_RESPONSE = new CacheResponse() {
71c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        @Override public Map<String, List<String>> getHeaders() throws IOException {
72c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            Map<String, List<String>> result = new HashMap<String, List<String>>();
73c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            result.put(null, Collections.singletonList("HTTP/1.1 502 Bad Gateway"));
74c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return result;
75c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
76c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        @Override public InputStream getBody() throws IOException {
77c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return new ByteArrayInputStream(EmptyArray.BYTE);
78c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
79c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    };
80c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public static final int DEFAULT_CHUNK_LENGTH = 1024;
81c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
82c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public static final String OPTIONS = "OPTIONS";
83c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public static final String GET = "GET";
84c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public static final String HEAD = "HEAD";
85c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public static final String POST = "POST";
86c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public static final String PUT = "PUT";
87c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public static final String DELETE = "DELETE";
88c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public static final String TRACE = "TRACE";
89c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public static final String CONNECT = "CONNECT";
90c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
91c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public static final int HTTP_CONTINUE = 100;
92c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
93c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    protected final HttpURLConnectionImpl policy;
94c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
95c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    protected final String method;
96c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
97c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private ResponseSource responseSource;
98c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
99c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    protected HttpConnection connection;
100c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private OutputStream requestBodyOut;
101c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
102c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private Transport transport;
103c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
104c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private InputStream responseBodyIn;
105c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
106c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private final ResponseCache responseCache = ResponseCache.getDefault();
107c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private CacheResponse cacheResponse;
108c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private CacheRequest cacheRequest;
109c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
110c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /** The time when the request headers were written, or -1 if they haven't been written yet. */
111c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    long sentRequestMillis = -1;
112c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
113c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
114c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * True if this client added an "Accept-Encoding: gzip" header field and is
115c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * therefore responsible for also decompressing the transfer stream.
116c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
117c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private boolean transparentGzip;
118c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
119c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    final URI uri;
120c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
121c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    final RequestHeaders requestHeaders;
122c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
123c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /** Null until a response is received from the network or the cache. */
124c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    ResponseHeaders responseHeaders;
125c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
126c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /*
127c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * The cache response currently being validated on a conditional get. Null
128c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * if the cached response doesn't exist or doesn't need validation. If the
129c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * conditional get succeeds, these will be used for the response headers and
130c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * body. If it fails, these be closed and set to null.
131c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
132c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private ResponseHeaders cachedResponseHeaders;
133c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private InputStream cachedResponseBody;
134c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
135c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
136c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * True if the socket connection should be released to the connection pool
137c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * when the response has been fully read.
138c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
139c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private boolean automaticallyReleaseConnectionToPool;
140c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
141c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /** True if the socket connection is no longer needed by this engine. */
142c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private boolean connectionReleased;
143c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
144c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
145c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * @param requestHeaders the client's supplied request headers. This class
146c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     *     creates a private copy that it can mutate.
147c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * @param connection the connection used for an intermediate response
148c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     *     immediately prior to this request/response pair, such as a same-host
149c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     *     redirect. This engine assumes ownership of the connection and must
150c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     *     release it when it is unneeded.
151c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
152c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public HttpEngine(HttpURLConnectionImpl policy, String method, RawHeaders requestHeaders,
153c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            HttpConnection connection, RetryableOutputStream requestBodyOut) throws IOException {
154c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        this.policy = policy;
155c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        this.method = method;
156c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        this.connection = connection;
157c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        this.requestBodyOut = requestBodyOut;
158c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
159c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        try {
160c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            uri = Libcore.toUriLenient(policy.getURL());
161c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        } catch (URISyntaxException e) {
162c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            throw new IOException(e);
163c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
164c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
165c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        this.requestHeaders = new RequestHeaders(uri, new RawHeaders(requestHeaders));
166c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
167c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
168c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public URI getUri() {
169c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return uri;
170c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
171c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
172c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
173c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * Figures out what the response source will be, and opens a socket to that
174c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * source if necessary. Prepares the request headers and gets ready to start
175c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * writing the request body if it exists.
176c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
177c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public final void sendRequest() throws IOException {
178c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (responseSource != null) {
179c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return;
180c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
181c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
182c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        prepareRawRequestHeaders();
183c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        initResponseSource();
184c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (responseCache instanceof ExtendedResponseCache) {
185c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            ((ExtendedResponseCache) responseCache).trackResponse(responseSource);
186c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
187c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
188c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        /*
189c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath         * The raw response source may require the network, but the request
190c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath         * headers may forbid network use. In that case, dispose of the network
191c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath         * response and use a BAD_GATEWAY response instead.
192c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath         */
193c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (requestHeaders.isOnlyIfCached() && responseSource.requiresConnection()) {
194c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
195c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                IoUtils.closeQuietly(cachedResponseBody);
196c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
197c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            this.responseSource = ResponseSource.CACHE;
198c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            this.cacheResponse = BAD_GATEWAY_RESPONSE;
199c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            RawHeaders rawResponseHeaders = RawHeaders.fromMultimap(cacheResponse.getHeaders());
200c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            setResponse(new ResponseHeaders(uri, rawResponseHeaders), cacheResponse.getBody());
201c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
202c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
203c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (responseSource.requiresConnection()) {
204c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            sendSocketRequest();
205c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        } else if (connection != null) {
206c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            HttpConnectionPool.INSTANCE.recycle(connection);
207c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            connection = null;
208c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
209c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
210c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
211c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
212c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * Initialize the source for this response. It may be corrected later if the
213c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * request headers forbids network use.
214c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
215c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private void initResponseSource() throws IOException {
216c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        responseSource = ResponseSource.NETWORK;
217c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (!policy.getUseCaches() || responseCache == null) {
218c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return;
219c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
220c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
221c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        CacheResponse candidate = responseCache.get(uri, method,
222c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                requestHeaders.getHeaders().toMultimap());
223c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (candidate == null) {
224c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return;
225c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
226c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
227c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        Map<String, List<String>> responseHeadersMap = candidate.getHeaders();
228c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        cachedResponseBody = candidate.getBody();
229c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (!acceptCacheResponseType(candidate)
230c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                || responseHeadersMap == null
231c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                || cachedResponseBody == null) {
232c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            IoUtils.closeQuietly(cachedResponseBody);
233c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return;
234c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
235c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
236c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        RawHeaders rawResponseHeaders = RawHeaders.fromMultimap(responseHeadersMap);
237c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        cachedResponseHeaders = new ResponseHeaders(uri, rawResponseHeaders);
238c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        long now = System.currentTimeMillis();
239c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        this.responseSource = cachedResponseHeaders.chooseResponseSource(now, requestHeaders);
240c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (responseSource == ResponseSource.CACHE) {
241c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            this.cacheResponse = candidate;
242c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            setResponse(cachedResponseHeaders, cachedResponseBody);
243c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        } else if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
244c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            this.cacheResponse = candidate;
245c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        } else if (responseSource == ResponseSource.NETWORK) {
246c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            IoUtils.closeQuietly(cachedResponseBody);
247c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        } else {
248c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            throw new AssertionError();
249c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
250c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
251c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
252c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private void sendSocketRequest() throws IOException {
253c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (connection == null) {
254c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            connect();
255c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
256c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
257c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (transport != null) {
258c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            throw new IllegalStateException();
259c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
260c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
261c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        transport = connection.newTransport(this);
262c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
263c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (hasRequestBody() && requestBodyOut == null) {
264c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            // Create a request body if we don't have one already. We'll already
265c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            // have one if we're retrying a failed POST.
266c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            requestBodyOut = transport.createRequestBody();
267c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
268c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
269c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
270c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
271c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * Connect to the origin server either directly or via a proxy.
272c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
273c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    protected void connect() throws IOException {
274c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (connection == null) {
275c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            connection = openSocketConnection();
276c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
277c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
278c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
279c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    protected final HttpConnection openSocketConnection() throws IOException {
280c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        HttpConnection result = HttpConnection.connect(uri, getSslSocketFactory(),
281c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                policy.getProxy(), requiresTunnel(), policy.getConnectTimeout());
282c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        Proxy proxy = result.getAddress().getProxy();
283c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (proxy != null) {
284c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            policy.setProxy(proxy);
285c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            // Add the authority to the request line when we're using a proxy.
286c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            requestHeaders.getHeaders().setStatusLine(getRequestLine());
287c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
288c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        result.setSoTimeout(policy.getReadTimeout());
289c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return result;
290c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
291c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
292c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
293c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * @param body the response body, or null if it doesn't exist or isn't
294c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     *     available.
295c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
296c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private void setResponse(ResponseHeaders headers, InputStream body) throws IOException {
297c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (this.responseBodyIn != null) {
298c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            throw new IllegalStateException();
299c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
300c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        this.responseHeaders = headers;
301c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (body != null) {
302c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            initContentStream(body);
303c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
304c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
305c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
306c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    boolean hasRequestBody() {
307c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return method == POST || method == PUT;
308c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
309c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
310c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
311c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * Returns the request body or null if this request doesn't have a body.
312c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
313c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public final OutputStream getRequestBody() {
314c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (responseSource == null) {
315c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            throw new IllegalStateException();
316c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
317c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return requestBodyOut;
318c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
319c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
320c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public final boolean hasResponse() {
321c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return responseHeaders != null;
322c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
323c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
324c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public final RequestHeaders getRequestHeaders() {
325c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return requestHeaders;
326c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
327c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
328c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public final ResponseHeaders getResponseHeaders() {
329c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (responseHeaders == null) {
330c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            throw new IllegalStateException();
331c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
332c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return responseHeaders;
333c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
334c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
335c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public final int getResponseCode() {
336c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (responseHeaders == null) {
337c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            throw new IllegalStateException();
338c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
339c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return responseHeaders.getHeaders().getResponseCode();
340c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
341c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
342c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public final InputStream getResponseBody() {
343c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (responseHeaders == null) {
344c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            throw new IllegalStateException();
345c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
346c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return responseBodyIn;
347c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
348c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
349c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public final CacheResponse getCacheResponse() {
350c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return cacheResponse;
351c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
352c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
353c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public final HttpConnection getConnection() {
354c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return connection;
355c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
356c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
357c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public final boolean hasRecycledConnection() {
358c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return connection != null && connection.isRecycled();
359c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
360c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
361c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
362c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * Returns true if {@code cacheResponse} is of the right type. This
363c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * condition is necessary but not sufficient for the cached response to
364c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * be used.
365c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
366c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    protected boolean acceptCacheResponseType(CacheResponse cacheResponse) {
367c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return true;
368c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
369c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
370c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private void maybeCache() throws IOException {
371c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        // Are we caching at all?
372c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (!policy.getUseCaches() || responseCache == null) {
373c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return;
374c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
375c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
376c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        // Should we cache this response for this request?
377c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (!responseHeaders.isCacheable(requestHeaders)) {
378c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return;
379c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
380c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
381c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        // Offer this request to the cache.
382c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        cacheRequest = responseCache.put(uri, getHttpConnectionToCache());
383c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
384c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
385c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    protected OkHttpConnection getHttpConnectionToCache() {
386c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return policy;
387c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
388c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
389c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
390c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * Cause the socket connection to be released to the connection pool when
391c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * it is no longer needed. If it is already unneeded, it will be pooled
392c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * immediately. Otherwise the connection is held so that redirects can be
393c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * handled by the same connection.
394c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
395c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public final void automaticallyReleaseConnectionToPool() {
396c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        automaticallyReleaseConnectionToPool = true;
397c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (connection != null && connectionReleased) {
398c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            HttpConnectionPool.INSTANCE.recycle(connection);
399c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            connection = null;
400c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
401c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
402c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
403c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
404c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * Releases this engine so that its resources may be either reused or
405c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * closed. Also call {@link #automaticallyReleaseConnectionToPool} unless
406c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * the connection will be used to follow a redirect.
407c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
408c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public final void release(boolean reusable) {
409c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        // If the response body comes from the cache, close it.
410c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (responseBodyIn == cachedResponseBody) {
411c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            IoUtils.closeQuietly(responseBodyIn);
412c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
413c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
414c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (!connectionReleased && connection != null) {
415c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            connectionReleased = true;
416c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
417c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (!reusable || !transport.makeReusable(requestBodyOut, responseBodyIn)) {
418c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                connection.closeSocketAndStreams();
419c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                connection = null;
420c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            } else if (automaticallyReleaseConnectionToPool) {
421c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                HttpConnectionPool.INSTANCE.recycle(connection);
422c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                connection = null;
423c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
424c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
425c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
426c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
427c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private void initContentStream(InputStream transferStream) throws IOException {
428c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (transparentGzip && responseHeaders.isContentEncodingGzip()) {
429c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            /*
430c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath             * If the response was transparently gzipped, remove the gzip header field
431c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath             * so clients don't double decompress. http://b/3009828
432c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath             */
433c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            responseHeaders.stripContentEncoding();
434c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            responseBodyIn = new GZIPInputStream(transferStream);
435c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        } else {
436c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            responseBodyIn = transferStream;
437c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
438c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
439c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
440c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
441c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * Returns true if the response must have a (possibly 0-length) body.
442c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * See RFC 2616 section 4.3.
443c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
444c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public final boolean hasResponseBody() {
445c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        int responseCode = responseHeaders.getHeaders().getResponseCode();
446c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
447c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        // HEAD requests never yield a body regardless of the response headers.
448c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (method == HEAD) {
449c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return false;
450c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
451c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
452c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (method != CONNECT
453c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                && (responseCode < HTTP_CONTINUE || responseCode >= 200)
454c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                && responseCode != HttpURLConnectionImpl.HTTP_NO_CONTENT
455c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                && responseCode != HttpURLConnectionImpl.HTTP_NOT_MODIFIED) {
456c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return true;
457c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
458c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
459c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        /*
460c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath         * If the Content-Length or Transfer-Encoding headers disagree with the
461c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath         * response code, the response is malformed. For best compatibility, we
462c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath         * honor the headers.
463c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath         */
464c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (responseHeaders.getContentLength() != -1 || responseHeaders.isChunked()) {
465c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return true;
466c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
467c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
468c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return false;
469c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
470c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
471c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
472c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * Populates requestHeaders with defaults and cookies.
473c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     *
474c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * <p>This client doesn't specify a default {@code Accept} header because it
475c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * doesn't know what content types the application is interested in.
476c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
477c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private void prepareRawRequestHeaders() throws IOException {
478c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        requestHeaders.getHeaders().setStatusLine(getRequestLine());
479c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
480c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (requestHeaders.getUserAgent() == null) {
481c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            requestHeaders.setUserAgent(getDefaultUserAgent());
482c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
483c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
484c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (requestHeaders.getHost() == null) {
485c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            requestHeaders.setHost(getOriginAddress(policy.getURL()));
486c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
487c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
488c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        // TODO: this shouldn't be set for SPDY (it's ignored)
489c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if ((connection == null || connection.httpMinorVersion != 0)
490c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                && requestHeaders.getConnection() == null) {
491c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            requestHeaders.setConnection("Keep-Alive");
492c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
493c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
494c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (requestHeaders.getAcceptEncoding() == null) {
495c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            transparentGzip = true;
496c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            // TODO: this shouldn't be set for SPDY (it isn't necessary)
497c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            requestHeaders.setAcceptEncoding("gzip");
498c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
499c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
500c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (hasRequestBody() && requestHeaders.getContentType() == null) {
501c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            requestHeaders.setContentType("application/x-www-form-urlencoded");
502c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
503c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
504c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        long ifModifiedSince = policy.getIfModifiedSince();
505c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (ifModifiedSince != 0) {
506c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            requestHeaders.setIfModifiedSince(new Date(ifModifiedSince));
507c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
508c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
509c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        CookieHandler cookieHandler = CookieHandler.getDefault();
510c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (cookieHandler != null) {
511c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            requestHeaders.addCookies(
512c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                    cookieHandler.get(uri, requestHeaders.getHeaders().toMultimap()));
513c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
514c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
515c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
516c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
517c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * Returns the request status line, like "GET / HTTP/1.1". This is exposed
518c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * to the application by {@link HttpURLConnectionImpl#getHeaderFields}, so
519c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * it needs to be set even if the transport is SPDY.
520c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
521c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    String getRequestLine() {
522c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        String protocol = (connection == null || connection.httpMinorVersion != 0)
523c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                ? "HTTP/1.1"
524c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                : "HTTP/1.0";
525c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return method + " " + requestString() + " " + protocol;
526c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
527c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
528c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    private String requestString() {
529c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        URL url = policy.getURL();
530c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (includeAuthorityInRequestLine()) {
531c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return url.toString();
532c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        } else {
533c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            String fileOnly = url.getFile();
534c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (fileOnly == null) {
535c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                fileOnly = "/";
536c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            } else if (!fileOnly.startsWith("/")) {
537c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                fileOnly = "/" + fileOnly;
538c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
539c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return fileOnly;
540c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
541c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
542c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
543c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
544c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * Returns true if the request line should contain the full URL with host
545c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * and port (like "GET http://android.com/foo HTTP/1.1") or only the path
546c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * (like "GET /foo HTTP/1.1").
547c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     *
548c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * <p>This is non-final because for HTTPS it's never necessary to supply the
549c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * full URL, even if a proxy is in use.
550c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
551c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    protected boolean includeAuthorityInRequestLine() {
552c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return policy.usingProxy();
553c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
554c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
555c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
556c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * Returns the SSL configuration for connections created by this engine.
557c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * We cannot reuse HTTPS connections if the socket factory has changed.
558c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
559c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    protected SSLSocketFactory getSslSocketFactory() {
560c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return null;
561c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
562c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
563c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    protected final String getDefaultUserAgent() {
564c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        String agent = System.getProperty("http.agent");
565c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return agent != null ? agent : ("Java" + System.getProperty("java.version"));
566c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
567c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
568c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    protected final String getOriginAddress(URL url) {
569c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        int port = url.getPort();
570c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        String result = url.getHost();
571c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (port > 0 && port != policy.getDefaultPort()) {
572c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            result = result + ":" + port;
573c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
574c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return result;
575c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
576c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
577c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    protected boolean requiresTunnel() {
578c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        return false;
579c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
580c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
581c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
582c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * Flushes the remaining request header and body, parses the HTTP response
583c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     * headers and starts reading the HTTP response body if it exists.
584c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
585c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    public final void readResponse() throws IOException {
586c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (hasResponse()) {
587c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return;
588c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
589c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
590c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (responseSource == null) {
591c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            throw new IllegalStateException("readResponse() without sendRequest()");
592c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
593c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
594c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (!responseSource.requiresConnection()) {
595c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            return;
596c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
597c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
598c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (sentRequestMillis == -1) {
599c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (requestBodyOut instanceof RetryableOutputStream) {
600c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                int contentLength = ((RetryableOutputStream) requestBodyOut).contentLength();
601c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                requestHeaders.setContentLength(contentLength);
602c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
603c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            transport.writeRequestHeaders();
604c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
605c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
606c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (requestBodyOut != null) {
607c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            requestBodyOut.close();
608c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (requestBodyOut instanceof RetryableOutputStream) {
609c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                transport.writeRequestBody((RetryableOutputStream) requestBodyOut);
610c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
611c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
612c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
613c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        transport.flushRequest();
614c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
615c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        responseHeaders = transport.readResponseHeaders();
616c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        responseHeaders.setLocalTimestamps(sentRequestMillis, System.currentTimeMillis());
617c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
618c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
619c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            if (cachedResponseHeaders.validate(responseHeaders)) {
620c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                release(true);
621c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                ResponseHeaders combinedHeaders = cachedResponseHeaders.combine(responseHeaders);
622c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                setResponse(combinedHeaders, cachedResponseBody);
623c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                if (responseCache instanceof ExtendedResponseCache) {
624c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                    ExtendedResponseCache httpResponseCache = (ExtendedResponseCache) responseCache;
625c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                    httpResponseCache.trackConditionalCacheHit();
626c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                    httpResponseCache.update(cacheResponse, getHttpConnectionToCache());
627c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                }
628c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                return;
629c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            } else {
630c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath                IoUtils.closeQuietly(cachedResponseBody);
631c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
632c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
633c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
634c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        if (hasResponseBody()) {
635c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            maybeCache(); // reentrant. this calls into user code which may call back into this!
636c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
637c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
638c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        initContentStream(transport.getTransferStream(cacheRequest));
639c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
640c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath}
641