HttpEngine.java revision 3c938a3f6b61ce5e2dba0d039b03fe73b89fd26c
1c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/*
2c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  Licensed to the Apache Software Foundation (ASF) under one or more
3c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  contributor license agreements.  See the NOTICE file distributed with
4c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  this work for additional information regarding copyright ownership.
5c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  The ASF licenses this file to You under the Apache License, Version 2.0
6c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  (the "License"); you may not use this file except in compliance with
7c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  the License.  You may obtain a copy of the License at
8c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
9c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *     http://www.apache.org/licenses/LICENSE-2.0
10c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
11c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  Unless required by applicable law or agreed to in writing, software
12c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  distributed under the License is distributed on an "AS IS" BASIS,
13c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  See the License for the specific language governing permissions and
15c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  limitations under the License.
16c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */
17c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
182231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonpackage com.squareup.okhttp.internal.http;
192231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
202231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.Address;
212231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.Connection;
223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Headers;
23a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport com.squareup.okhttp.OkHttpClient;
24a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport com.squareup.okhttp.OkResponseCache;
253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Request;
263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Response;
272231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.ResponseSource;
283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Route;
292231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.TunnelRequest;
3054cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport com.squareup.okhttp.internal.Dns;
31c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.IOException;
32c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.InputStream;
33c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CacheRequest;
34c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CookieHandler;
353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.net.ProtocolException;
36c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URL;
372231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.UnknownHostException;
383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.security.cert.CertificateException;
39c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.List;
40c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Map;
417899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport javax.net.ssl.HostnameVerifier;
423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport javax.net.ssl.SSLHandshakeException;
43c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport javax.net.ssl.SSLSocketFactory;
443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.BufferedSink;
453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.GzipSource;
463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Okio;
473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Sink;
483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Source;
49c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.Util.closeQuietly;
5154cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport static com.squareup.okhttp.internal.Util.getDefaultPort;
5254cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport static com.squareup.okhttp.internal.Util.getEffectivePort;
533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.http.StatusLine.HTTP_CONTINUE;
543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static java.net.HttpURLConnection.HTTP_NOT_MODIFIED;
553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static java.net.HttpURLConnection.HTTP_NO_CONTENT;
5654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
57c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/**
58c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Handles a single HTTP request/response pair. Each HTTP engine follows this
59c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * lifecycle:
60c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <ol>
6154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * <li>It is created.
6254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * <li>The HTTP request message is sent with sendRequest(). Once the request
6354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * is sent it is an error to modify the request headers. After
6454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * sendRequest() has been called the request body can be written to if
6554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * it exists.
6654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * <li>The HTTP response message is read with readResponse(). After the
6754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * response has been read the response headers and body can be read.
6854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * All responses have a response body input stream, though in some
6954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * instances this stream is empty.
70c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * </ol>
71c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
72c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>The request and response may be served by the HTTP response cache, by the
73c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * network, or by both in the event of a conditional GET.
74c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */
75c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathpublic class HttpEngine {
763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  final OkHttpClient client;
7754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Connection connection;
793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private RouteSelector routeSelector;
803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Route route;
8154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
8254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private Transport transport;
8354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
8454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** The time when the request headers were written, or -1 if they haven't been written yet. */
8554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  long sentRequestMillis = -1;
8654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
8754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
8854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * True if this client added an "Accept-Encoding: gzip" header field and is
8954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * therefore responsible for also decompressing the transfer stream.
9054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
9154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private boolean transparentGzip;
9254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * True if the request body must be completely buffered before transmission;
953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * false if it can be streamed. Buffering has two advantages: we don't need
963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * the content-length in advance and we can retransmit if necessary. The
973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * upside of streaming is that we can save memory.
983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final boolean bufferRequestBody;
10054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Request request;
1023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Sink requestBodyOut;
1033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private BufferedSink bufferedRequestBody;
10454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private ResponseSource responseSource;
10654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** Null until a response is received from the network or the cache. */
1083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Response response;
1093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Source responseTransferSource;
1103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Source responseBody;
1113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private InputStream responseBodyBytes;
11254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
11354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
1143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * The cache response currently being validated on a conditional get. Null
1153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * if the cached response doesn't exist or doesn't need validation. If the
1163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * conditional get succeeds, these will be used for the response. If it fails,
1173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * it will be set to null.
11854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
1193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Response validatingResponse;
12054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** The cache request currently being populated from a network response. */
1223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private CacheRequest cacheRequest;
12354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
12454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
1253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * @param request the HTTP request without a body. The body must be
1263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   *     written via the engine's request body stream.
12754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @param connection the connection used for an intermediate response
128a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath   *     immediately prior to this request/response pair, such as a same-host
129a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath   *     redirect. This engine assumes ownership of the connection and must
130a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath   *     release it when it is unneeded.
1313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * @param routeSelector the route selector used for a failed attempt
1323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   *     immediately preceding this attempt, or null if this request doesn't
1333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   *     recover from a failure.
13454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
1353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public HttpEngine(OkHttpClient client, Request request, boolean bufferRequestBody,
1363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      Connection connection, RouteSelector routeSelector, RetryableSink requestBodyOut) {
137a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    this.client = client;
1383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.request = request;
1393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.bufferRequestBody = bufferRequestBody;
14054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this.connection = connection;
1413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.routeSelector = routeSelector;
1423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.route = connection != null ? connection.getRoute() : null;
14354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this.requestBodyOut = requestBodyOut;
14454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
14554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
14654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
14754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Figures out what the response source will be, and opens a socket to that
14854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * source if necessary. Prepares the request headers and gets ready to start
14954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * writing the request body if it exists.
15054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
15154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final void sendRequest() throws IOException {
1523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (responseSource != null) return; // Already sent.
1533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (transport != null) throw new IllegalStateException();
15454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
15554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    prepareRawRequestHeaders();
156a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    OkResponseCache responseCache = client.getOkResponseCache();
1573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Response cacheResponse = responseCache != null
1593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        ? responseCache.get(request)
1603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        : null;
1613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    long now = System.currentTimeMillis();
1623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    CacheStrategy cacheStrategy = new CacheStrategy.Factory(now, request, cacheResponse).get();
1633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    responseSource = cacheStrategy.source;
1643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    request = cacheStrategy.request;
1653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
166a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    if (responseCache != null) {
167a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      responseCache.trackResponse(responseSource);
16854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
16954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (responseSource != ResponseSource.NETWORK) {
1713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      validatingResponse = cacheStrategy.response;
17254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
17354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (cacheResponse != null && !responseSource.usesCache()) {
1753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      closeQuietly(cacheResponse.body()); // We don't need this cached response. Close it.
17654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
17754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (responseSource.requiresConnection()) {
1793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // Open a connection unless we inherited one from a redirect.
1803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (connection == null) {
1813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        connect();
1823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
183a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
1843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      transport = (Transport) connection.newTransport(this);
18554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // Create a request body if we don't have one already. We'll already have
1873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // one if we're retrying a failed POST.
1883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (hasRequestBody() && requestBodyOut == null) {
1893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        requestBodyOut = transport.createRequestBody(request);
1903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
19154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
19254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
1933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // We're using a cached response. Recycle a connection we may have inherited from a redirect.
1943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (connection != null) {
1953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        client.getConnectionPool().recycle(connection);
1963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        connection = null;
1973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
19854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // No need for the network! Promote the cached response immediately.
2003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      this.response = validatingResponse;
2013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (validatingResponse.body() != null) {
2023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        initContentStream(validatingResponse.body().source());
2033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
20454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
2053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
20654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Response cacheableResponse() {
2083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Use an unreadable response body when offering the response to the cache.
2093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // The cache isn't allowed to consume the response body bytes!
2103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return response.newBuilder().body(null).build();
21154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
21254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
21354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Connect to the origin server either directly or via a proxy. */
2143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private void connect() throws IOException {
2153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (connection != null) throw new IllegalStateException();
2163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
21754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (routeSelector == null) {
2183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      String uriHost = request.url().getHost();
2193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (uriHost == null || uriHost.length() == 0) {
2203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        throw new UnknownHostException(request.url().toString());
22154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
2227407d6984ce69693097befc9b72609a8156463bbNarayan Kamath      SSLSocketFactory sslSocketFactory = null;
2237407d6984ce69693097befc9b72609a8156463bbNarayan Kamath      HostnameVerifier hostnameVerifier = null;
2243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (request.isHttps()) {
225a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        sslSocketFactory = client.getSslSocketFactory();
226a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        hostnameVerifier = client.getHostnameVerifier();
2277407d6984ce69693097befc9b72609a8156463bbNarayan Kamath      }
2283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      Address address = new Address(uriHost, getEffectivePort(request.url()), sslSocketFactory,
2293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          hostnameVerifier, client.getAuthenticator(), client.getProxy(), client.getProtocols());
2303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      routeSelector = new RouteSelector(address, request.uri(), client.getProxySelector(),
231a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath          client.getConnectionPool(), Dns.DEFAULT, client.getRoutesDatabase());
23254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
2333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = routeSelector.next(request.method());
2353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
23654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (!connection.isConnected()) {
237a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      connection.connect(client.getConnectTimeout(), client.getReadTimeout(), getTunnelConfig());
2383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (connection.isSpdy()) client.getConnectionPool().share(connection);
239a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      client.getRoutesDatabase().connected(connection.getRoute());
2403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } else if (!connection.isSpdy()) {
24176739264ce52fe7a6c5c3558dad87b649118deffNarayan Kamath      connection.updateReadTimeout(client.getReadTimeout());
24254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
24354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    route = connection.getRoute();
24554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
24654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
24754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
24854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Called immediately before the transport transmits HTTP request headers.
24954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * This is used to observe the sent time should the request be cached.
25054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
25154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public void writingRequestHeaders() {
2523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (sentRequestMillis != -1) throw new IllegalStateException();
25354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    sentRequestMillis = System.currentTimeMillis();
25454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
25554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
25654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  boolean hasRequestBody() {
2573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return HttpMethod.hasRequestBody(request.method());
25854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
25954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
26054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Returns the request body or null if this request doesn't have a body. */
2613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final Sink getRequestBody() {
2623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (responseSource == null) throw new IllegalStateException();
26354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return requestBodyOut;
26454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
26554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final BufferedSink getBufferedRequestBody() {
2673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    BufferedSink result = bufferedRequestBody;
2683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (result != null) return result;
2693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Sink requestBody = getRequestBody();
2703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return requestBody != null
2713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        ? (bufferedRequestBody = Okio.buffer(requestBody))
2723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        : null;
2733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
2743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
27554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final boolean hasResponse() {
2763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return response != null;
27754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
27854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final ResponseSource responseSource() {
2803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return responseSource;
28154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
28254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final Request getRequest() {
2843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return request;
28554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
28654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** Returns the engine's response. */
2883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  // TODO: the returned body will always be null.
2893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final Response getResponse() {
2903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (response == null) throw new IllegalStateException();
2913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return response;
29254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
29354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final Source getResponseBody() {
2953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (response == null) throw new IllegalStateException();
2963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return responseBody;
29754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
29854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final InputStream getResponseBodyBytes() {
3003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    InputStream result = responseBodyBytes;
3013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return result != null
3023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        ? result
3033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        : (responseBodyBytes = Okio.buffer(getResponseBody()).inputStream());
30454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
30554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
30654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final Connection getConnection() {
30754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return connection;
30854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
30954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
31054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
3113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Report and attempt to recover from {@code e}. Returns a new HTTP engine
3123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * that should be used for the retry if {@code e} is recoverable, or null if
3133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * the failure is permanent.
31454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
3153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public HttpEngine recover(IOException e) {
3163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (routeSelector != null && connection != null) {
3173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      routeSelector.connectFailed(connection, e);
3183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
3193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
3203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    boolean canRetryRequestBody = requestBodyOut == null || requestBodyOut instanceof RetryableSink;
3213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (routeSelector == null && connection == null // No connection.
3223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        || routeSelector != null && !routeSelector.hasNext() // No more routes to attempt.
3233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        || !isRecoverable(e)
3243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        || !canRetryRequestBody) {
3253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return null;
3263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
3273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
3283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Connection connection = close();
3293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
3303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // For failure recovery, use the same route selector with a new connection.
3313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return new HttpEngine(client, request, bufferRequestBody, connection, routeSelector,
3323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        (RetryableSink) requestBodyOut);
3333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
3343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
3353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private boolean isRecoverable(IOException e) {
3363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // If the problem was a CertificateException from the X509TrustManager,
3373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // do not retry, we didn't have an abrupt server-initiated exception.
3383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    boolean sslFailure =
3393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        e instanceof SSLHandshakeException && e.getCause() instanceof CertificateException;
3403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    boolean protocolFailure = e instanceof ProtocolException;
3413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return !sslFailure && !protocolFailure;
3423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
3433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
3443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
3453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Returns the route used to retrieve the response. Null if we haven't
3463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * connected yet, or if no connection was necessary.
3473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
3483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public Route getRoute() {
3493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return route;
35054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
35154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
35254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void maybeCache() throws IOException {
353a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    OkResponseCache responseCache = client.getOkResponseCache();
354a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    if (responseCache == null) return;
355a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
35654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Should we cache this response for this request?
3573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (!CacheStrategy.isCacheable(response, request)) {
3583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      responseCache.maybeRemove(request);
35954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return;
36054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
36154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
36254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Offer this request to the cache.
3633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    cacheRequest = responseCache.put(cacheableResponse());
36454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
36554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
36654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
3673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Configure the socket connection to be either pooled or closed when it is
3683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * either exhausted or closed. If it is unneeded when this is called, it will
3693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * be released immediately.
37054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
3713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final void releaseConnection() throws IOException {
3723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (transport != null) {
3733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      transport.releaseConnectionOnIdle();
37454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
3753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = null;
37654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
37754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
37854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
3793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Release any resources held by this engine. If a connection is still held by
3803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * this engine, it is returned.
38154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
3823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final Connection close() {
3833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (bufferedRequestBody != null) {
3843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // This also closes the wrapped requestBodyOut.
3853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      closeQuietly(bufferedRequestBody);
3863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } else if (requestBodyOut != null) {
3873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      closeQuietly(requestBodyOut);
388c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
389c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
3903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // If this engine never achieved a response body, its connection cannot be reused.
3913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (responseBody == null) {
3923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      closeQuietly(connection);
3933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      connection = null;
3943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return null;
3953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
396c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
3973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Close the response body. This will recycle the connection if it is eligible.
3983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    closeQuietly(responseBody);
3993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
4003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Clear the buffer held by the response body input stream adapter.
4013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    closeQuietly(responseBodyBytes);
4023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
4033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Close the connection if it cannot be reused.
4043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (transport != null && !transport.canReuseConnection()) {
4053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      closeQuietly(connection);
4063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      connection = null;
4073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return null;
40854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
4093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
4103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Connection result = connection;
4113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = null;
4123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return result;
41354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
414c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
4153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
4163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Initialize the response content stream from the response transfer source.
4173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * These two sources are the same unless we're doing transparent gzip, in
4183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * which case the content source is decompressed.
4193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   *
4203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * <p>Whenever we do transparent gzip we also strip the corresponding headers.
4213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * We strip the Content-Encoding header to prevent the application from
4223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * attempting to double decompress. We strip the Content-Length header because
4233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * it is the length of the compressed content, but the application is only
4243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * interested in the length of the uncompressed content.
4253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   *
4263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * <p>This method should only be used for non-empty response bodies. Response
4273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * codes like "304 Not Modified" can include "Content-Encoding: gzip" without
4283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * a response body and we will crash if we attempt to decompress the zero-byte
4293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * source.
4303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
4313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private void initContentStream(Source transferSource) throws IOException {
4323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    responseTransferSource = transferSource;
4333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (transparentGzip && "gzip".equalsIgnoreCase(response.header("Content-Encoding"))) {
4343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      response = response.newBuilder()
4353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          .removeHeader("Content-Encoding")
4363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          .removeHeader("Content-Length")
4373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          .build();
4383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      responseBody = new GzipSource(transferSource);
43954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
4403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      responseBody = transferSource;
44154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
44254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
44354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
44454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
44554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Returns true if the response must have a (possibly 0-length) body.
44654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * See RFC 2616 section 4.3.
44754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
44854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final boolean hasResponseBody() {
44954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // HEAD requests never yield a body regardless of the response headers.
4503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (request.method().equals("HEAD")) {
45154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return false;
45254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
45354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
4543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    int responseCode = response.code();
45554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if ((responseCode < HTTP_CONTINUE || responseCode >= 200)
4563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && responseCode != HTTP_NO_CONTENT
4573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && responseCode != HTTP_NOT_MODIFIED) {
45854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return true;
45954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
46054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
46154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // If the Content-Length or Transfer-Encoding headers disagree with the
46254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // response code, the response is malformed. For best compatibility, we
46354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // honor the headers.
4643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (OkHeaders.contentLength(response) != -1
4653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        || "chunked".equalsIgnoreCase(response.header("Transfer-Encoding"))) {
46654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return true;
46754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
46854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
46954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return false;
47054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
47154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
47254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
4733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Populates request with defaults and cookies.
47454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   *
47554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * <p>This client doesn't specify a default {@code Accept} header because it
47654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * doesn't know what content types the application is interested in.
47754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
47854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void prepareRawRequestHeaders() throws IOException {
4793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Request.Builder result = request.newBuilder();
48054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
4813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (request.getUserAgent() == null) {
4823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      result.setUserAgent(getDefaultUserAgent());
48354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
48454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
4853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (request.header("Host") == null) {
4863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      result.header("Host", hostHeader(request.url()));
48754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
48854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
48954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if ((connection == null || connection.getHttpMinorVersion() != 0)
4903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && request.header("Connection") == null) {
4913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      result.header("Connection", "Keep-Alive");
49254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
49354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
4943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (request.header("Accept-Encoding") == null) {
49554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      transparentGzip = true;
4963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      result.header("Accept-Encoding", "gzip");
49754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
49854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
4993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (hasRequestBody() && request.header("Content-Type") == null) {
5003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      result.header("Content-Type", "application/x-www-form-urlencoded");
50154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
50254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
503a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    CookieHandler cookieHandler = client.getCookieHandler();
50454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (cookieHandler != null) {
5053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // Capture the request headers added so far so that they can be offered to the CookieHandler.
5063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // This is mostly to stay close to the RI; it is unlikely any of the headers above would
5073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // affect cookie choice besides "Host".
5083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      Map<String, List<String>> headers = OkHeaders.toMultimap(result.build().headers(), null);
50954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
5103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      Map<String, List<String>> cookies = cookieHandler.get(request.uri(), headers);
51154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
5123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // Add any new cookies to the request.
5133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      OkHeaders.addCookies(result, cookies);
514c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
515c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
5163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    request = result.build();
51754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
51854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
51954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public static String getDefaultUserAgent() {
52054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String agent = System.getProperty("http.agent");
52154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return agent != null ? agent : ("Java" + System.getProperty("java.version"));
52254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
52354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
5243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public static String hostHeader(URL url) {
5253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return getEffectivePort(url) != getDefaultPort(url.getProtocol())
5263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        ? url.getHost() + ":" + url.getPort()
5273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        : url.getHost();
52854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
52954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
53054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
53154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Flushes the remaining request header and body, parses the HTTP response
53254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * headers and starts reading the HTTP response body if it exists.
53354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
53454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final void readResponse() throws IOException {
5353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (response != null) return;
5363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (responseSource == null) throw new IllegalStateException("call sendRequest() first!");
5373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (!responseSource.requiresConnection()) return;
53854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
5393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Flush the request body if there's data outstanding.
5403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (bufferedRequestBody != null && bufferedRequestBody.buffer().size() > 0) {
5413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      bufferedRequestBody.flush();
54254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
54354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
54454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (sentRequestMillis == -1) {
5453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (OkHeaders.contentLength(request) == -1 && requestBodyOut instanceof RetryableSink) {
5463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        // We might not learn the Content-Length until the request body has been buffered.
5473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        long contentLength = ((RetryableSink) requestBodyOut).contentLength();
5483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        request = request.newBuilder()
5493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            .header("Content-Length", Long.toString(contentLength))
5503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            .build();
55154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
5523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      transport.writeRequestHeaders(request);
55354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
55454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
55554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (requestBodyOut != null) {
5563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (bufferedRequestBody != null) {
5573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        // This also closes the wrapped requestBodyOut.
5583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        bufferedRequestBody.close();
5593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      } else {
5603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        requestBodyOut.close();
5613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
5623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (requestBodyOut instanceof RetryableSink) {
5633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        transport.writeRequestBody((RetryableSink) requestBodyOut);
56454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
56554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
56654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
56754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transport.flushRequest();
56854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
5693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    response = transport.readResponseHeaders()
5703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .request(request)
5713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .handshake(connection.getHandshake())
5723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .header(OkHeaders.SENT_MILLIS, Long.toString(sentRequestMillis))
5733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .header(OkHeaders.RECEIVED_MILLIS, Long.toString(System.currentTimeMillis()))
5743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .setResponseSource(responseSource)
5753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .build();
5763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.setHttpMinorVersion(response.httpMinorVersion());
5773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    receiveHeaders(response.headers());
57854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
57954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
5803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (validatingResponse.validate(response)) {
5813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        transport.emptyTransferStream();
5823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        releaseConnection();
5833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        response = combine(validatingResponse, response);
5843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
5853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        // Update the cache after combining headers but before stripping the
5863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        // Content-Encoding header (as performed by initContentStream()).
587a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        OkResponseCache responseCache = client.getOkResponseCache();
588a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        responseCache.trackConditionalCacheHit();
5893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        responseCache.update(validatingResponse, cacheableResponse());
590166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
5913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (validatingResponse.body() != null) {
5923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          initContentStream(validatingResponse.body().source());
5933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
59454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return;
59554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } else {
5963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        closeQuietly(validatingResponse.body());
59754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
598c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
599c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
6003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (!hasResponseBody()) {
6013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // Don't call initContentStream() when the response doesn't have any content.
6023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      responseTransferSource = transport.getTransferStream(cacheRequest);
6033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      responseBody = responseTransferSource;
6043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return;
605c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
606c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
6073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    maybeCache();
60854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    initContentStream(transport.getTransferStream(cacheRequest));
60954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
610c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
6113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
6123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Combines cached headers with a network headers as defined by RFC 2616,
6133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * 13.5.3.
6143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
6153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private static Response combine(Response cached, Response network) throws IOException {
6163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Headers.Builder result = new Headers.Builder();
6173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
6183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Headers cachedHeaders = cached.headers();
6193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    for (int i = 0; i < cachedHeaders.size(); i++) {
6203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      String fieldName = cachedHeaders.name(i);
6213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      String value = cachedHeaders.value(i);
6223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if ("Warning".equals(fieldName) && value.startsWith("1")) {
6233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        continue; // drop 100-level freshness warnings
6243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
6253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (!isEndToEnd(fieldName) || network.header(fieldName) == null) {
6263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        result.add(fieldName, value);
6273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
6283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
6293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
6303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Headers networkHeaders = network.headers();
6313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    for (int i = 0; i < networkHeaders.size(); i++) {
6323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      String fieldName = networkHeaders.name(i);
6333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (isEndToEnd(fieldName)) {
6343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        result.add(fieldName, networkHeaders.value(i));
6353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
6363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
6373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
6383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return cached.newBuilder().headers(result.build()).build();
6393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
6403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
6413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
6423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Returns true if {@code fieldName} is an end-to-end HTTP header, as
6433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * defined by RFC 2616, 13.5.1.
6443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
6453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private static boolean isEndToEnd(String fieldName) {
6463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return !"Connection".equalsIgnoreCase(fieldName)
6473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && !"Keep-Alive".equalsIgnoreCase(fieldName)
6483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && !"Proxy-Authenticate".equalsIgnoreCase(fieldName)
6493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && !"Proxy-Authorization".equalsIgnoreCase(fieldName)
6503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && !"TE".equalsIgnoreCase(fieldName)
6513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && !"Trailers".equalsIgnoreCase(fieldName)
6523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && !"Transfer-Encoding".equalsIgnoreCase(fieldName)
6533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && !"Upgrade".equalsIgnoreCase(fieldName);
6543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
6553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
6563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private TunnelRequest getTunnelConfig() {
6573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (!request.isHttps()) return null;
6583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
6593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String userAgent = request.getUserAgent();
6603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (userAgent == null) userAgent = getDefaultUserAgent();
6613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
6623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    URL url = request.url();
6633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return new TunnelRequest(url.getHost(), getEffectivePort(url), userAgent,
6643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        request.getProxyAuthorization());
66554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
6667899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
6673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public void receiveHeaders(Headers headers) throws IOException {
668a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    CookieHandler cookieHandler = client.getCookieHandler();
66954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (cookieHandler != null) {
6703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      cookieHandler.put(request.uri(), OkHeaders.toMultimap(headers, null));
6717899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
67254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
673c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath}
674