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