1953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson/*
2953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *  Licensed to the Apache Software Foundation (ASF) under one or more
3953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *  contributor license agreements.  See the NOTICE file distributed with
4953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *  this work for additional information regarding copyright ownership.
5953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *  The ASF licenses this file to You under the Apache License, Version 2.0
6953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *  (the "License"); you may not use this file except in compliance with
7953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *  the License.  You may obtain a copy of the License at
8953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *
9953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *     http://www.apache.org/licenses/LICENSE-2.0
10953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *
11953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *  Unless required by applicable law or agreed to in writing, software
12953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *  distributed under the License is distributed on an "AS IS" BASIS,
13953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *  See the License for the specific language governing permissions and
15953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *  limitations under the License.
16953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson */
17953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
18953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonpackage libcore.net.http;
19953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
20953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.io.BufferedOutputStream;
21953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.io.ByteArrayInputStream;
22953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.io.IOException;
23953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.io.InputStream;
24953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.io.OutputStream;
25953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.net.CacheRequest;
26953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.net.CacheResponse;
27953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.net.CookieHandler;
28e636ca4d6bd101324bd95fbc817401e6e0b80a2cJesse Wilsonimport java.net.ExtendedResponseCache;
29953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.net.HttpURLConnection;
30953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.net.Proxy;
31953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.net.ResponseCache;
32e636ca4d6bd101324bd95fbc817401e6e0b80a2cJesse Wilsonimport java.net.ResponseSource;
33953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.net.URI;
34953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.net.URISyntaxException;
35953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.net.URL;
36953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.nio.charset.Charsets;
37953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.util.Collections;
389531eea15052eccc004b5f853ab4452becf7a8abJesse Wilsonimport java.util.Date;
39953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.util.HashMap;
40953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.util.List;
41953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.util.Map;
42953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport java.util.zip.GZIPInputStream;
43fa5e8dfe3c7ed144b0fbe69091628dedd6a3b961Jesse Wilsonimport javax.net.ssl.SSLSocketFactory;
44953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport libcore.io.IoUtils;
45953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport libcore.io.Streams;
46953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonimport libcore.util.EmptyArray;
47953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
48953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson/**
49953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson * Handles a single HTTP request/response pair. Each HTTP engine follows this
50953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson * lifecycle:
51953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson * <ol>
52953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *     <li>It is created.
53953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *     <li>The HTTP request message is sent with sendRequest(). Once the request
54953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *         is sent it is an error to modify the request headers. After
55953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *         sendRequest() has been called the request body can be written to if
56953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *         it exists.
57953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *     <li>The HTTP response message is read with readResponse(). After the
58953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *         response has been read the response headers and body can be read.
59953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *         All responses have a response body input stream, though in some
60953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *         instances this stream is empty.
61953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson * </ol>
62953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *
63953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson * <p>The request and response may be served by the HTTP response cache, by the
64953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson * network, or by both in the event of a conditional GET.
65953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson *
66953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson * <p>This class may hold a socket connection that needs to be released or
67a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson * recycled. By default, this socket connection is held when the last byte of
68a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson * the response is consumed. To release the connection when it is no longer
69a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson * required, use {@link #automaticallyReleaseConnectionToPool()}.
70953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson */
71953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilsonpublic class HttpEngine {
72fddea0213028dd6d467f316584fac0f6e0745ce9Elliott Hughes    private static final CacheResponse GATEWAY_TIMEOUT_RESPONSE = new CacheResponse() {
73953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        @Override public Map<String, List<String>> getHeaders() throws IOException {
74953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            Map<String, List<String>> result = new HashMap<String, List<String>>();
75fddea0213028dd6d467f316584fac0f6e0745ce9Elliott Hughes            result.put(null, Collections.singletonList("HTTP/1.1 504 Gateway Timeout"));
76953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            return result;
77953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
78953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        @Override public InputStream getBody() throws IOException {
79953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            return new ByteArrayInputStream(EmptyArray.BYTE);
80953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
81953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    };
82953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
83953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
84953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * The maximum number of bytes to buffer when sending headers and a request
85953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * body. When the headers and body can be sent in a single write, the
86953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * request completes sooner. In one WiFi benchmark, using a large enough
87953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * buffer sped up some uploads by half.
88953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
89953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private static final int MAX_REQUEST_BUFFER_LENGTH = 32768;
90953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
91953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public static final int DEFAULT_CHUNK_LENGTH = 1024;
92953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
93953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public static final String OPTIONS = "OPTIONS";
94953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public static final String GET = "GET";
95953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public static final String HEAD = "HEAD";
96953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public static final String POST = "POST";
97953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public static final String PUT = "PUT";
98953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public static final String DELETE = "DELETE";
99953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public static final String TRACE = "TRACE";
100953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public static final String CONNECT = "CONNECT";
101953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
102953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public static final int HTTP_CONTINUE = 100;
103953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
104953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
105953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * HTTP 1.1 doesn't specify how many redirects to follow, but HTTP/1.0
106953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * recommended 5. http://www.w3.org/Protocols/HTTP/1.0/spec.html#Code3xx
107953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
108953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public static final int MAX_REDIRECTS = 5;
109953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
110953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    protected final HttpURLConnectionImpl policy;
111953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
112953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    protected final String method;
113953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
114953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private ResponseSource responseSource;
115953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
116953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    protected HttpConnection connection;
117953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private InputStream socketIn;
118953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private OutputStream socketOut;
119953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
120953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
121953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * This stream buffers the request headers and the request body when their
122953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * combined size is less than MAX_REQUEST_BUFFER_LENGTH. By combining them
123953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * we can save socket writes, which in turn saves a packet transmission.
124953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * This is socketOut if the request size is large or unknown.
125953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
126953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private OutputStream requestOut;
127953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private AbstractHttpOutputStream requestBodyOut;
128953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
129953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private InputStream responseBodyIn;
130953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
131953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private final ResponseCache responseCache = ResponseCache.getDefault();
132953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private CacheResponse cacheResponse;
133953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private CacheRequest cacheRequest;
134953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
135953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /** The time when the request headers were written, or -1 if they haven't been written yet. */
136953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private long sentRequestMillis = -1;
137953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
138953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
139953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * True if this client added an "Accept-Encoding: gzip" header field and is
140953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * therefore responsible for also decompressing the transfer stream.
141953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
142953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private boolean transparentGzip;
143953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
144953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    boolean sendChunked;
145953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
146953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
147953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * The version this client will use. Either 0 for HTTP/1.0, or 1 for
148953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * HTTP/1.1. Upon receiving a non-HTTP/1.1 response, this client
149953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * automatically sets its version to HTTP/1.0.
150953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
151953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    // TODO: is HTTP minor version tracked across HttpEngines?
152953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private int httpMinorVersion = 1; // Assume HTTP/1.1
153953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
154953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private final URI uri;
155953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
15609f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson    private final RequestHeaders requestHeaders;
157953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
158953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /** Null until a response is received from the network or the cache */
1597a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson    private ResponseHeaders responseHeaders;
160953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
161953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /*
162953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * The cache response currently being validated on a conditional get. Null
163953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * if the cached response doesn't exist or doesn't need validation. If the
164953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * conditional get succeeds, these will be used for the response headers and
165953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * body. If it fails, these be closed and set to null.
166953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
167757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    private ResponseHeaders cachedResponseHeaders;
168757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    private InputStream cachedResponseBody;
169953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
170953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
171953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * True if the socket connection should be released to the connection pool
172953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * when the response has been fully read.
173953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
174a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson    private boolean automaticallyReleaseConnectionToPool;
175a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
176a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson    /** True if the socket connection is no longer needed by this engine. */
177757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    private boolean connectionReleased;
178953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
179953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
18009f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson     * @param requestHeaders the client's supplied request headers. This class
18109f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson     *     creates a private copy that it can mutate.
182953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * @param connection the connection used for an intermediate response
183953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     *     immediately prior to this request/response pair, such as a same-host
184953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     *     redirect. This engine assumes ownership of the connection and must
185953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     *     release it when it is unneeded.
186953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
187953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public HttpEngine(HttpURLConnectionImpl policy, String method, RawHeaders requestHeaders,
188953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            HttpConnection connection, RetryableOutputStream requestBodyOut) throws IOException {
189953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        this.policy = policy;
190953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        this.method = method;
191953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        this.connection = connection;
192953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        this.requestBodyOut = requestBodyOut;
193953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
194953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        try {
195953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            uri = policy.getURL().toURILenient();
196953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        } catch (URISyntaxException e) {
197953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            throw new IOException(e);
198953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
19909f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson
20009f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson        this.requestHeaders = new RequestHeaders(uri, new RawHeaders(requestHeaders));
201953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
202953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
2035d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson    public URI getUri() {
2045d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson        return uri;
2055d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson    }
2065d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson
207953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
208953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * Figures out what the response source will be, and opens a socket to that
209953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * source if necessary. Prepares the request headers and gets ready to start
210953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * writing the request body if it exists.
211953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
212953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public final void sendRequest() throws IOException {
213953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (responseSource != null) {
214953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            return;
215953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
216953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
217953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        prepareRawRequestHeaders();
21809f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson        initResponseSource();
219e636ca4d6bd101324bd95fbc817401e6e0b80a2cJesse Wilson        if (responseCache instanceof ExtendedResponseCache) {
220e636ca4d6bd101324bd95fbc817401e6e0b80a2cJesse Wilson            ((ExtendedResponseCache) responseCache).trackResponse(responseSource);
221ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson        }
222953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
223953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        /*
224953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson         * The raw response source may require the network, but the request
225953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson         * headers may forbid network use. In that case, dispose of the network
226fddea0213028dd6d467f316584fac0f6e0745ce9Elliott Hughes         * response and use a GATEWAY_TIMEOUT response instead, as specified
227fddea0213028dd6d467f316584fac0f6e0745ce9Elliott Hughes         * by http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4.
228953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson         */
229ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson        if (requestHeaders.isOnlyIfCached() && responseSource.requiresConnection()) {
230953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
231757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                IoUtils.closeQuietly(cachedResponseBody);
232953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            }
233953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            this.responseSource = ResponseSource.CACHE;
234fddea0213028dd6d467f316584fac0f6e0745ce9Elliott Hughes            this.cacheResponse = GATEWAY_TIMEOUT_RESPONSE;
2357a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson            RawHeaders rawResponseHeaders = RawHeaders.fromMultimap(cacheResponse.getHeaders());
2367a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson            setResponse(new ResponseHeaders(uri, rawResponseHeaders), cacheResponse.getBody());
237953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
238953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
239953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (responseSource.requiresConnection()) {
240953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            sendSocketRequest();
241a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        } else if (connection != null) {
242a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson            HttpConnectionPool.INSTANCE.recycle(connection);
243a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson            connection = null;
244953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
245953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
246953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
247953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
248953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * Initialize the source for this response. It may be corrected later if the
249953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * request headers forbids network use.
250953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
25109f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson    private void initResponseSource() throws IOException {
252953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        responseSource = ResponseSource.NETWORK;
253953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (!policy.getUseCaches() || responseCache == null) {
254953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            return;
255953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
256953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
25709f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson        CacheResponse candidate = responseCache.get(uri, method,
258ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson                requestHeaders.getHeaders().toMultimap());
259433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson        if (candidate == null) {
260953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            return;
261953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
262433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson
263757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        Map<String, List<String>> responseHeadersMap = candidate.getHeaders();
264757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        cachedResponseBody = candidate.getBody();
265757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        if (!acceptCacheResponseType(candidate)
266757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                || responseHeadersMap == null
267757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                || cachedResponseBody == null) {
268757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson            IoUtils.closeQuietly(cachedResponseBody);
269953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            return;
270953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
271953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
2727a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson        RawHeaders rawResponseHeaders = RawHeaders.fromMultimap(responseHeadersMap);
2737a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson        cachedResponseHeaders = new ResponseHeaders(uri, rawResponseHeaders);
274953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        long now = System.currentTimeMillis();
27509f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson        this.responseSource = cachedResponseHeaders.chooseResponseSource(now, requestHeaders);
276953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (responseSource == ResponseSource.CACHE) {
277953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            this.cacheResponse = candidate;
2787a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson            setResponse(cachedResponseHeaders, cachedResponseBody);
279953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        } else if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
280953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            this.cacheResponse = candidate;
281953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        } else if (responseSource == ResponseSource.NETWORK) {
282757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson            IoUtils.closeQuietly(cachedResponseBody);
283953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        } else {
284953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            throw new AssertionError();
285953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
286953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
287953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
288953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private void sendSocketRequest() throws IOException {
289953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (connection == null) {
290953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            connect();
291953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
292953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
293953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (socketOut != null || requestOut != null || socketIn != null) {
294953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            throw new IllegalStateException();
295953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
296953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
297953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        socketOut = connection.getOutputStream();
298953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        requestOut = socketOut;
299953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        socketIn = connection.getInputStream();
300953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
3019531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        if (hasRequestBody()) {
302953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            initRequestBodyOut();
303953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
304953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
305953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
306953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
307953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * Connect to the origin server either directly or via a proxy.
308953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
309953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    protected void connect() throws IOException {
310953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (connection == null) {
311953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            connection = openSocketConnection();
312953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
313953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
314953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
315953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    protected final HttpConnection openSocketConnection() throws IOException {
316fa5e8dfe3c7ed144b0fbe69091628dedd6a3b961Jesse Wilson        HttpConnection result = HttpConnection.connect(uri, getSslSocketFactory(),
317fa5e8dfe3c7ed144b0fbe69091628dedd6a3b961Jesse Wilson                policy.getProxy(), requiresTunnel(), policy.getConnectTimeout());
318953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        Proxy proxy = result.getAddress().getProxy();
319953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (proxy != null) {
320953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            policy.setProxy(proxy);
321953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
322953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        result.setSoTimeout(policy.getReadTimeout());
323953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        return result;
324953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
325953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
326953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    protected void initRequestBodyOut() throws IOException {
327953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        int chunkLength = policy.getChunkLength();
32809f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson        if (chunkLength > 0 || requestHeaders.isChunked()) {
329953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            sendChunked = true;
330953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            if (chunkLength == -1) {
331953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                chunkLength = DEFAULT_CHUNK_LENGTH;
332953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            }
333953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
334953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
335953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (socketOut == null) {
336953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            throw new IllegalStateException("No socket to write to; was a POST cached?");
337953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
338953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
339953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (httpMinorVersion == 0) {
340953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            sendChunked = false;
341953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
342953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
343953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        int fixedContentLength = policy.getFixedContentLength();
344953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (requestBodyOut != null) {
345953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            // request body was already initialized by the predecessor HTTP engine
346953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        } else if (fixedContentLength != -1) {
347953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            writeRequestHeaders(fixedContentLength);
348953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            requestBodyOut = new FixedLengthOutputStream(requestOut, fixedContentLength);
349953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        } else if (sendChunked) {
350953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            writeRequestHeaders(-1);
351953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            requestBodyOut = new ChunkedOutputStream(requestOut, chunkLength);
352ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson        } else if (requestHeaders.getContentLength() != -1) {
353ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson            writeRequestHeaders(requestHeaders.getContentLength());
354ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson            requestBodyOut = new RetryableOutputStream(requestHeaders.getContentLength());
355953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        } else {
356953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            requestBodyOut = new RetryableOutputStream();
357953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
358953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
359953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
360953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
361953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * @param body the response body, or null if it doesn't exist or isn't
362953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     *     available.
363953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
3647a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson    private void setResponse(ResponseHeaders headers, InputStream body) throws IOException {
365953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (this.responseBodyIn != null) {
366953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            throw new IllegalStateException();
367953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
3687a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson        this.responseHeaders = headers;
369ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson        this.httpMinorVersion = responseHeaders.getHeaders().getHttpMinorVersion();
370953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (body != null) {
371953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            initContentStream(body);
372953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
373953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
374953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
3759531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    private boolean hasRequestBody() {
3769531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        return method == POST || method == PUT;
3779531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson    }
3789531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson
379953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
380953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * Returns the request body or null if this request doesn't have a body.
381953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
382953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public final OutputStream getRequestBody() {
383953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (responseSource == null) {
384953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            throw new IllegalStateException();
385953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
386953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        return requestBodyOut;
387953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
388953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
389953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public final boolean hasResponse() {
3907a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson        return responseHeaders != null;
391953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
392953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
39309f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson    public final RequestHeaders getRequestHeaders() {
39409f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson        return requestHeaders;
395953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
396953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
3977a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson    public final ResponseHeaders getResponseHeaders() {
3987a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson        if (responseHeaders == null) {
399953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            throw new IllegalStateException();
400953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
4017a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson        return responseHeaders;
4027a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson    }
4037a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson
4047a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson    public final int getResponseCode() {
4057a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson        if (responseHeaders == null) {
4067a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson            throw new IllegalStateException();
4077a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson        }
408ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson        return responseHeaders.getHeaders().getResponseCode();
409953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
410953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
411953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public final InputStream getResponseBody() {
4127a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson        if (responseHeaders == null) {
413953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            throw new IllegalStateException();
414953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
415953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        return responseBodyIn;
416953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
417953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
418953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public final CacheResponse getCacheResponse() {
419953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        return cacheResponse;
420953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
421953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
422953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public final HttpConnection getConnection() {
423953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        return connection;
424953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
425953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
426b2b02ac6cd42a69463fd172531aa1f9b9bb887a8Jesse Wilson    public final boolean hasRecycledConnection() {
427b2b02ac6cd42a69463fd172531aa1f9b9bb887a8Jesse Wilson        return connection != null && connection.isRecycled();
428b2b02ac6cd42a69463fd172531aa1f9b9bb887a8Jesse Wilson    }
429b2b02ac6cd42a69463fd172531aa1f9b9bb887a8Jesse Wilson
430953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
431953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * Returns true if {@code cacheResponse} is of the right type. This
432953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * condition is necessary but not sufficient for the cached response to
433953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * be used.
434953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
435953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    protected boolean acceptCacheResponseType(CacheResponse cacheResponse) {
436953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        return true;
437953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
438953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
439953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private void maybeCache() throws IOException {
4404ec2d0dea800397fb32227e0631246f4d2a73191Brian Carlstrom        // Never cache responses to proxy CONNECT requests.
4414ec2d0dea800397fb32227e0631246f4d2a73191Brian Carlstrom        if (method == CONNECT) {
4424ec2d0dea800397fb32227e0631246f4d2a73191Brian Carlstrom            return;
4434ec2d0dea800397fb32227e0631246f4d2a73191Brian Carlstrom        }
4444ec2d0dea800397fb32227e0631246f4d2a73191Brian Carlstrom
445953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        // Are we caching at all?
446953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (!policy.getUseCaches() || responseCache == null) {
447953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            return;
448953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
449953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
450953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        // Should we cache this response for this request?
45109f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson        if (!responseHeaders.isCacheable(requestHeaders)) {
452953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            return;
453953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
454953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
455953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        // Offer this request to the cache.
456953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        cacheRequest = responseCache.put(uri, getHttpConnectionToCache());
457953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
458953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
459953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    protected HttpURLConnection getHttpConnectionToCache() {
460953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        return policy;
461953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
462953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
463953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
464a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson     * Cause the socket connection to be released to the connection pool when
465a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson     * it is no longer needed. If it is already unneeded, it will be pooled
466a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson     * immediately.
467a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson     */
468a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson    public final void automaticallyReleaseConnectionToPool() {
469a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        automaticallyReleaseConnectionToPool = true;
470757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        if (connection != null && connectionReleased) {
471a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson            HttpConnectionPool.INSTANCE.recycle(connection);
472a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson            connection = null;
473a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        }
474a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson    }
475a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
476a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson    /**
477757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson     * Releases this engine so that its resources may be either reused or
478757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson     * closed.
479953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
480757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson    public final void release(boolean reusable) {
481757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        // If the response body comes from the cache, close it.
482757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        if (responseBodyIn == cachedResponseBody) {
483757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson            IoUtils.closeQuietly(responseBodyIn);
484a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson        }
485a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson
486757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson        if (!connectionReleased && connection != null) {
487757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson            connectionReleased = true;
488953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
489757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson            // We cannot reuse sockets that have incomplete output.
490757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson            if (requestBodyOut != null && !requestBodyOut.closed) {
491757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                reusable = false;
492757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson            }
493953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
494b11e44a402eb0f9c5da1f7482fc1eabdd9adff47Jesse Wilson            // If the request specified that the connection shouldn't be reused,
495b11e44a402eb0f9c5da1f7482fc1eabdd9adff47Jesse Wilson            // don't reuse it. This advice doesn't apply to CONNECT requests because
496b11e44a402eb0f9c5da1f7482fc1eabdd9adff47Jesse Wilson            // the "Connection: close" header goes the origin server, not the proxy.
497b11e44a402eb0f9c5da1f7482fc1eabdd9adff47Jesse Wilson            if (requestHeaders.hasConnectionClose() && method != CONNECT) {
498b11e44a402eb0f9c5da1f7482fc1eabdd9adff47Jesse Wilson                reusable = false;
499b11e44a402eb0f9c5da1f7482fc1eabdd9adff47Jesse Wilson            }
500b11e44a402eb0f9c5da1f7482fc1eabdd9adff47Jesse Wilson
501b11e44a402eb0f9c5da1f7482fc1eabdd9adff47Jesse Wilson            // If the response specified that the connection shouldn't be reused, don't reuse it.
502b11e44a402eb0f9c5da1f7482fc1eabdd9adff47Jesse Wilson            if (responseHeaders != null && responseHeaders.hasConnectionClose()) {
503757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                reusable = false;
504757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson            }
505953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
506757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson            if (responseBodyIn instanceof UnknownLengthHttpInputStream) {
507a4193d7e636802a2705ffb52cb69c69ff59bfbb2Jesse Wilson                reusable = false;
508953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            }
509953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
510757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson            if (reusable && responseBodyIn != null) {
511757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                // We must discard the response body before the connection can be reused.
512757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                try {
513757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                    Streams.skipAll(responseBodyIn);
514757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                } catch (IOException e) {
515757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                    reusable = false;
516757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                }
517757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson            }
518757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson
519757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson            if (!reusable) {
520757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                connection.closeSocketAndStreams();
521757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                connection = null;
522757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson            } else if (automaticallyReleaseConnectionToPool) {
523757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                HttpConnectionPool.INSTANCE.recycle(connection);
524757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                connection = null;
525757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson            }
526953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
527953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
528953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
529953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private void initContentStream(InputStream transferStream) throws IOException {
5307a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson        if (transparentGzip && responseHeaders.isContentEncodingGzip()) {
531953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            /*
532953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson             * If the response was transparently gzipped, remove the gzip header field
533953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson             * so clients don't double decompress. http://b/3009828
5346d41a7cc3cb4cc684c8bece69ddc55954812ad6eNarayan Kamath             *
5356d41a7cc3cb4cc684c8bece69ddc55954812ad6eNarayan Kamath             * Also remove the Content-Length in this case because it contains the length
5366d41a7cc3cb4cc684c8bece69ddc55954812ad6eNarayan Kamath             * of the gzipped response. This isn't terribly useful and is dangerous because
5376d41a7cc3cb4cc684c8bece69ddc55954812ad6eNarayan Kamath             * clients can query the content length, but not the content encoding.
538953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson             */
5397a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson            responseHeaders.stripContentEncoding();
5406d41a7cc3cb4cc684c8bece69ddc55954812ad6eNarayan Kamath            responseHeaders.stripContentLength();
541953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            responseBodyIn = new GZIPInputStream(transferStream);
542953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        } else {
543953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            responseBodyIn = transferStream;
544953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
545953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
546953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
547953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private InputStream getTransferStream() throws IOException {
548953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (!hasResponseBody()) {
549953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            return new FixedLengthInputStream(socketIn, cacheRequest, this, 0);
550953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
551953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
5527a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson        if (responseHeaders.isChunked()) {
553953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            return new ChunkedInputStream(socketIn, cacheRequest, this);
554953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
555953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
556ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson        if (responseHeaders.getContentLength() != -1) {
5577a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson            return new FixedLengthInputStream(socketIn, cacheRequest, this,
558ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson                    responseHeaders.getContentLength());
559953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
560953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
561953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        /*
562953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson         * Wrap the input stream from the HttpConnection (rather than
563953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson         * just returning "socketIn" directly here), so that we can control
564953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson         * its use after the reference escapes.
565953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson         */
566953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        return new UnknownLengthHttpInputStream(socketIn, cacheRequest, this);
567953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
568953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
569953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private void readResponseHeaders() throws IOException {
570953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        RawHeaders headers;
571953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        do {
572953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            headers = new RawHeaders();
573f589846f86761ffea3c06ab9d105d3f19328d121Jesse Wilson            headers.setStatusLine(Streams.readAsciiLine(socketIn));
574953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            readHeaders(headers);
575953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        } while (headers.getResponseCode() == HTTP_CONTINUE);
5767a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson        setResponse(new ResponseHeaders(uri, headers), null);
577953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
578953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
579953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
580953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * Returns true if the response must have a (possibly 0-length) body.
581953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * See RFC 2616 section 4.3.
582953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
583953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public final boolean hasResponseBody() {
584ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson        int responseCode = responseHeaders.getHeaders().getResponseCode();
585df29c7dc69965462cd19de8910b04c8cb463e57fJesse Wilson
586df29c7dc69965462cd19de8910b04c8cb463e57fJesse Wilson        // HEAD requests never yield a body regardless of the response headers.
587df29c7dc69965462cd19de8910b04c8cb463e57fJesse Wilson        if (method == HEAD) {
588df29c7dc69965462cd19de8910b04c8cb463e57fJesse Wilson            return false;
589df29c7dc69965462cd19de8910b04c8cb463e57fJesse Wilson        }
590df29c7dc69965462cd19de8910b04c8cb463e57fJesse Wilson
591df29c7dc69965462cd19de8910b04c8cb463e57fJesse Wilson        if (method != CONNECT
592953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                && (responseCode < HTTP_CONTINUE || responseCode >= 200)
593953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                && responseCode != HttpURLConnectionImpl.HTTP_NO_CONTENT
594953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                && responseCode != HttpURLConnectionImpl.HTTP_NOT_MODIFIED) {
595953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            return true;
596953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
597953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
598953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        /*
599953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson         * If the Content-Length or Transfer-Encoding headers disagree with the
600953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson         * response code, the response is malformed. For best compatibility, we
601953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson         * honor the headers.
602953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson         */
603ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson        if (responseHeaders.getContentLength() != -1 || responseHeaders.isChunked()) {
604953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            return true;
605953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
606953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
607953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        return false;
608953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
609953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
610953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
611953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * Trailers are headers included after the last chunk of a response encoded
612953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * with chunked encoding.
613953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
614953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    final void readTrailers() throws IOException {
615ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson        readHeaders(responseHeaders.getHeaders());
616953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
617953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
618953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private void readHeaders(RawHeaders headers) throws IOException {
619953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        // parse the result headers until the first blank line
620953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        String line;
621f589846f86761ffea3c06ab9d105d3f19328d121Jesse Wilson        while (!(line = Streams.readAsciiLine(socketIn)).isEmpty()) {
622433e3fac172d0c4449051b0c61c0c63b298a0903Jesse Wilson            headers.addLine(line);
623953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
624953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
625953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        CookieHandler cookieHandler = CookieHandler.getDefault();
626953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (cookieHandler != null) {
627953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            cookieHandler.put(uri, headers.toMultimap());
628953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
629953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
630953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
631953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
632953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * Prepares the HTTP headers and sends them to the server.
633953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     *
634953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * <p>For streaming requests with a body, headers must be prepared
635953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * <strong>before</strong> the output stream has been written to. Otherwise
636953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * the body would need to be buffered!
637953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     *
638953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * <p>For non-streaming requests with a body, headers must be prepared
639953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * <strong>after</strong> the output stream has been written to and closed.
640953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * This ensures that the {@code Content-Length} header field receives the
641953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * proper value.
642953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     *
643953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * @param contentLength the number of bytes in the request body, or -1 if
644953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     *      the request body length is unknown.
645953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
646953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private void writeRequestHeaders(int contentLength) throws IOException {
647953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (sentRequestMillis != -1) {
648953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            throw new IllegalStateException();
649953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
650953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
651953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        RawHeaders headersToSend = getNetworkRequestHeaders();
652953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        byte[] bytes = headersToSend.toHeaderString().getBytes(Charsets.ISO_8859_1);
653953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
654953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (contentLength != -1 && bytes.length + contentLength <= MAX_REQUEST_BUFFER_LENGTH) {
655953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            requestOut = new BufferedOutputStream(socketOut, bytes.length + contentLength);
656953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
657953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
658953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        sentRequestMillis = System.currentTimeMillis();
659953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        requestOut.write(bytes);
660953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
661953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
662953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
663953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * Returns the headers to send on a network request.
664953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     *
665953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * <p>This adds the content length and content-type headers, which are
666953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * neither needed nor known when querying the response cache.
667953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     *
668953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * <p>It updates the status line, which may need to be fully qualified if
669953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * the connection is using a proxy.
670953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
671953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    protected RawHeaders getNetworkRequestHeaders() throws IOException {
672ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson        requestHeaders.getHeaders().setStatusLine(getRequestLine());
673953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
674953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        int fixedContentLength = policy.getFixedContentLength();
675953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (fixedContentLength != -1) {
67609f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson            requestHeaders.setContentLength(fixedContentLength);
677953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        } else if (sendChunked) {
67809f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson            requestHeaders.setChunked();
679953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        } else if (requestBodyOut instanceof RetryableOutputStream) {
68009f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson            int contentLength = ((RetryableOutputStream) requestBodyOut).contentLength();
68109f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson            requestHeaders.setContentLength(contentLength);
682953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
683953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
684ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson        return requestHeaders.getHeaders();
685953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
686953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
687953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
688953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * Populates requestHeaders with defaults and cookies.
689953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     *
690953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * <p>This client doesn't specify a default {@code Accept} header because it
691953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * doesn't know what content types the application is interested in.
692953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
693953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private void prepareRawRequestHeaders() throws IOException {
694ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson        requestHeaders.getHeaders().setStatusLine(getRequestLine());
695953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
696ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson        if (requestHeaders.getUserAgent() == null) {
69709f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson            requestHeaders.setUserAgent(getDefaultUserAgent());
698953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
699953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
700ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson        if (requestHeaders.getHost() == null) {
70109f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson            requestHeaders.setHost(getOriginAddress(policy.getURL()));
702953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
703953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
704ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson        if (httpMinorVersion > 0 && requestHeaders.getConnection() == null) {
70509f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson            requestHeaders.setConnection("Keep-Alive");
706953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
707953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
708ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson        if (requestHeaders.getAcceptEncoding() == null) {
709953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            transparentGzip = true;
71009f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson            requestHeaders.setAcceptEncoding("gzip");
7119531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        }
7129531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson
713ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson        if (hasRequestBody() && requestHeaders.getContentType() == null) {
71409f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson            requestHeaders.setContentType("application/x-www-form-urlencoded");
7159531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        }
7169531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson
7179531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        long ifModifiedSince = policy.getIfModifiedSince();
7189531eea15052eccc004b5f853ab4452becf7a8abJesse Wilson        if (ifModifiedSince != 0) {
71909f1b0cc860e039dff39e9429c1eaddcb6af598eJesse Wilson            requestHeaders.setIfModifiedSince(new Date(ifModifiedSince));
720953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
721953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
722953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        CookieHandler cookieHandler = CookieHandler.getDefault();
723953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (cookieHandler != null) {
724ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson            requestHeaders.addCookies(
725ed211506be4a0985678cf099282ed8fe851868fbJesse Wilson                    cookieHandler.get(uri, requestHeaders.getHeaders().toMultimap()));
726953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
727953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
728953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
729953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private String getRequestLine() {
730953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        String protocol = (httpMinorVersion == 0) ? "HTTP/1.0" : "HTTP/1.1";
731953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        return method + " " + requestString() + " " + protocol;
732953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
733953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
734953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    private String requestString() {
735953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        URL url = policy.getURL();
736953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (includeAuthorityInRequestLine()) {
737953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            return url.toString();
738953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        } else {
739953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            String fileOnly = url.getFile();
7405292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson            if (fileOnly == null) {
741953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                fileOnly = "/";
7425292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson            } else if (!fileOnly.startsWith("/")) {
7435292410e4ebf7fb5149eefd2f52fcb94c46690a6Jesse Wilson                fileOnly = "/" + fileOnly;
744953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            }
745953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            return fileOnly;
746953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
747953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
748953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
749953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
750953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * Returns true if the request line should contain the full URL with host
751953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * and port (like "GET http://android.com/foo HTTP/1.1") or only the path
752953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * (like "GET /foo HTTP/1.1").
753953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     *
754953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * <p>This is non-final because for HTTPS it's never necessary to supply the
755953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * full URL, even if a proxy is in use.
756953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
757953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    protected boolean includeAuthorityInRequestLine() {
758953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        return policy.usingProxy();
759953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
760953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
761fa5e8dfe3c7ed144b0fbe69091628dedd6a3b961Jesse Wilson    /**
762fa5e8dfe3c7ed144b0fbe69091628dedd6a3b961Jesse Wilson     * Returns the SSL configuration for connections created by this engine.
763fa5e8dfe3c7ed144b0fbe69091628dedd6a3b961Jesse Wilson     * We cannot reuse HTTPS connections if the socket factory has changed.
764fa5e8dfe3c7ed144b0fbe69091628dedd6a3b961Jesse Wilson     */
765fa5e8dfe3c7ed144b0fbe69091628dedd6a3b961Jesse Wilson    protected SSLSocketFactory getSslSocketFactory() {
766fa5e8dfe3c7ed144b0fbe69091628dedd6a3b961Jesse Wilson        return null;
767fa5e8dfe3c7ed144b0fbe69091628dedd6a3b961Jesse Wilson    }
768fa5e8dfe3c7ed144b0fbe69091628dedd6a3b961Jesse Wilson
769953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    protected final String getDefaultUserAgent() {
770953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        String agent = System.getProperty("http.agent");
771953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        return agent != null ? agent : ("Java" + System.getProperty("java.version"));
772953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
773953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
774953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    protected final String getOriginAddress(URL url) {
775953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        int port = url.getPort();
776953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        String result = url.getHost();
777953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (port > 0 && port != policy.getDefaultPort()) {
778953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            result = result + ":" + port;
779953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
780953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        return result;
781953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
782953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
783953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    protected boolean requiresTunnel() {
784953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        return false;
785953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
786953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
787953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    /**
788953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * Flushes the remaining request header and body, parses the HTTP response
789953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     * headers and starts reading the HTTP response body if it exists.
790953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson     */
791953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    public final void readResponse() throws IOException {
792953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (hasResponse()) {
793953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            return;
794953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
795953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
796953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (responseSource == null) {
797953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            throw new IllegalStateException("readResponse() without sendRequest()");
798953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
799953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
800953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (!responseSource.requiresConnection()) {
801953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            return;
802953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
803953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
804953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (sentRequestMillis == -1) {
805953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            int contentLength = requestBodyOut instanceof RetryableOutputStream
806953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                    ? ((RetryableOutputStream) requestBodyOut).contentLength()
807953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                    : -1;
808953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            writeRequestHeaders(contentLength);
809953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
810953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
811953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (requestBodyOut != null) {
812953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            requestBodyOut.close();
813953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            if (requestBodyOut instanceof RetryableOutputStream) {
814953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                ((RetryableOutputStream) requestBodyOut).writeToSocket(requestOut);
815953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            }
816953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
817953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
818953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        requestOut.flush();
819953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        requestOut = socketOut;
820953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
821953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        readResponseHeaders();
8227a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson        responseHeaders.setLocalTimestamps(sentRequestMillis, System.currentTimeMillis());
823953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
824953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
8257a891b5c83357dbeea2bbe4a5335ae685501e21cJesse Wilson            if (cachedResponseHeaders.validate(responseHeaders)) {
8265d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson                release(true);
8275d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson                ResponseHeaders combinedHeaders = cachedResponseHeaders.combine(responseHeaders);
8285d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson                setResponse(combinedHeaders, cachedResponseBody);
829e636ca4d6bd101324bd95fbc817401e6e0b80a2cJesse Wilson                if (responseCache instanceof ExtendedResponseCache) {
830e636ca4d6bd101324bd95fbc817401e6e0b80a2cJesse Wilson                    ExtendedResponseCache httpResponseCache = (ExtendedResponseCache) responseCache;
8315d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson                    httpResponseCache.trackConditionalCacheHit();
8325d7e0fc1af3141aa41e9c21d74da3c36b933517fJesse Wilson                    httpResponseCache.update(cacheResponse, getHttpConnectionToCache());
833ca8ae42fd6a787757897a680108bdcf7b0d37f13Jesse Wilson                }
834953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson                return;
835953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            } else {
836757afaa7afe96791a3cc612c9e3c4597a7321c7eJesse Wilson                IoUtils.closeQuietly(cachedResponseBody);
837953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            }
838953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
839953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
840953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        if (hasResponseBody()) {
841953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson            maybeCache(); // reentrant. this calls into user code which may call back into this!
842953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        }
843953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson
844953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson        initContentStream(getTransferStream());
845953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson    }
846953df613522e12a418cb7cb73248594d6c9f53d4Jesse Wilson}
847