HttpEngine.java revision 76739264ce52fe7a6c5c3558dad87b649118deff
1c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/* 2c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Licensed to the Apache Software Foundation (ASF) under one or more 3c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * contributor license agreements. See the NOTICE file distributed with 4c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * this work for additional information regarding copyright ownership. 5c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * The ASF licenses this file to You under the Apache License, Version 2.0 6c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * (the "License"); you may not use this file except in compliance with 7c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * the License. You may obtain a copy of the License at 8c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 9c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * http://www.apache.org/licenses/LICENSE-2.0 10c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 11c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Unless required by applicable law or agreed to in writing, software 12c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS, 13c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * See the License for the specific language governing permissions and 15c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * limitations under the License. 16c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 17c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 182231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonpackage com.squareup.okhttp.internal.http; 192231db3e6bb54447a9b14cf004a6cb03c373651cjwilson 202231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.Address; 212231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.Connection; 22a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport com.squareup.okhttp.OkHttpClient; 23a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport com.squareup.okhttp.OkResponseCache; 242231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.ResponseSource; 252231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.TunnelRequest; 2654cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport com.squareup.okhttp.internal.Dns; 272231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.internal.Platform; 282231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.internal.Util; 29c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.ByteArrayInputStream; 30c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.IOException; 31c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.InputStream; 32c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.OutputStream; 33c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CacheRequest; 34c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CacheResponse; 35c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CookieHandler; 36a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport java.net.HttpURLConnection; 37c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.Proxy; 38c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URI; 39c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URISyntaxException; 40c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URL; 412231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.UnknownHostException; 42c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Collections; 43c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Date; 44c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.HashMap; 45c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.List; 46c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Map; 47c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.zip.GZIPInputStream; 487899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport javax.net.ssl.HostnameVerifier; 49c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport javax.net.ssl.SSLSocketFactory; 50c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 5154cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport static com.squareup.okhttp.internal.Util.EMPTY_BYTE_ARRAY; 5254cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport static com.squareup.okhttp.internal.Util.getDefaultPort; 5354cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport static com.squareup.okhttp.internal.Util.getEffectivePort; 5454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 55c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/** 56c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Handles a single HTTP request/response pair. Each HTTP engine follows this 57c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * lifecycle: 58c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <ol> 5954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * <li>It is created. 6054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * <li>The HTTP request message is sent with sendRequest(). Once the request 6154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * is sent it is an error to modify the request headers. After 6254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * sendRequest() has been called the request body can be written to if 6354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * it exists. 6454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * <li>The HTTP response message is read with readResponse(). After the 6554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * response has been read the response headers and body can be read. 6654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * All responses have a response body input stream, though in some 6754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * instances this stream is empty. 68c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * </ol> 69c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 70c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>The request and response may be served by the HTTP response cache, by the 71c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * network, or by both in the event of a conditional GET. 72c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 73c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>This class may hold a socket connection that needs to be released or 74c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * recycled. By default, this socket connection is held when the last byte of 75c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * the response is consumed. To release the connection when it is no longer 76c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * required, use {@link #automaticallyReleaseConnectionToPool()}. 77c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 78c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathpublic class HttpEngine { 7954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private static final CacheResponse GATEWAY_TIMEOUT_RESPONSE = new CacheResponse() { 8054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson @Override public Map<String, List<String>> getHeaders() throws IOException { 8154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson Map<String, List<String>> result = new HashMap<String, List<String>>(); 8254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson result.put(null, Collections.singletonList("HTTP/1.1 504 Gateway Timeout")); 8354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return result; 84c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 8554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson @Override public InputStream getBody() throws IOException { 8654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return new ByteArrayInputStream(EMPTY_BYTE_ARRAY); 87c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 8854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson }; 8954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public static final int HTTP_CONTINUE = 100; 9054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 91a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath protected final Policy policy; 92a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath protected final OkHttpClient client; 9354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 9454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson protected final String method; 9554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 9654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private ResponseSource responseSource; 9754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 9854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson protected Connection connection; 9954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson protected RouteSelector routeSelector; 10054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private OutputStream requestBodyOut; 10154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 10254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private Transport transport; 10354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 10454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private InputStream responseTransferIn; 10554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private InputStream responseBodyIn; 10654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 10754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private CacheResponse cacheResponse; 10854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private CacheRequest cacheRequest; 10954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 11054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** The time when the request headers were written, or -1 if they haven't been written yet. */ 11154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson long sentRequestMillis = -1; 11254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 113415f345db25a1075e6103e55e7ea735ced0072b9Narayan Kamath /** Whether the connection has been established */ 114415f345db25a1075e6103e55e7ea735ced0072b9Narayan Kamath boolean connected; 115415f345db25a1075e6103e55e7ea735ced0072b9Narayan Kamath 11654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 11754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * True if this client added an "Accept-Encoding: gzip" header field and is 11854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * therefore responsible for also decompressing the transfer stream. 11954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 12054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private boolean transparentGzip; 12154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 12254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson final URI uri; 12354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 12454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson final RequestHeaders requestHeaders; 12554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 12654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** Null until a response is received from the network or the cache. */ 12754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson ResponseHeaders responseHeaders; 12854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 12954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // The cache response currently being validated on a conditional get. Null 13054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // if the cached response doesn't exist or doesn't need validation. If the 13154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // conditional get succeeds, these will be used for the response headers and 13254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // body. If it fails, these be closed and set to null. 13354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private ResponseHeaders cachedResponseHeaders; 13454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private InputStream cachedResponseBody; 13554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 13654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 13754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * True if the socket connection should be released to the connection pool 13854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * when the response has been fully read. 13954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 14054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private boolean automaticallyReleaseConnectionToPool; 14154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 14254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** True if the socket connection is no longer needed by this engine. */ 14354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private boolean connectionReleased; 14454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 14554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 14654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * @param requestHeaders the client's supplied request headers. This class 147a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * creates a private copy that it can mutate. 14854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * @param connection the connection used for an intermediate response 149a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * immediately prior to this request/response pair, such as a same-host 150a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * redirect. This engine assumes ownership of the connection and must 151a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * release it when it is unneeded. 15254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 153a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath public HttpEngine(OkHttpClient client, Policy policy, String method, RawHeaders requestHeaders, 15454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson Connection connection, RetryableOutputStream requestBodyOut) throws IOException { 155a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath this.client = client; 15654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.policy = policy; 15754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.method = method; 15854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.connection = connection; 15954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.requestBodyOut = requestBodyOut; 16054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 16154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson try { 16254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson uri = Platform.get().toUriLenient(policy.getURL()); 16354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } catch (URISyntaxException e) { 164faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath throw new IOException(e.getMessage()); 16554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 16654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 16754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.requestHeaders = new RequestHeaders(uri, new RawHeaders(requestHeaders)); 16854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 16954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 17054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public URI getUri() { 17154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return uri; 17254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 17354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 17454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 17554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Figures out what the response source will be, and opens a socket to that 17654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * source if necessary. Prepares the request headers and gets ready to start 17754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * writing the request body if it exists. 17854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 17954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final void sendRequest() throws IOException { 18054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (responseSource != null) { 18154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return; 18254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 18354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 18454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson prepareRawRequestHeaders(); 18554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson initResponseSource(); 186a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath OkResponseCache responseCache = client.getOkResponseCache(); 187a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath if (responseCache != null) { 188a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath responseCache.trackResponse(responseSource); 18954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 19054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 19154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // The raw response source may require the network, but the request 19254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // headers may forbid network use. In that case, dispose of the network 19354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // response and use a GATEWAY_TIMEOUT response instead, as specified 19454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // by http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4. 19554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (requestHeaders.isOnlyIfCached() && responseSource.requiresConnection()) { 19654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (responseSource == ResponseSource.CONDITIONAL_CACHE) { 19754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson Util.closeQuietly(cachedResponseBody); 19854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 19954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.responseSource = ResponseSource.CACHE; 20054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.cacheResponse = GATEWAY_TIMEOUT_RESPONSE; 20154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson RawHeaders rawResponseHeaders = RawHeaders.fromMultimap(cacheResponse.getHeaders(), true); 20254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson setResponse(new ResponseHeaders(uri, rawResponseHeaders), cacheResponse.getBody()); 20354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 20454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 20554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (responseSource.requiresConnection()) { 20654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson sendSocketRequest(); 20754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else if (connection != null) { 208a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath client.getConnectionPool().recycle(connection); 20954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson connection = null; 21054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 21154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 21254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 21354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 21454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Initialize the source for this response. It may be corrected later if the 21554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * request headers forbids network use. 21654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 21754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private void initResponseSource() throws IOException { 21854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson responseSource = ResponseSource.NETWORK; 219a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath if (!policy.getUseCaches()) return; 22054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 221a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath OkResponseCache responseCache = client.getOkResponseCache(); 222a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath if (responseCache == null) return; 223a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath 224a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath CacheResponse candidate = responseCache.get( 225a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath uri, method, requestHeaders.getHeaders().toMultimap(false)); 226a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath if (candidate == null) return; 22754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 22854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson Map<String, List<String>> responseHeadersMap = candidate.getHeaders(); 22954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson cachedResponseBody = candidate.getBody(); 23054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (!acceptCacheResponseType(candidate) 23154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson || responseHeadersMap == null 23254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson || cachedResponseBody == null) { 23354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson Util.closeQuietly(cachedResponseBody); 23454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return; 23554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 23654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 23754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson RawHeaders rawResponseHeaders = RawHeaders.fromMultimap(responseHeadersMap, true); 23854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson cachedResponseHeaders = new ResponseHeaders(uri, rawResponseHeaders); 23954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson long now = System.currentTimeMillis(); 24054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.responseSource = cachedResponseHeaders.chooseResponseSource(now, requestHeaders); 24154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (responseSource == ResponseSource.CACHE) { 24254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.cacheResponse = candidate; 24354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson setResponse(cachedResponseHeaders, cachedResponseBody); 24454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else if (responseSource == ResponseSource.CONDITIONAL_CACHE) { 24554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.cacheResponse = candidate; 24654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else if (responseSource == ResponseSource.NETWORK) { 24754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson Util.closeQuietly(cachedResponseBody); 24854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else { 24954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new AssertionError(); 25054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 25154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 25254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 25354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private void sendSocketRequest() throws IOException { 25454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (connection == null) { 25554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson connect(); 25654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 25754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 25854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (transport != null) { 25954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new IllegalStateException(); 26054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 26154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 26254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson transport = (Transport) connection.newTransport(this); 26354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 26454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (hasRequestBody() && requestBodyOut == null) { 26554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // Create a request body if we don't have one already. We'll already 26654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // have one if we're retrying a failed POST. 26754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson requestBodyOut = transport.createRequestBody(); 26854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 26954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 27054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 27154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** Connect to the origin server either directly or via a proxy. */ 27254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson protected final void connect() throws IOException { 27354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (connection != null) { 27454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return; 27554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 27654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (routeSelector == null) { 27754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson String uriHost = uri.getHost(); 27854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (uriHost == null) { 27954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new UnknownHostException(uri.toString()); 28054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 2817407d6984ce69693097befc9b72609a8156463bbNarayan Kamath SSLSocketFactory sslSocketFactory = null; 2827407d6984ce69693097befc9b72609a8156463bbNarayan Kamath HostnameVerifier hostnameVerifier = null; 2837407d6984ce69693097befc9b72609a8156463bbNarayan Kamath if (uri.getScheme().equalsIgnoreCase("https")) { 284a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath sslSocketFactory = client.getSslSocketFactory(); 285a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath hostnameVerifier = client.getHostnameVerifier(); 2867407d6984ce69693097befc9b72609a8156463bbNarayan Kamath } 2877407d6984ce69693097befc9b72609a8156463bbNarayan Kamath Address address = new Address(uriHost, getEffectivePort(uri), sslSocketFactory, 288a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath hostnameVerifier, client.getAuthenticator(), client.getProxy(), client.getTransports()); 289a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath routeSelector = new RouteSelector(address, uri, client.getProxySelector(), 290a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath client.getConnectionPool(), Dns.DEFAULT, client.getRoutesDatabase()); 29154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 292fb0eb65be9f50e75fa37c250e97914252c587cafjwilson connection = routeSelector.next(method); 29354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (!connection.isConnected()) { 294a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath connection.connect(client.getConnectTimeout(), client.getReadTimeout(), getTunnelConfig()); 295a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath client.getConnectionPool().maybeShare(connection); 296a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath client.getRoutesDatabase().connected(connection.getRoute()); 29776739264ce52fe7a6c5c3558dad87b649118deffNarayan Kamath } else { 29876739264ce52fe7a6c5c3558dad87b649118deffNarayan Kamath connection.updateReadTimeout(client.getReadTimeout()); 29954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 30054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson connected(connection); 301a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath if (connection.getRoute().getProxy() != client.getProxy()) { 3027407d6984ce69693097befc9b72609a8156463bbNarayan Kamath // Update the request line if the proxy changed; it may need a host name. 30354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson requestHeaders.getHeaders().setRequestLine(getRequestLine()); 30454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 30554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 30654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 30754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 30854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Called after a socket connection has been created or retrieved from the 30954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * pool. Subclasses use this hook to get a reference to the TLS data. 31054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 31154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson protected void connected(Connection connection) { 312415f345db25a1075e6103e55e7ea735ced0072b9Narayan Kamath connected = true; 31354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 31454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 31554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 31654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Called immediately before the transport transmits HTTP request headers. 31754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * This is used to observe the sent time should the request be cached. 31854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 31954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public void writingRequestHeaders() { 32054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (sentRequestMillis != -1) { 32154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new IllegalStateException(); 32254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 32354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson sentRequestMillis = System.currentTimeMillis(); 32454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 32554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 32654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 32754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * @param body the response body, or null if it doesn't exist or isn't 32854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * available. 32954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 33054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private void setResponse(ResponseHeaders headers, InputStream body) throws IOException { 33154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (this.responseBodyIn != null) { 33254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new IllegalStateException(); 33354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 33454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.responseHeaders = headers; 33554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (body != null) { 33654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson initContentStream(body); 33754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 33854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 33954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 34054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson boolean hasRequestBody() { 34154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return method.equals("POST") || method.equals("PUT"); 34254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 34354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 34454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** Returns the request body or null if this request doesn't have a body. */ 34554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final OutputStream getRequestBody() { 34654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (responseSource == null) { 34754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new IllegalStateException(); 34854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 34954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return requestBodyOut; 35054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 35154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 35254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final boolean hasResponse() { 35354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return responseHeaders != null; 35454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 35554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 35654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final RequestHeaders getRequestHeaders() { 35754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return requestHeaders; 35854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 35954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 36054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final ResponseHeaders getResponseHeaders() { 36154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (responseHeaders == null) { 36254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new IllegalStateException(); 36354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 36454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return responseHeaders; 36554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 36654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 36754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final int getResponseCode() { 36854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (responseHeaders == null) { 36954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new IllegalStateException(); 37054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 37154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return responseHeaders.getHeaders().getResponseCode(); 37254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 37354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 37454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final InputStream getResponseBody() { 37554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (responseHeaders == null) { 37654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new IllegalStateException(); 37754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 37854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return responseBodyIn; 37954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 38054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 38154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final CacheResponse getCacheResponse() { 38254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return cacheResponse; 38354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 38454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 38554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final Connection getConnection() { 38654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return connection; 38754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 38854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 38954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 39054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Returns true if {@code cacheResponse} is of the right type. This 39154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * condition is necessary but not sufficient for the cached response to 39254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * be used. 39354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 39454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson protected boolean acceptCacheResponseType(CacheResponse cacheResponse) { 39554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return true; 39654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 39754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 39854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private void maybeCache() throws IOException { 39954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // Are we caching at all? 400a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath if (!policy.getUseCaches()) return; 401a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath OkResponseCache responseCache = client.getOkResponseCache(); 402a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath if (responseCache == null) return; 403a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath 404a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath HttpURLConnection connectionToCache = policy.getHttpConnectionToCache(); 40554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 40654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // Should we cache this response for this request? 40754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (!responseHeaders.isCacheable(requestHeaders)) { 408a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath responseCache.maybeRemove(connectionToCache.getRequestMethod(), uri); 40954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return; 41054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 41154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 41254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // Offer this request to the cache. 413a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath cacheRequest = responseCache.put(uri, connectionToCache); 41454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 41554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 41654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 41754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Cause the socket connection to be released to the connection pool when 41854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * it is no longer needed. If it is already unneeded, it will be pooled 41954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * immediately. Otherwise the connection is held so that redirects can be 42054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * handled by the same connection. 42154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 42254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final void automaticallyReleaseConnectionToPool() { 42354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson automaticallyReleaseConnectionToPool = true; 42454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (connection != null && connectionReleased) { 425a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath client.getConnectionPool().recycle(connection); 42654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson connection = null; 42754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 42854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 42954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 43054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 43154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Releases this engine so that its resources may be either reused or 43254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * closed. Also call {@link #automaticallyReleaseConnectionToPool} unless 43354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * the connection will be used to follow a redirect. 43454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 43554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final void release(boolean streamCancelled) { 43654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // If the response body comes from the cache, close it. 43754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (responseBodyIn == cachedResponseBody) { 43854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson Util.closeQuietly(responseBodyIn); 439c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 440c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 44154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (!connectionReleased && connection != null) { 44254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson connectionReleased = true; 443c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 44454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (transport == null || !transport.makeReusable(streamCancelled, requestBodyOut, 44554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson responseTransferIn)) { 44654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson Util.closeQuietly(connection); 44754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson connection = null; 44854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else if (automaticallyReleaseConnectionToPool) { 449a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath client.getConnectionPool().recycle(connection); 45054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson connection = null; 45154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 45254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 45354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 454c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 45554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private void initContentStream(InputStream transferStream) throws IOException { 45654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson responseTransferIn = transferStream; 45754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (transparentGzip && responseHeaders.isContentEncodingGzip()) { 45854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // If the response was transparently gzipped, remove the gzip header field 45954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // so clients don't double decompress. http://b/3009828 46054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // 46154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // Also remove the Content-Length in this case because it contains the 46254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // length 528 of the gzipped response. This isn't terribly useful and is 46354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // dangerous because 529 clients can query the content length, but not 46454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // the content encoding. 46554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson responseHeaders.stripContentEncoding(); 46654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson responseHeaders.stripContentLength(); 46754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson responseBodyIn = new GZIPInputStream(transferStream); 46854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else { 46954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson responseBodyIn = transferStream; 47054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 47154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 47254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 47354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 47454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Returns true if the response must have a (possibly 0-length) body. 47554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * See RFC 2616 section 4.3. 47654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 47754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final boolean hasResponseBody() { 47854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson int responseCode = responseHeaders.getHeaders().getResponseCode(); 47954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 48054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // HEAD requests never yield a body regardless of the response headers. 48154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (method.equals("HEAD")) { 48254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return false; 48354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 48454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 48554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if ((responseCode < HTTP_CONTINUE || responseCode >= 200) 48654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson && responseCode != HttpURLConnectionImpl.HTTP_NO_CONTENT 48754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson && responseCode != HttpURLConnectionImpl.HTTP_NOT_MODIFIED) { 48854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return true; 48954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 49054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 49154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // If the Content-Length or Transfer-Encoding headers disagree with the 49254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // response code, the response is malformed. For best compatibility, we 49354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // honor the headers. 49454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (responseHeaders.getContentLength() != -1 || responseHeaders.isChunked()) { 49554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return true; 49654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 49754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 49854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return false; 49954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 50054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 50154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 50254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Populates requestHeaders with defaults and cookies. 50354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * 50454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * <p>This client doesn't specify a default {@code Accept} header because it 50554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * doesn't know what content types the application is interested in. 50654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 50754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private void prepareRawRequestHeaders() throws IOException { 50854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson requestHeaders.getHeaders().setRequestLine(getRequestLine()); 50954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 51054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (requestHeaders.getUserAgent() == null) { 51154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson requestHeaders.setUserAgent(getDefaultUserAgent()); 51254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 51354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 51454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (requestHeaders.getHost() == null) { 51554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson requestHeaders.setHost(getOriginAddress(policy.getURL())); 51654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 51754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 51854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if ((connection == null || connection.getHttpMinorVersion() != 0) 51954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson && requestHeaders.getConnection() == null) { 52054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson requestHeaders.setConnection("Keep-Alive"); 52154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 52254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 52354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (requestHeaders.getAcceptEncoding() == null) { 52454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson transparentGzip = true; 52554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson requestHeaders.setAcceptEncoding("gzip"); 52654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 52754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 52854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (hasRequestBody() && requestHeaders.getContentType() == null) { 52954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson requestHeaders.setContentType("application/x-www-form-urlencoded"); 53054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 53154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 53254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson long ifModifiedSince = policy.getIfModifiedSince(); 53354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (ifModifiedSince != 0) { 53454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson requestHeaders.setIfModifiedSince(new Date(ifModifiedSince)); 53554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 53654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 537a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath CookieHandler cookieHandler = client.getCookieHandler(); 53854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (cookieHandler != null) { 53954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson requestHeaders.addCookies( 54054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson cookieHandler.get(uri, requestHeaders.getHeaders().toMultimap(false))); 54154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 54254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 54354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 54454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 54554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Returns the request status line, like "GET / HTTP/1.1". This is exposed 54654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * to the application by {@link HttpURLConnectionImpl#getHeaderFields}, so 54754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * it needs to be set even if the transport is SPDY. 54854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 54954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson String getRequestLine() { 55054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson String protocol = 55154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson (connection == null || connection.getHttpMinorVersion() != 0) ? "HTTP/1.1" : "HTTP/1.0"; 55254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return method + " " + requestString() + " " + protocol; 55354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 55454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 55554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private String requestString() { 55654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson URL url = policy.getURL(); 55754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (includeAuthorityInRequestLine()) { 55854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return url.toString(); 55954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else { 56054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return requestPath(url); 56154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 56254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 56354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 56454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 56554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Returns the path to request, like the '/' in 'GET / HTTP/1.1'. Never 56654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * empty, even if the request URL is. Includes the query component if it 56754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * exists. 56854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 56954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public static String requestPath(URL url) { 57054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson String fileOnly = url.getFile(); 57154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (fileOnly == null) { 57254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return "/"; 57354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else if (!fileOnly.startsWith("/")) { 57454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return "/" + fileOnly; 57554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else { 57654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return fileOnly; 577c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 57854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 579c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 58054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 58154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Returns true if the request line should contain the full URL with host 58254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * and port (like "GET http://android.com/foo HTTP/1.1") or only the path 58354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * (like "GET /foo HTTP/1.1"). 58454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * 58554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * <p>This is non-final because for HTTPS it's never necessary to supply the 58654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * full URL, even if a proxy is in use. 58754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 58854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson protected boolean includeAuthorityInRequestLine() { 5897407d6984ce69693097befc9b72609a8156463bbNarayan Kamath return connection == null 5907407d6984ce69693097befc9b72609a8156463bbNarayan Kamath ? policy.usingProxy() // A proxy was requested. 591faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath : connection.getRoute().getProxy().type() == Proxy.Type.HTTP; // A proxy was selected. 59254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 59354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 59454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public static String getDefaultUserAgent() { 59554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson String agent = System.getProperty("http.agent"); 59654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return agent != null ? agent : ("Java" + System.getProperty("java.version")); 59754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 59854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 59954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public static String getOriginAddress(URL url) { 60054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson int port = url.getPort(); 60154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson String result = url.getHost(); 60254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (port > 0 && port != getDefaultPort(url.getProtocol())) { 60354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson result = result + ":" + port; 60454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 60554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return result; 60654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 60754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 60854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 60954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Flushes the remaining request header and body, parses the HTTP response 61054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * headers and starts reading the HTTP response body if it exists. 61154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 61254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final void readResponse() throws IOException { 61354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (hasResponse()) { 614faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath responseHeaders.setResponseSource(responseSource); 61554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return; 61654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 61754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 61854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (responseSource == null) { 61954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson throw new IllegalStateException("readResponse() without sendRequest()"); 62054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 62154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 62254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (!responseSource.requiresConnection()) { 62354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return; 62454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 62554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 62654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (sentRequestMillis == -1) { 62754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (requestBodyOut instanceof RetryableOutputStream) { 62854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson int contentLength = ((RetryableOutputStream) requestBodyOut).contentLength(); 62954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson requestHeaders.setContentLength(contentLength); 63054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 63154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson transport.writeRequestHeaders(); 63254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 63354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 63454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (requestBodyOut != null) { 63554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson requestBodyOut.close(); 63654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (requestBodyOut instanceof RetryableOutputStream) { 63754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson transport.writeRequestBody((RetryableOutputStream) requestBodyOut); 63854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 63954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 64054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 64154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson transport.flushRequest(); 64254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 64354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson responseHeaders = transport.readResponseHeaders(); 64454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson responseHeaders.setLocalTimestamps(sentRequestMillis, System.currentTimeMillis()); 645faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath responseHeaders.setResponseSource(responseSource); 64654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 64754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (responseSource == ResponseSource.CONDITIONAL_CACHE) { 64854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (cachedResponseHeaders.validate(responseHeaders)) { 64954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson release(false); 65054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson ResponseHeaders combinedHeaders = cachedResponseHeaders.combine(responseHeaders); 65154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson setResponse(combinedHeaders, cachedResponseBody); 652a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath OkResponseCache responseCache = client.getOkResponseCache(); 653a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath responseCache.trackConditionalCacheHit(); 654a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath responseCache.update(cacheResponse, policy.getHttpConnectionToCache()); 65554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return; 65654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else { 65754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson Util.closeQuietly(cachedResponseBody); 65854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 659c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 660c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 66154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (hasResponseBody()) { 66254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson maybeCache(); // reentrant. this calls into user code which may call back into this! 663c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 664c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 66554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson initContentStream(transport.getTransferStream(cacheRequest)); 66654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 667c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 66854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson protected TunnelRequest getTunnelConfig() { 66954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return null; 67054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 6717899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath 67254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public void receiveHeaders(RawHeaders headers) throws IOException { 673a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath CookieHandler cookieHandler = client.getCookieHandler(); 67454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (cookieHandler != null) { 67554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson cookieHandler.put(uri, headers.toMultimap(true)); 6767899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath } 67754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 678c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath} 679