HttpEngine.java revision 78092f38ebd93018ead53a87b53118dc829cbb8a
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; 223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Headers; 23a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport com.squareup.okhttp.OkHttpClient; 24a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport com.squareup.okhttp.OkResponseCache; 253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Request; 263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Response; 272231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.ResponseSource; 283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Route; 292231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.TunnelRequest; 3054cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport com.squareup.okhttp.internal.Dns; 31c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.IOException; 32c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.InputStream; 33c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CacheRequest; 34c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CookieHandler; 353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.net.ProtocolException; 36c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URL; 372231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.UnknownHostException; 383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.security.cert.CertificateException; 39c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.List; 40c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Map; 417899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport javax.net.ssl.HostnameVerifier; 423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport javax.net.ssl.SSLHandshakeException; 43c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport javax.net.ssl.SSLSocketFactory; 443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.BufferedSink; 453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.GzipSource; 463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Okio; 473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Sink; 483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Source; 49c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.Util.closeQuietly; 5154cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport static com.squareup.okhttp.internal.Util.getDefaultPort; 5254cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport static com.squareup.okhttp.internal.Util.getEffectivePort; 533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.http.StatusLine.HTTP_CONTINUE; 543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static java.net.HttpURLConnection.HTTP_NOT_MODIFIED; 553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static java.net.HttpURLConnection.HTTP_NO_CONTENT; 5654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 57c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/** 58c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Handles a single HTTP request/response pair. Each HTTP engine follows this 59c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * lifecycle: 60c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <ol> 6154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * <li>It is created. 6254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * <li>The HTTP request message is sent with sendRequest(). Once the request 6354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * is sent it is an error to modify the request headers. After 6454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * sendRequest() has been called the request body can be written to if 6554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * it exists. 6654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * <li>The HTTP response message is read with readResponse(). After the 6754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * response has been read the response headers and body can be read. 6854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * All responses have a response body input stream, though in some 6954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * instances this stream is empty. 70c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * </ol> 71c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 72c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>The request and response may be served by the HTTP response cache, by the 73c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * network, or by both in the event of a conditional GET. 74c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 75c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathpublic class HttpEngine { 763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller final OkHttpClient client; 7754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private Connection connection; 793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private RouteSelector routeSelector; 803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private Route route; 8154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 8254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private Transport transport; 8354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 8454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** The time when the request headers were written, or -1 if they haven't been written yet. */ 8554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson long sentRequestMillis = -1; 8654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 8754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 8854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * True if this client added an "Accept-Encoding: gzip" header field and is 8954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * therefore responsible for also decompressing the transfer stream. 9054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 9154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private boolean transparentGzip; 9254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * True if the request body must be completely buffered before transmission; 953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * false if it can be streamed. Buffering has two advantages: we don't need 963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * the content-length in advance and we can retransmit if necessary. The 973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * upside of streaming is that we can save memory. 983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public final boolean bufferRequestBody; 10054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 101c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller private Request originalRequest; 1023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private Request request; 1033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private Sink requestBodyOut; 1043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private BufferedSink bufferedRequestBody; 10554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 1063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private ResponseSource responseSource; 10754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 1083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** Null until a response is received from the network or the cache. */ 1093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private Response response; 1103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private Source responseTransferSource; 1113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private Source responseBody; 1123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private InputStream responseBodyBytes; 11354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 11454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 1153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * The cache response currently being validated on a conditional get. Null 1163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * if the cached response doesn't exist or doesn't need validation. If the 1173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * conditional get succeeds, these will be used for the response. If it fails, 1183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * it will be set to null. 11954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 1203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private Response validatingResponse; 12154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 1223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** The cache request currently being populated from a network response. */ 1233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private CacheRequest cacheRequest; 12454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 12554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 1263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * @param request the HTTP request without a body. The body must be 1273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * written via the engine's request body stream. 12854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * @param connection the connection used for an intermediate response 129a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * immediately prior to this request/response pair, such as a same-host 130a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * redirect. This engine assumes ownership of the connection and must 131a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * release it when it is unneeded. 1323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * @param routeSelector the route selector used for a failed attempt 1333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * immediately preceding this attempt, or null if this request doesn't 1343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * recover from a failure. 13554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 1363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public HttpEngine(OkHttpClient client, Request request, boolean bufferRequestBody, 1373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Connection connection, RouteSelector routeSelector, RetryableSink requestBodyOut) { 138a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath this.client = client; 139c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller this.originalRequest = request; 1403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller this.request = request; 1413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller this.bufferRequestBody = bufferRequestBody; 14254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.connection = connection; 1433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller this.routeSelector = routeSelector; 14454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.requestBodyOut = requestBodyOut; 14578092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller 14678092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller if (connection != null) { 14778092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller connection.setOwner(this); 14878092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller this.route = connection.getRoute(); 14978092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller } else { 15078092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller this.route = null; 15178092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller } 15254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 15354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 15454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 15554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Figures out what the response source will be, and opens a socket to that 15654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * source if necessary. Prepares the request headers and gets ready to start 15754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * writing the request body if it exists. 15854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 15954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final void sendRequest() throws IOException { 1603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (responseSource != null) return; // Already sent. 1613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (transport != null) throw new IllegalStateException(); 16254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 16354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson prepareRawRequestHeaders(); 164a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath OkResponseCache responseCache = client.getOkResponseCache(); 1653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Response cacheResponse = responseCache != null 1673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ? responseCache.get(request) 1683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller : null; 1693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller long now = System.currentTimeMillis(); 1703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller CacheStrategy cacheStrategy = new CacheStrategy.Factory(now, request, cacheResponse).get(); 1713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller responseSource = cacheStrategy.source; 1723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller request = cacheStrategy.request; 1733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 174a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath if (responseCache != null) { 175a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath responseCache.trackResponse(responseSource); 17654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 17754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 1783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (responseSource != ResponseSource.NETWORK) { 1793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller validatingResponse = cacheStrategy.response; 18054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 18154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 1823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (cacheResponse != null && !responseSource.usesCache()) { 1833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closeQuietly(cacheResponse.body()); // We don't need this cached response. Close it. 18454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 18554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 1863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (responseSource.requiresConnection()) { 1873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Open a connection unless we inherited one from a redirect. 1883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (connection == null) { 1893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connect(); 1903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 191a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath 19278092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller // Blow up if we aren't the current owner of the connection. 19378092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller if (connection.getOwner() != this && !connection.isSpdy()) throw new AssertionError(); 19478092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller 1953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller transport = (Transport) connection.newTransport(this); 19654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 1973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Create a request body if we don't have one already. We'll already have 1983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // one if we're retrying a failed POST. 1993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (hasRequestBody() && requestBodyOut == null) { 2003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller requestBodyOut = transport.createRequestBody(request); 2013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 20254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 20354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else { 2043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // We're using a cached response. Recycle a connection we may have inherited from a redirect. 2053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (connection != null) { 2063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller client.getConnectionPool().recycle(connection); 2073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection = null; 2083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 20954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 2103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // No need for the network! Promote the cached response immediately. 2113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller this.response = validatingResponse; 2123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (validatingResponse.body() != null) { 2133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller initContentStream(validatingResponse.body().source()); 2143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 21554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 2163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 21754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 2183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private Response cacheableResponse() { 2193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Use an unreadable response body when offering the response to the cache. 2203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // The cache isn't allowed to consume the response body bytes! 2213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return response.newBuilder().body(null).build(); 22254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 22354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 22454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** Connect to the origin server either directly or via a proxy. */ 2253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private void connect() throws IOException { 2263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (connection != null) throw new IllegalStateException(); 2273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 22854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (routeSelector == null) { 2293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller String uriHost = request.url().getHost(); 2303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (uriHost == null || uriHost.length() == 0) { 2313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller throw new UnknownHostException(request.url().toString()); 23254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 2337407d6984ce69693097befc9b72609a8156463bbNarayan Kamath SSLSocketFactory sslSocketFactory = null; 2347407d6984ce69693097befc9b72609a8156463bbNarayan Kamath HostnameVerifier hostnameVerifier = null; 2353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (request.isHttps()) { 236a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath sslSocketFactory = client.getSslSocketFactory(); 237a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath hostnameVerifier = client.getHostnameVerifier(); 2387407d6984ce69693097befc9b72609a8156463bbNarayan Kamath } 2393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Address address = new Address(uriHost, getEffectivePort(request.url()), sslSocketFactory, 2403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller hostnameVerifier, client.getAuthenticator(), client.getProxy(), client.getProtocols()); 2413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller routeSelector = new RouteSelector(address, request.uri(), client.getProxySelector(), 242a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath client.getConnectionPool(), Dns.DEFAULT, client.getRoutesDatabase()); 24354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 2443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection = routeSelector.next(request.method()); 24678092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller connection.setOwner(this); 2473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 24854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (!connection.isConnected()) { 249a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath connection.connect(client.getConnectTimeout(), client.getReadTimeout(), getTunnelConfig()); 2503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (connection.isSpdy()) client.getConnectionPool().share(connection); 251a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath client.getRoutesDatabase().connected(connection.getRoute()); 2523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } else if (!connection.isSpdy()) { 25376739264ce52fe7a6c5c3558dad87b649118deffNarayan Kamath connection.updateReadTimeout(client.getReadTimeout()); 25454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 25554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 2563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller route = connection.getRoute(); 25754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 25854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 25954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 26054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Called immediately before the transport transmits HTTP request headers. 26154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * This is used to observe the sent time should the request be cached. 26254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 26354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public void writingRequestHeaders() { 2643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (sentRequestMillis != -1) throw new IllegalStateException(); 26554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson sentRequestMillis = System.currentTimeMillis(); 26654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 26754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 26854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson boolean hasRequestBody() { 2693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return HttpMethod.hasRequestBody(request.method()); 27054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 27154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 27254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** Returns the request body or null if this request doesn't have a body. */ 2733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public final Sink getRequestBody() { 2743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (responseSource == null) throw new IllegalStateException(); 27554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return requestBodyOut; 27654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 27754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 2783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public final BufferedSink getBufferedRequestBody() { 2793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink result = bufferedRequestBody; 2803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (result != null) return result; 2813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Sink requestBody = getRequestBody(); 2823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return requestBody != null 2833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ? (bufferedRequestBody = Okio.buffer(requestBody)) 2843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller : null; 2853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 28754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final boolean hasResponse() { 2883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return response != null; 28954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 29054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 2913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public final ResponseSource responseSource() { 2923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return responseSource; 29354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 29454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 2953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public final Request getRequest() { 2963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return request; 29754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 29854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 2993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** Returns the engine's response. */ 3003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // TODO: the returned body will always be null. 3013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public final Response getResponse() { 3023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (response == null) throw new IllegalStateException(); 3033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return response; 30454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 30554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 3063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public final Source getResponseBody() { 3073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (response == null) throw new IllegalStateException(); 3083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return responseBody; 30954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 31054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 3113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public final InputStream getResponseBodyBytes() { 3123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller InputStream result = responseBodyBytes; 3133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return result != null 3143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ? result 3153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller : (responseBodyBytes = Okio.buffer(getResponseBody()).inputStream()); 31654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 31754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 31854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final Connection getConnection() { 31954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return connection; 32054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 32154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 32254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 3233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Report and attempt to recover from {@code e}. Returns a new HTTP engine 3243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * that should be used for the retry if {@code e} is recoverable, or null if 3253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * the failure is permanent. 32654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 3273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public HttpEngine recover(IOException e) { 3283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (routeSelector != null && connection != null) { 3293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller routeSelector.connectFailed(connection, e); 3303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller boolean canRetryRequestBody = requestBodyOut == null || requestBodyOut instanceof RetryableSink; 3333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (routeSelector == null && connection == null // No connection. 3343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller || routeSelector != null && !routeSelector.hasNext() // No more routes to attempt. 3353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller || !isRecoverable(e) 3363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller || !canRetryRequestBody) { 3373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return null; 3383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Connection connection = close(); 3413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // For failure recovery, use the same route selector with a new connection. 343c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller return new HttpEngine(client, originalRequest, bufferRequestBody, connection, routeSelector, 3443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller (RetryableSink) requestBodyOut); 3453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private boolean isRecoverable(IOException e) { 3483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // If the problem was a CertificateException from the X509TrustManager, 3493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // do not retry, we didn't have an abrupt server-initiated exception. 3503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller boolean sslFailure = 3513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller e instanceof SSLHandshakeException && e.getCause() instanceof CertificateException; 3523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller boolean protocolFailure = e instanceof ProtocolException; 3533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return !sslFailure && !protocolFailure; 3543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 3573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Returns the route used to retrieve the response. Null if we haven't 3583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * connected yet, or if no connection was necessary. 3593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 3603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public Route getRoute() { 3613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return route; 36254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 36354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 36454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private void maybeCache() throws IOException { 365a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath OkResponseCache responseCache = client.getOkResponseCache(); 366a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath if (responseCache == null) return; 367a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath 36854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // Should we cache this response for this request? 3693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (!CacheStrategy.isCacheable(response, request)) { 3703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller responseCache.maybeRemove(request); 37154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return; 37254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 37354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 37454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // Offer this request to the cache. 3753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller cacheRequest = responseCache.put(cacheableResponse()); 37654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 37754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 37854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 3793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Configure the socket connection to be either pooled or closed when it is 3803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * either exhausted or closed. If it is unneeded when this is called, it will 3813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * be released immediately. 38254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 3833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public final void releaseConnection() throws IOException { 384c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller if (transport != null && connection != null) { 3853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller transport.releaseConnectionOnIdle(); 38654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 3873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection = null; 38854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 38954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 39054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 3913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Release any resources held by this engine. If a connection is still held by 3923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * this engine, it is returned. 39354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 3943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public final Connection close() { 3953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (bufferedRequestBody != null) { 3963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // This also closes the wrapped requestBodyOut. 3973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closeQuietly(bufferedRequestBody); 3983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } else if (requestBodyOut != null) { 3993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closeQuietly(requestBodyOut); 400c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 401c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 4023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // If this engine never achieved a response body, its connection cannot be reused. 4033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (responseBody == null) { 4043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closeQuietly(connection); 4053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection = null; 4063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return null; 4073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 408c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 4093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Close the response body. This will recycle the connection if it is eligible. 4103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closeQuietly(responseBody); 4113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Clear the buffer held by the response body input stream adapter. 4133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closeQuietly(responseBodyBytes); 4143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Close the connection if it cannot be reused. 4163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (transport != null && !transport.canReuseConnection()) { 4173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closeQuietly(connection); 4183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection = null; 4193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return null; 42054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 4213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 42278092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller // Prevent this engine from disconnecting a connection it no longer owns. 42378092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller if (connection != null && !connection.clearOwner()) { 42478092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller connection = null; 42578092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller } 42678092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller 4273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Connection result = connection; 4283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection = null; 4293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return result; 43054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 431c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 4323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 4333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Initialize the response content stream from the response transfer source. 4343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * These two sources are the same unless we're doing transparent gzip, in 4353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * which case the content source is decompressed. 4363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * 4373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * <p>Whenever we do transparent gzip we also strip the corresponding headers. 4383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * We strip the Content-Encoding header to prevent the application from 4393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * attempting to double decompress. We strip the Content-Length header because 4403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * it is the length of the compressed content, but the application is only 4413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * interested in the length of the uncompressed content. 4423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * 4433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * <p>This method should only be used for non-empty response bodies. Response 4443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * codes like "304 Not Modified" can include "Content-Encoding: gzip" without 4453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * a response body and we will crash if we attempt to decompress the zero-byte 4463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * source. 4473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 4483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private void initContentStream(Source transferSource) throws IOException { 4493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller responseTransferSource = transferSource; 4503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (transparentGzip && "gzip".equalsIgnoreCase(response.header("Content-Encoding"))) { 4513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller response = response.newBuilder() 4523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .removeHeader("Content-Encoding") 4533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .removeHeader("Content-Length") 4543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .build(); 4553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller responseBody = new GzipSource(transferSource); 45654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else { 4573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller responseBody = transferSource; 45854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 45954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 46054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 46154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 46254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Returns true if the response must have a (possibly 0-length) body. 46354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * See RFC 2616 section 4.3. 46454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 46554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final boolean hasResponseBody() { 46654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // HEAD requests never yield a body regardless of the response headers. 4673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (request.method().equals("HEAD")) { 46854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return false; 46954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 47054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 4713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller int responseCode = response.code(); 47254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if ((responseCode < HTTP_CONTINUE || responseCode >= 200) 4733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && responseCode != HTTP_NO_CONTENT 4743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && responseCode != HTTP_NOT_MODIFIED) { 47554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return true; 47654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 47754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 47854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // If the Content-Length or Transfer-Encoding headers disagree with the 47954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // response code, the response is malformed. For best compatibility, we 48054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // honor the headers. 4813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (OkHeaders.contentLength(response) != -1 4823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller || "chunked".equalsIgnoreCase(response.header("Transfer-Encoding"))) { 48354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return true; 48454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 48554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 48654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return false; 48754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 48854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 48954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 4903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Populates request with defaults and cookies. 49154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * 49254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * <p>This client doesn't specify a default {@code Accept} header because it 49354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * doesn't know what content types the application is interested in. 49454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 49554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private void prepareRawRequestHeaders() throws IOException { 4963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Request.Builder result = request.newBuilder(); 49754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 4983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (request.getUserAgent() == null) { 4993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller result.setUserAgent(getDefaultUserAgent()); 50054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 50154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 5023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (request.header("Host") == null) { 5033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller result.header("Host", hostHeader(request.url())); 50454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 50554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 50654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if ((connection == null || connection.getHttpMinorVersion() != 0) 5073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && request.header("Connection") == null) { 5083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller result.header("Connection", "Keep-Alive"); 50954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 51054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 5113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (request.header("Accept-Encoding") == null) { 51254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson transparentGzip = true; 5133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller result.header("Accept-Encoding", "gzip"); 51454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 51554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 5163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (hasRequestBody() && request.header("Content-Type") == null) { 5173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller result.header("Content-Type", "application/x-www-form-urlencoded"); 51854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 51954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 520a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath CookieHandler cookieHandler = client.getCookieHandler(); 52154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (cookieHandler != null) { 5223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Capture the request headers added so far so that they can be offered to the CookieHandler. 5233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // This is mostly to stay close to the RI; it is unlikely any of the headers above would 5243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // affect cookie choice besides "Host". 5253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Map<String, List<String>> headers = OkHeaders.toMultimap(result.build().headers(), null); 52654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 5273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Map<String, List<String>> cookies = cookieHandler.get(request.uri(), headers); 52854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 5293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Add any new cookies to the request. 5303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller OkHeaders.addCookies(result, cookies); 531c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 532c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 5333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller request = result.build(); 53454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 53554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 53654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public static String getDefaultUserAgent() { 53754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson String agent = System.getProperty("http.agent"); 53854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return agent != null ? agent : ("Java" + System.getProperty("java.version")); 53954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 54054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 5413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public static String hostHeader(URL url) { 5423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return getEffectivePort(url) != getDefaultPort(url.getProtocol()) 5433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ? url.getHost() + ":" + url.getPort() 5443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller : url.getHost(); 54554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 54654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 54754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 54854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Flushes the remaining request header and body, parses the HTTP response 54954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * headers and starts reading the HTTP response body if it exists. 55054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 55154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final void readResponse() throws IOException { 5523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (response != null) return; 5533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (responseSource == null) throw new IllegalStateException("call sendRequest() first!"); 5543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (!responseSource.requiresConnection()) return; 55554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 5563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Flush the request body if there's data outstanding. 5573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (bufferedRequestBody != null && bufferedRequestBody.buffer().size() > 0) { 5583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller bufferedRequestBody.flush(); 55954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 56054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 56154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (sentRequestMillis == -1) { 5623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (OkHeaders.contentLength(request) == -1 && requestBodyOut instanceof RetryableSink) { 5633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // We might not learn the Content-Length until the request body has been buffered. 5643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller long contentLength = ((RetryableSink) requestBodyOut).contentLength(); 5653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller request = request.newBuilder() 5663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .header("Content-Length", Long.toString(contentLength)) 5673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .build(); 56854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 5693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller transport.writeRequestHeaders(request); 57054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 57154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 57254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (requestBodyOut != null) { 5733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (bufferedRequestBody != null) { 5743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // This also closes the wrapped requestBodyOut. 5753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller bufferedRequestBody.close(); 5763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } else { 5773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller requestBodyOut.close(); 5783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 5793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (requestBodyOut instanceof RetryableSink) { 5803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller transport.writeRequestBody((RetryableSink) requestBodyOut); 58154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 58254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 58354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 58454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson transport.flushRequest(); 58554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 5863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller response = transport.readResponseHeaders() 5873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .request(request) 5883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .handshake(connection.getHandshake()) 5893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .header(OkHeaders.SENT_MILLIS, Long.toString(sentRequestMillis)) 5903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .header(OkHeaders.RECEIVED_MILLIS, Long.toString(System.currentTimeMillis())) 5913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .setResponseSource(responseSource) 5923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .build(); 5933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.setHttpMinorVersion(response.httpMinorVersion()); 5943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller receiveHeaders(response.headers()); 59554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 59654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (responseSource == ResponseSource.CONDITIONAL_CACHE) { 5973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (validatingResponse.validate(response)) { 5983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller transport.emptyTransferStream(); 5993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller releaseConnection(); 6003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller response = combine(validatingResponse, response); 6013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Update the cache after combining headers but before stripping the 6033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Content-Encoding header (as performed by initContentStream()). 604a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath OkResponseCache responseCache = client.getOkResponseCache(); 605a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath responseCache.trackConditionalCacheHit(); 6063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller responseCache.update(validatingResponse, cacheableResponse()); 607166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 6083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (validatingResponse.body() != null) { 6093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller initContentStream(validatingResponse.body().source()); 6103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 61154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return; 61254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else { 6133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closeQuietly(validatingResponse.body()); 61454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 615c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 616c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 6173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (!hasResponseBody()) { 6183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Don't call initContentStream() when the response doesn't have any content. 6193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller responseTransferSource = transport.getTransferStream(cacheRequest); 6203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller responseBody = responseTransferSource; 6213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return; 622c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 623c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 6243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller maybeCache(); 62554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson initContentStream(transport.getTransferStream(cacheRequest)); 62654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 627c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 6283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 6293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Combines cached headers with a network headers as defined by RFC 2616, 6303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * 13.5.3. 6313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 6323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private static Response combine(Response cached, Response network) throws IOException { 6333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Headers.Builder result = new Headers.Builder(); 6343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Headers cachedHeaders = cached.headers(); 6363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller for (int i = 0; i < cachedHeaders.size(); i++) { 6373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller String fieldName = cachedHeaders.name(i); 6383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller String value = cachedHeaders.value(i); 6393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if ("Warning".equals(fieldName) && value.startsWith("1")) { 6403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller continue; // drop 100-level freshness warnings 6413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (!isEndToEnd(fieldName) || network.header(fieldName) == null) { 6433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller result.add(fieldName, value); 6443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Headers networkHeaders = network.headers(); 6483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller for (int i = 0; i < networkHeaders.size(); i++) { 6493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller String fieldName = networkHeaders.name(i); 6503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (isEndToEnd(fieldName)) { 6513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller result.add(fieldName, networkHeaders.value(i)); 6523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return cached.newBuilder().headers(result.build()).build(); 6563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 6593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Returns true if {@code fieldName} is an end-to-end HTTP header, as 6603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * defined by RFC 2616, 13.5.1. 6613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 6623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private static boolean isEndToEnd(String fieldName) { 6633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return !"Connection".equalsIgnoreCase(fieldName) 6643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && !"Keep-Alive".equalsIgnoreCase(fieldName) 6653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && !"Proxy-Authenticate".equalsIgnoreCase(fieldName) 6663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && !"Proxy-Authorization".equalsIgnoreCase(fieldName) 6673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && !"TE".equalsIgnoreCase(fieldName) 6683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && !"Trailers".equalsIgnoreCase(fieldName) 6693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && !"Transfer-Encoding".equalsIgnoreCase(fieldName) 6703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && !"Upgrade".equalsIgnoreCase(fieldName); 6713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private TunnelRequest getTunnelConfig() { 6743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (!request.isHttps()) return null; 6753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller String userAgent = request.getUserAgent(); 6773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (userAgent == null) userAgent = getDefaultUserAgent(); 6783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller URL url = request.url(); 6803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return new TunnelRequest(url.getHost(), getEffectivePort(url), userAgent, 6813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller request.getProxyAuthorization()); 68254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 6837899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath 6843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public void receiveHeaders(Headers headers) throws IOException { 685a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath CookieHandler cookieHandler = client.getCookieHandler(); 68654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (cookieHandler != null) { 6873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller cookieHandler.put(request.uri(), OkHeaders.toMultimap(headers, null)); 6887899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath } 68954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 690c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath} 691