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; 237c7f22d80748dc444d5da3c5be11d7d81ef14a2bLorenzo Colittiimport com.squareup.okhttp.HostResolver; 24a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport com.squareup.okhttp.OkHttpClient; 25a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport com.squareup.okhttp.OkResponseCache; 263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Request; 273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Response; 282231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.ResponseSource; 293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Route; 302231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.TunnelRequest; 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; 812ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller private final Response priorResponse; 8254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 8354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private Transport transport; 8454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 8554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** The time when the request headers were written, or -1 if they haven't been written yet. */ 8654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson long sentRequestMillis = -1; 8754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 8854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 8954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * True if this client added an "Accept-Encoding: gzip" header field and is 9054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * therefore responsible for also decompressing the transfer stream. 9154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 9254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private boolean transparentGzip; 9354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * True if the request body must be completely buffered before transmission; 963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * false if it can be streamed. Buffering has two advantages: we don't need 973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * the content-length in advance and we can retransmit if necessary. The 983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * upside of streaming is that we can save memory. 993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 1003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public final boolean bufferRequestBody; 10154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 1022ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller /** 1032ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller * The original application-provided request. Never modified by OkHttp. When 1042ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller * follow-up requests are necessary, they are derived from this request. 1052ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller */ 1062ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller private final Request userRequest; 1072ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller 1082ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller /** 1092ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller * The request to send on the network, or null for no network request. This is 1102ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller * derived from the user request, and customized to support OkHttp features 1112ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller * like compression and caching. 1122ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller */ 1132ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller private Request networkRequest; 1142ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller 1152ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller /** 1162ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller * The cached response, or null if the cache doesn't exist or cannot be used 1172ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller * for this request. Conditional caching means this may be non-null even when 1182ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller * the network request is non-null. Never modified by OkHttp. 1192ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller */ 1202ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller private Response cacheResponse; 1212ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller 1222ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller /** 1232ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller * The response read from the network. Null if the network response hasn't 1242ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller * been read yet, or if the network is not used. Never modified by OkHttp. 1252ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller */ 1262ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller private Response networkResponse; 1272ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller 1282ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller /** 1292ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller * The user-visible response. This is derived from either the network 1302ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller * response, cache response, or both. It is customized to support OkHttp 1312ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller * features like compression and caching. 1322ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller */ 1332ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller private Response userResponse; 1342ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller 1353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private Sink requestBodyOut; 1363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private BufferedSink bufferedRequestBody; 13754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 1383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private ResponseSource responseSource; 13954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 1403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** Null until a response is received from the network or the cache. */ 1413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private Source responseTransferSource; 1423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private Source responseBody; 1433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private InputStream responseBodyBytes; 14454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 1453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** The cache request currently being populated from a network response. */ 1462ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller private CacheRequest storeRequest; 14754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 14854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 1493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * @param request the HTTP request without a body. The body must be 1503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * written via the engine's request body stream. 15154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * @param connection the connection used for an intermediate response 152a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * immediately prior to this request/response pair, such as a same-host 153a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * redirect. This engine assumes ownership of the connection and must 154a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * release it when it is unneeded. 1553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * @param routeSelector the route selector used for a failed attempt 1563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * immediately preceding this attempt, or null if this request doesn't 1573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * recover from a failure. 15854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 1593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public HttpEngine(OkHttpClient client, Request request, boolean bufferRequestBody, 1602ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller Connection connection, RouteSelector routeSelector, RetryableSink requestBodyOut, 1612ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller Response priorResponse) { 162a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath this.client = client; 1632ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller this.userRequest = request; 1643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller this.bufferRequestBody = bufferRequestBody; 16554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.connection = connection; 1663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller this.routeSelector = routeSelector; 16754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson this.requestBodyOut = requestBodyOut; 1682ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller this.priorResponse = priorResponse; 16978092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller 17078092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller if (connection != null) { 17178092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller connection.setOwner(this); 17278092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller this.route = connection.getRoute(); 17378092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller } else { 17478092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller this.route = null; 17578092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller } 17654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 17754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 17854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 17954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Figures out what the response source will be, and opens a socket to that 18054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * source if necessary. Prepares the request headers and gets ready to start 18154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * writing the request body if it exists. 18254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 18354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final void sendRequest() throws IOException { 1843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (responseSource != null) return; // Already sent. 1853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (transport != null) throw new IllegalStateException(); 18654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 1872ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller Request request = networkRequest(userRequest); 1883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1892ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller OkResponseCache responseCache = client.getOkResponseCache(); 1902ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller Response cacheCandidate = responseCache != null 1913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ? responseCache.get(request) 1923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller : null; 1933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller long now = System.currentTimeMillis(); 1942ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller CacheStrategy cacheStrategy = new CacheStrategy.Factory(now, request, cacheCandidate).get(); 1953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller responseSource = cacheStrategy.source; 1962ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller networkRequest = cacheStrategy.networkRequest; 1972ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller cacheResponse = cacheStrategy.cacheResponse; 1983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 199a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath if (responseCache != null) { 200a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath responseCache.trackResponse(responseSource); 20154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 20254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 2032ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller if (cacheCandidate != null 2042ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller && (responseSource == ResponseSource.NONE || cacheResponse == null)) { 2052ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it. 20654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 20754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 2082ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller if (networkRequest != null) { 2093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Open a connection unless we inherited one from a redirect. 2103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (connection == null) { 2112ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller connect(networkRequest); 2123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 213a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath 21478092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller // Blow up if we aren't the current owner of the connection. 21578092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller if (connection.getOwner() != this && !connection.isSpdy()) throw new AssertionError(); 21678092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller 2173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller transport = (Transport) connection.newTransport(this); 21854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 2193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Create a request body if we don't have one already. We'll already have 2203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // one if we're retrying a failed POST. 2213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (hasRequestBody() && requestBodyOut == null) { 2223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller requestBodyOut = transport.createRequestBody(request); 2233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 22454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 22554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else { 2263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // We're using a cached response. Recycle a connection we may have inherited from a redirect. 2273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (connection != null) { 2283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller client.getConnectionPool().recycle(connection); 2293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection = null; 2303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 23154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 2323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // No need for the network! Promote the cached response immediately. 2332ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller this.userResponse = cacheResponse.newBuilder() 2342ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller .request(userRequest) 2352ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller .priorResponse(stripBody(priorResponse)) 2362ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller .cacheResponse(stripBody(cacheResponse)) 2372ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller .build(); 2382ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller if (userResponse.body() != null) { 2392ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller initContentStream(userResponse.body().source()); 2403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 24154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 2423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 24354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 2442ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller private static Response stripBody(Response response) { 2452ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller return response != null && response.body() != null 2462ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller ? response.newBuilder().body(null).build() 2472ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller : response; 24854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 24954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 25054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** Connect to the origin server either directly or via a proxy. */ 2512ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller private void connect(Request request) throws IOException { 2523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (connection != null) throw new IllegalStateException(); 2533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 25454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (routeSelector == null) { 2553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller String uriHost = request.url().getHost(); 2563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (uriHost == null || uriHost.length() == 0) { 2573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller throw new UnknownHostException(request.url().toString()); 25854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 2597407d6984ce69693097befc9b72609a8156463bbNarayan Kamath SSLSocketFactory sslSocketFactory = null; 2607407d6984ce69693097befc9b72609a8156463bbNarayan Kamath HostnameVerifier hostnameVerifier = null; 2613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (request.isHttps()) { 262a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath sslSocketFactory = client.getSslSocketFactory(); 263a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath hostnameVerifier = client.getHostnameVerifier(); 2647407d6984ce69693097befc9b72609a8156463bbNarayan Kamath } 2653f968acf193178145e9d227f2f08b95e2d6a6c26Jake Wharton Address address = new Address(uriHost, getEffectivePort(request.url()), 2663f968acf193178145e9d227f2f08b95e2d6a6c26Jake Wharton client.getSocketFactory(), sslSocketFactory, hostnameVerifier, client.getAuthenticator(), 2673f968acf193178145e9d227f2f08b95e2d6a6c26Jake Wharton client.getProxy(), client.getProtocols()); 2683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller routeSelector = new RouteSelector(address, request.uri(), client.getProxySelector(), 2697c7f22d80748dc444d5da3c5be11d7d81ef14a2bLorenzo Colitti client.getConnectionPool(), client.getHostResolver(), client.getRoutesDatabase()); 27054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 2713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection = routeSelector.next(request.method()); 27378092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller connection.setOwner(this); 2743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 27554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (!connection.isConnected()) { 276a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath connection.connect(client.getConnectTimeout(), client.getReadTimeout(), getTunnelConfig()); 2773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (connection.isSpdy()) client.getConnectionPool().share(connection); 278a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath client.getRoutesDatabase().connected(connection.getRoute()); 2793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } else if (!connection.isSpdy()) { 28076739264ce52fe7a6c5c3558dad87b649118deffNarayan Kamath connection.updateReadTimeout(client.getReadTimeout()); 28154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 28254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 2833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller route = connection.getRoute(); 28454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 28554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 28654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 28754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Called immediately before the transport transmits HTTP request headers. 28854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * This is used to observe the sent time should the request be cached. 28954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 29054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public void writingRequestHeaders() { 2913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (sentRequestMillis != -1) throw new IllegalStateException(); 29254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson sentRequestMillis = System.currentTimeMillis(); 29354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 29454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 29554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson boolean hasRequestBody() { 2962ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller return HttpMethod.hasRequestBody(userRequest.method()); 29754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 29854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 29954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** Returns the request body or null if this request doesn't have a body. */ 3003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public final Sink getRequestBody() { 3013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (responseSource == null) throw new IllegalStateException(); 30254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return requestBodyOut; 30354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 30454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 3053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public final BufferedSink getBufferedRequestBody() { 3063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink result = bufferedRequestBody; 3073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (result != null) return result; 3083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Sink requestBody = getRequestBody(); 3093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return requestBody != null 3103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ? (bufferedRequestBody = Okio.buffer(requestBody)) 3113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller : null; 3123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 31454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final boolean hasResponse() { 3152ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller return userResponse != null; 31654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 31754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 3183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public final Request getRequest() { 3192ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller return userRequest; 32054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 32154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 3223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** Returns the engine's response. */ 3233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // TODO: the returned body will always be null. 3243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public final Response getResponse() { 3252ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller if (userResponse == null) throw new IllegalStateException(); 3262ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller return userResponse; 32754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 32854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 3293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public final Source getResponseBody() { 3302ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller if (userResponse == null) throw new IllegalStateException(); 3313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return responseBody; 33254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 33354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 3343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public final InputStream getResponseBodyBytes() { 3353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller InputStream result = responseBodyBytes; 3363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return result != null 3373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ? result 3383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller : (responseBodyBytes = Okio.buffer(getResponseBody()).inputStream()); 33954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 34054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 34154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final Connection getConnection() { 34254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return connection; 34354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 34454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 34554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 3463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Report and attempt to recover from {@code e}. Returns a new HTTP engine 3473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * that should be used for the retry if {@code e} is recoverable, or null if 3483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * the failure is permanent. 34954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 3503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public HttpEngine recover(IOException e) { 3513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (routeSelector != null && connection != null) { 3523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller routeSelector.connectFailed(connection, e); 3533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller boolean canRetryRequestBody = requestBodyOut == null || requestBodyOut instanceof RetryableSink; 3563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (routeSelector == null && connection == null // No connection. 3573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller || routeSelector != null && !routeSelector.hasNext() // No more routes to attempt. 3583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller || !isRecoverable(e) 3593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller || !canRetryRequestBody) { 3603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return null; 3613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Connection connection = close(); 3643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // For failure recovery, use the same route selector with a new connection. 3662ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller return new HttpEngine(client, userRequest, bufferRequestBody, connection, routeSelector, 3672ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller (RetryableSink) requestBodyOut, priorResponse); 3683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private boolean isRecoverable(IOException e) { 3713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // If the problem was a CertificateException from the X509TrustManager, 3723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // do not retry, we didn't have an abrupt server-initiated exception. 3733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller boolean sslFailure = 3743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller e instanceof SSLHandshakeException && e.getCause() instanceof CertificateException; 3753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller boolean protocolFailure = e instanceof ProtocolException; 3763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return !sslFailure && !protocolFailure; 3773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 3803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Returns the route used to retrieve the response. Null if we haven't 3813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * connected yet, or if no connection was necessary. 3823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 3833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public Route getRoute() { 3843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return route; 38554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 38654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 38754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson private void maybeCache() throws IOException { 388a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath OkResponseCache responseCache = client.getOkResponseCache(); 389a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath if (responseCache == null) return; 390a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath 39154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // Should we cache this response for this request? 3922ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller if (!CacheStrategy.isCacheable(userResponse, networkRequest)) { 3932ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller responseCache.maybeRemove(networkRequest); 39454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return; 39554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 39654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 39754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // Offer this request to the cache. 3982ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller storeRequest = responseCache.put(stripBody(userResponse)); 39954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 40054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 40154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 4023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Configure the socket connection to be either pooled or closed when it is 4033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * either exhausted or closed. If it is unneeded when this is called, it will 4043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * be released immediately. 40554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 4063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public final void releaseConnection() throws IOException { 407c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller if (transport != null && connection != null) { 4083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller transport.releaseConnectionOnIdle(); 40954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 4103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection = null; 41154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 41254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 41354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 414602d5e4cfdbd0bad91e7872837f95aff5b461595Jesse Wilson * Immediately closes the socket connection if it's currently held by this 415602d5e4cfdbd0bad91e7872837f95aff5b461595Jesse Wilson * engine. Use this to interrupt an in-flight request from any thread. It's 416602d5e4cfdbd0bad91e7872837f95aff5b461595Jesse Wilson * the caller's responsibility to close the request body and response body 417602d5e4cfdbd0bad91e7872837f95aff5b461595Jesse Wilson * streams; otherwise resources may be leaked. 418602d5e4cfdbd0bad91e7872837f95aff5b461595Jesse Wilson */ 419602d5e4cfdbd0bad91e7872837f95aff5b461595Jesse Wilson public final void disconnect() throws IOException { 420602d5e4cfdbd0bad91e7872837f95aff5b461595Jesse Wilson if (transport != null) { 421602d5e4cfdbd0bad91e7872837f95aff5b461595Jesse Wilson transport.disconnect(this); 422602d5e4cfdbd0bad91e7872837f95aff5b461595Jesse Wilson } 423602d5e4cfdbd0bad91e7872837f95aff5b461595Jesse Wilson } 424602d5e4cfdbd0bad91e7872837f95aff5b461595Jesse Wilson 425602d5e4cfdbd0bad91e7872837f95aff5b461595Jesse Wilson /** 4263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Release any resources held by this engine. If a connection is still held by 4273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * this engine, it is returned. 42854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 4293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public final Connection close() { 4303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (bufferedRequestBody != null) { 4313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // This also closes the wrapped requestBodyOut. 4323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closeQuietly(bufferedRequestBody); 4333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } else if (requestBodyOut != null) { 4343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closeQuietly(requestBodyOut); 435c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 436c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 4373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // If this engine never achieved a response body, its connection cannot be reused. 4383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (responseBody == null) { 4393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closeQuietly(connection); 4403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection = null; 4413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return null; 4423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 443c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 4443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Close the response body. This will recycle the connection if it is eligible. 4453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closeQuietly(responseBody); 4463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Clear the buffer held by the response body input stream adapter. 4483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closeQuietly(responseBodyBytes); 4493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Close the connection if it cannot be reused. 4513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (transport != null && !transport.canReuseConnection()) { 4523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closeQuietly(connection); 4533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection = null; 4543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return null; 45554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 4563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 45778092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller // Prevent this engine from disconnecting a connection it no longer owns. 45878092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller if (connection != null && !connection.clearOwner()) { 45978092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller connection = null; 46078092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller } 46178092f38ebd93018ead53a87b53118dc829cbb8aNeil Fuller 4623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Connection result = connection; 4633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection = null; 4643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return result; 46554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 466c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 4673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 4683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Initialize the response content stream from the response transfer source. 4693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * These two sources are the same unless we're doing transparent gzip, in 4703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * which case the content source is decompressed. 4713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * 4723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * <p>Whenever we do transparent gzip we also strip the corresponding headers. 4733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * We strip the Content-Encoding header to prevent the application from 4743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * attempting to double decompress. We strip the Content-Length header because 4753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * it is the length of the compressed content, but the application is only 4763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * interested in the length of the uncompressed content. 4773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * 4783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * <p>This method should only be used for non-empty response bodies. Response 4793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * codes like "304 Not Modified" can include "Content-Encoding: gzip" without 4803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * a response body and we will crash if we attempt to decompress the zero-byte 4813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * source. 4823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 4833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private void initContentStream(Source transferSource) throws IOException { 4843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller responseTransferSource = transferSource; 4852ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller if (transparentGzip && "gzip".equalsIgnoreCase(userResponse.header("Content-Encoding"))) { 4862ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller userResponse = userResponse.newBuilder() 4873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .removeHeader("Content-Encoding") 4883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .removeHeader("Content-Length") 4893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .build(); 4903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller responseBody = new GzipSource(transferSource); 49154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else { 4923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller responseBody = transferSource; 49354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 49454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 49554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 49654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 49754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Returns true if the response must have a (possibly 0-length) body. 49854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * See RFC 2616 section 4.3. 49954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 50054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final boolean hasResponseBody() { 50154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // HEAD requests never yield a body regardless of the response headers. 5022ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller if (userRequest.method().equals("HEAD")) { 50354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return false; 50454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 50554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 5062ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller int responseCode = userResponse.code(); 50754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if ((responseCode < HTTP_CONTINUE || responseCode >= 200) 5083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && responseCode != HTTP_NO_CONTENT 5093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && responseCode != HTTP_NOT_MODIFIED) { 51054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return true; 51154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 51254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 51354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // If the Content-Length or Transfer-Encoding headers disagree with the 51454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // response code, the response is malformed. For best compatibility, we 51554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson // honor the headers. 5162ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller if (OkHeaders.contentLength(networkResponse) != -1 5172ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller || "chunked".equalsIgnoreCase(networkResponse.header("Transfer-Encoding"))) { 51854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return true; 51954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 52054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 52154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return false; 52254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 52354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 52454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 5253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Populates request with defaults and cookies. 52654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * 52754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * <p>This client doesn't specify a default {@code Accept} header because it 52854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * doesn't know what content types the application is interested in. 52954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 5302ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller private Request networkRequest(Request request) throws IOException { 5313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Request.Builder result = request.newBuilder(); 53254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 5333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (request.getUserAgent() == null) { 5343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller result.setUserAgent(getDefaultUserAgent()); 53554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 53654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 5373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (request.header("Host") == null) { 5383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller result.header("Host", hostHeader(request.url())); 53954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 54054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 54154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if ((connection == null || connection.getHttpMinorVersion() != 0) 5423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && request.header("Connection") == null) { 5433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller result.header("Connection", "Keep-Alive"); 54454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 54554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 5463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (request.header("Accept-Encoding") == null) { 54754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson transparentGzip = true; 5483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller result.header("Accept-Encoding", "gzip"); 54954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 55054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 5513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (hasRequestBody() && request.header("Content-Type") == null) { 5523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller result.header("Content-Type", "application/x-www-form-urlencoded"); 55354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 55454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 555a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath CookieHandler cookieHandler = client.getCookieHandler(); 55654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (cookieHandler != null) { 5573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Capture the request headers added so far so that they can be offered to the CookieHandler. 5583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // This is mostly to stay close to the RI; it is unlikely any of the headers above would 5593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // affect cookie choice besides "Host". 5603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Map<String, List<String>> headers = OkHeaders.toMultimap(result.build().headers(), null); 56154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 5623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Map<String, List<String>> cookies = cookieHandler.get(request.uri(), headers); 56354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 5643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Add any new cookies to the request. 5653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller OkHeaders.addCookies(result, cookies); 566c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 567c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 5682ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller return result.build(); 56954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 57054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 57154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public static String getDefaultUserAgent() { 57254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson String agent = System.getProperty("http.agent"); 57354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return agent != null ? agent : ("Java" + System.getProperty("java.version")); 57454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 57554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 5763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public static String hostHeader(URL url) { 5773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return getEffectivePort(url) != getDefaultPort(url.getProtocol()) 5783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ? url.getHost() + ":" + url.getPort() 5793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller : url.getHost(); 58054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 58154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 58254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson /** 58354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * Flushes the remaining request header and body, parses the HTTP response 58454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * headers and starts reading the HTTP response body if it exists. 58554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson */ 58654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson public final void readResponse() throws IOException { 5872ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller if (userResponse != null) { 5882ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller return; // Already ready. 5892ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller } 5902ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller if (networkRequest == null && cacheResponse == null) { 5912ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller throw new IllegalStateException("call sendRequest() first!"); 5922ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller } 5932ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller if (networkRequest == null) { 5942ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller return; // No network response to read. 5952ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller } 59654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 5973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Flush the request body if there's data outstanding. 5983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (bufferedRequestBody != null && bufferedRequestBody.buffer().size() > 0) { 5993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller bufferedRequestBody.flush(); 60054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 60154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 60254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (sentRequestMillis == -1) { 6032ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller if (OkHeaders.contentLength(networkRequest) == -1 6042ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller && requestBodyOut instanceof RetryableSink) { 6053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // We might not learn the Content-Length until the request body has been buffered. 6063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller long contentLength = ((RetryableSink) requestBodyOut).contentLength(); 6072ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller networkRequest = networkRequest.newBuilder() 6083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .header("Content-Length", Long.toString(contentLength)) 6093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .build(); 61054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 6112ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller transport.writeRequestHeaders(networkRequest); 61254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 61354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 61454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (requestBodyOut != null) { 6153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (bufferedRequestBody != null) { 6163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // This also closes the wrapped requestBodyOut. 6173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller bufferedRequestBody.close(); 6183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } else { 6193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller requestBodyOut.close(); 6203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (requestBodyOut instanceof RetryableSink) { 6223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller transport.writeRequestBody((RetryableSink) requestBodyOut); 62354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 62454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 62554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 62654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson transport.flushRequest(); 62754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 6282ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller networkResponse = transport.readResponseHeaders() 6292ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller .request(networkRequest) 6303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .handshake(connection.getHandshake()) 6313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .header(OkHeaders.SENT_MILLIS, Long.toString(sentRequestMillis)) 6323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .header(OkHeaders.RECEIVED_MILLIS, Long.toString(System.currentTimeMillis())) 6333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .setResponseSource(responseSource) 6343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .build(); 6352ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller connection.setHttpMinorVersion(networkResponse.httpMinorVersion()); 6362ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller receiveHeaders(networkResponse.headers()); 63754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson 63854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (responseSource == ResponseSource.CONDITIONAL_CACHE) { 6392ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller if (cacheResponse.validate(networkResponse)) { 6402ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller userResponse = cacheResponse.newBuilder() 6412ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller .request(userRequest) 6422ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller .priorResponse(stripBody(priorResponse)) 6432ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller .headers(combine(cacheResponse.headers(), networkResponse.headers())) 6442ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller .cacheResponse(stripBody(cacheResponse)) 6452ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller .networkResponse(stripBody(networkResponse)) 6462ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller .build(); 6473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller transport.emptyTransferStream(); 6483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller releaseConnection(); 6493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Update the cache after combining headers but before stripping the 6513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Content-Encoding header (as performed by initContentStream()). 652a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath OkResponseCache responseCache = client.getOkResponseCache(); 653a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath responseCache.trackConditionalCacheHit(); 6542ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller responseCache.update(cacheResponse, stripBody(userResponse)); 6552ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller if (cacheResponse.body() != null) { 6562ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller initContentStream(cacheResponse.body().source()); 6573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6582ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller 65954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson return; 66054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } else { 6612ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller closeQuietly(cacheResponse.body()); 66254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 663c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 664c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 6652ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller userResponse = networkResponse.newBuilder() 6662ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller .request(userRequest) 6672ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller .priorResponse(stripBody(priorResponse)) 6682ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller .cacheResponse(stripBody(cacheResponse)) 6692ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller .networkResponse(stripBody(networkResponse)) 6702ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller .build(); 6712ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller 6723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (!hasResponseBody()) { 6733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Don't call initContentStream() when the response doesn't have any content. 6742ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller responseTransferSource = transport.getTransferStream(storeRequest); 6753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller responseBody = responseTransferSource; 6763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return; 677c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 678c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 6793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller maybeCache(); 6802ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller initContentStream(transport.getTransferStream(storeRequest)); 68154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 682c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 6833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 6843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Combines cached headers with a network headers as defined by RFC 2616, 6853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * 13.5.3. 6863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 6872ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller private static Headers combine(Headers cachedHeaders, Headers networkHeaders) throws IOException { 6883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Headers.Builder result = new Headers.Builder(); 6893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller for (int i = 0; i < cachedHeaders.size(); i++) { 6913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller String fieldName = cachedHeaders.name(i); 6923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller String value = cachedHeaders.value(i); 6933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if ("Warning".equals(fieldName) && value.startsWith("1")) { 6943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller continue; // drop 100-level freshness warnings 6953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6962ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller if (!isEndToEnd(fieldName) || networkHeaders.get(fieldName) == null) { 6973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller result.add(fieldName, value); 6983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 7003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller for (int i = 0; i < networkHeaders.size(); i++) { 7023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller String fieldName = networkHeaders.name(i); 7033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (isEndToEnd(fieldName)) { 7043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller result.add(fieldName, networkHeaders.value(i)); 7053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 7063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 7073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7082ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller return result.build(); 7093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 7103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 7123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Returns true if {@code fieldName} is an end-to-end HTTP header, as 7133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * defined by RFC 2616, 13.5.1. 7143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 7153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private static boolean isEndToEnd(String fieldName) { 7163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return !"Connection".equalsIgnoreCase(fieldName) 7173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && !"Keep-Alive".equalsIgnoreCase(fieldName) 7183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && !"Proxy-Authenticate".equalsIgnoreCase(fieldName) 7193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && !"Proxy-Authorization".equalsIgnoreCase(fieldName) 7203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && !"TE".equalsIgnoreCase(fieldName) 7213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && !"Trailers".equalsIgnoreCase(fieldName) 7223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && !"Transfer-Encoding".equalsIgnoreCase(fieldName) 7233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller && !"Upgrade".equalsIgnoreCase(fieldName); 7243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 7253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private TunnelRequest getTunnelConfig() { 7272ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller if (!userRequest.isHttps()) return null; 7283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7292ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller String userAgent = userRequest.getUserAgent(); 7303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (userAgent == null) userAgent = getDefaultUserAgent(); 7313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7322ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller URL url = userRequest.url(); 7333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return new TunnelRequest(url.getHost(), getEffectivePort(url), userAgent, 7342ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller userRequest.getProxyAuthorization()); 73554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 7367899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath 7373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public void receiveHeaders(Headers headers) throws IOException { 738a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath CookieHandler cookieHandler = client.getCookieHandler(); 73954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson if (cookieHandler != null) { 7402ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller cookieHandler.put(userRequest.uri(), OkHeaders.toMultimap(headers, null)); 7417899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath } 74254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson } 743c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath} 744