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