HttpEngine.java revision c6bd683320121544811f481709b3fdbcbe9b3866
1c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/*
2c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  Licensed to the Apache Software Foundation (ASF) under one or more
3c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  contributor license agreements.  See the NOTICE file distributed with
4c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  this work for additional information regarding copyright ownership.
5c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  The ASF licenses this file to You under the Apache License, Version 2.0
6c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  (the "License"); you may not use this file except in compliance with
7c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  the License.  You may obtain a copy of the License at
8c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
9c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *     http://www.apache.org/licenses/LICENSE-2.0
10c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
11c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  Unless required by applicable law or agreed to in writing, software
12c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  distributed under the License is distributed on an "AS IS" BASIS,
13c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  See the License for the specific language governing permissions and
15c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *  limitations under the License.
16c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */
17c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
182231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonpackage com.squareup.okhttp.internal.http;
192231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
202231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.Address;
212231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.Connection;
223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Headers;
23a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport com.squareup.okhttp.OkHttpClient;
24a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport com.squareup.okhttp.OkResponseCache;
253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Request;
263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Response;
272231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.ResponseSource;
283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Route;
292231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.TunnelRequest;
3054cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport com.squareup.okhttp.internal.Dns;
31c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.IOException;
32c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.InputStream;
33c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CacheRequest;
34c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CookieHandler;
353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.net.ProtocolException;
36c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URL;
372231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.UnknownHostException;
383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.security.cert.CertificateException;
39c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.List;
40c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Map;
417899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport javax.net.ssl.HostnameVerifier;
423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport javax.net.ssl.SSLHandshakeException;
43c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport javax.net.ssl.SSLSocketFactory;
443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.BufferedSink;
453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.GzipSource;
463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Okio;
473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Sink;
483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Source;
49c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.Util.closeQuietly;
5154cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport static com.squareup.okhttp.internal.Util.getDefaultPort;
5254cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport static com.squareup.okhttp.internal.Util.getEffectivePort;
533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.http.StatusLine.HTTP_CONTINUE;
543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static java.net.HttpURLConnection.HTTP_NOT_MODIFIED;
553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static java.net.HttpURLConnection.HTTP_NO_CONTENT;
5654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
57c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/**
58c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Handles a single HTTP request/response pair. Each HTTP engine follows this
59c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * lifecycle:
60c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <ol>
6154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * <li>It is created.
6254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * <li>The HTTP request message is sent with sendRequest(). Once the request
6354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * is sent it is an error to modify the request headers. After
6454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * sendRequest() has been called the request body can be written to if
6554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * it exists.
6654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * <li>The HTTP response message is read with readResponse(). After the
6754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * response has been read the response headers and body can be read.
6854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * All responses have a response body input stream, though in some
6954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * instances this stream is empty.
70c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * </ol>
71c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
72c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>The request and response may be served by the HTTP response cache, by the
73c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * network, or by both in the event of a conditional GET.
74c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */
75c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathpublic class HttpEngine {
763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  final OkHttpClient client;
7754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Connection connection;
793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private RouteSelector routeSelector;
803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Route route;
8154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
8254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private Transport transport;
8354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
8454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** The time when the request headers were written, or -1 if they haven't been written yet. */
8554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  long sentRequestMillis = -1;
8654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
8754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
8854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * True if this client added an "Accept-Encoding: gzip" header field and is
8954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * therefore responsible for also decompressing the transfer stream.
9054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
9154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private boolean transparentGzip;
9254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * True if the request body must be completely buffered before transmission;
953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * false if it can be streamed. Buffering has two advantages: we don't need
963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * the content-length in advance and we can retransmit if necessary. The
973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * upside of streaming is that we can save memory.
983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final boolean bufferRequestBody;
10054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
101c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller  private Request originalRequest;
1023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Request request;
1033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Sink requestBodyOut;
1043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private BufferedSink bufferedRequestBody;
10554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private ResponseSource responseSource;
10754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** Null until a response is received from the network or the cache. */
1093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Response response;
1103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Source responseTransferSource;
1113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Source responseBody;
1123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private InputStream responseBodyBytes;
11354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
11454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
1153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * The cache response currently being validated on a conditional get. Null
1163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * if the cached response doesn't exist or doesn't need validation. If the
1173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * conditional get succeeds, these will be used for the response. If it fails,
1183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * it will be set to null.
11954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
1203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Response validatingResponse;
12154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** The cache request currently being populated from a network response. */
1233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private CacheRequest cacheRequest;
12454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
12554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
1263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * @param request the HTTP request without a body. The body must be
1273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   *     written via the engine's request body stream.
12854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @param connection the connection used for an intermediate response
129a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath   *     immediately prior to this request/response pair, such as a same-host
130a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath   *     redirect. This engine assumes ownership of the connection and must
131a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath   *     release it when it is unneeded.
1323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * @param routeSelector the route selector used for a failed attempt
1333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   *     immediately preceding this attempt, or null if this request doesn't
1343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   *     recover from a failure.
13554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
1363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public HttpEngine(OkHttpClient client, Request request, boolean bufferRequestBody,
1373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      Connection connection, RouteSelector routeSelector, RetryableSink requestBodyOut) {
138a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    this.client = client;
139c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    this.originalRequest = request;
1403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.request = request;
1413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.bufferRequestBody = bufferRequestBody;
14254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this.connection = connection;
1433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.routeSelector = routeSelector;
1443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.route = connection != null ? connection.getRoute() : null;
14554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this.requestBodyOut = requestBodyOut;
14654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
14754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
14854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
14954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Figures out what the response source will be, and opens a socket to that
15054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * source if necessary. Prepares the request headers and gets ready to start
15154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * writing the request body if it exists.
15254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
15354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final void sendRequest() throws IOException {
1543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (responseSource != null) return; // Already sent.
1553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (transport != null) throw new IllegalStateException();
15654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
15754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    prepareRawRequestHeaders();
158a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    OkResponseCache responseCache = client.getOkResponseCache();
1593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Response cacheResponse = responseCache != null
1613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        ? responseCache.get(request)
1623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        : null;
1633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    long now = System.currentTimeMillis();
1643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    CacheStrategy cacheStrategy = new CacheStrategy.Factory(now, request, cacheResponse).get();
1653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    responseSource = cacheStrategy.source;
1663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    request = cacheStrategy.request;
1673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
168a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    if (responseCache != null) {
169a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      responseCache.trackResponse(responseSource);
17054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
17154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (responseSource != ResponseSource.NETWORK) {
1733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      validatingResponse = cacheStrategy.response;
17454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
17554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (cacheResponse != null && !responseSource.usesCache()) {
1773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      closeQuietly(cacheResponse.body()); // We don't need this cached response. Close it.
17854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
17954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (responseSource.requiresConnection()) {
1813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // Open a connection unless we inherited one from a redirect.
1823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (connection == null) {
1833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        connect();
1843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
185a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
1863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      transport = (Transport) connection.newTransport(this);
18754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // Create a request body if we don't have one already. We'll already have
1893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // one if we're retrying a failed POST.
1903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (hasRequestBody() && requestBodyOut == null) {
1913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        requestBodyOut = transport.createRequestBody(request);
1923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
19354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
19454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
1953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // We're using a cached response. Recycle a connection we may have inherited from a redirect.
1963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (connection != null) {
1973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        client.getConnectionPool().recycle(connection);
1983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        connection = null;
1993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
20054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // No need for the network! Promote the cached response immediately.
2023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      this.response = validatingResponse;
2033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (validatingResponse.body() != null) {
2043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        initContentStream(validatingResponse.body().source());
2053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
20654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
2073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
20854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Response cacheableResponse() {
2103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Use an unreadable response body when offering the response to the cache.
2113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // The cache isn't allowed to consume the response body bytes!
2123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return response.newBuilder().body(null).build();
21354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
21454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
21554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Connect to the origin server either directly or via a proxy. */
2163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private void connect() throws IOException {
2173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (connection != null) throw new IllegalStateException();
2183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
21954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (routeSelector == null) {
2203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      String uriHost = request.url().getHost();
2213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (uriHost == null || uriHost.length() == 0) {
2223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        throw new UnknownHostException(request.url().toString());
22354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
2247407d6984ce69693097befc9b72609a8156463bbNarayan Kamath      SSLSocketFactory sslSocketFactory = null;
2257407d6984ce69693097befc9b72609a8156463bbNarayan Kamath      HostnameVerifier hostnameVerifier = null;
2263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (request.isHttps()) {
227a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        sslSocketFactory = client.getSslSocketFactory();
228a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        hostnameVerifier = client.getHostnameVerifier();
2297407d6984ce69693097befc9b72609a8156463bbNarayan Kamath      }
2303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      Address address = new Address(uriHost, getEffectivePort(request.url()), sslSocketFactory,
2313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          hostnameVerifier, client.getAuthenticator(), client.getProxy(), client.getProtocols());
2323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      routeSelector = new RouteSelector(address, request.uri(), client.getProxySelector(),
233a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath          client.getConnectionPool(), Dns.DEFAULT, client.getRoutesDatabase());
23454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
2353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = routeSelector.next(request.method());
2373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
23854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (!connection.isConnected()) {
239a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      connection.connect(client.getConnectTimeout(), client.getReadTimeout(), getTunnelConfig());
2403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (connection.isSpdy()) client.getConnectionPool().share(connection);
241a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      client.getRoutesDatabase().connected(connection.getRoute());
2423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } else if (!connection.isSpdy()) {
24376739264ce52fe7a6c5c3558dad87b649118deffNarayan Kamath      connection.updateReadTimeout(client.getReadTimeout());
24454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
24554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    route = connection.getRoute();
24754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
24854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
24954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
25054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Called immediately before the transport transmits HTTP request headers.
25154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * This is used to observe the sent time should the request be cached.
25254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
25354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public void writingRequestHeaders() {
2543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (sentRequestMillis != -1) throw new IllegalStateException();
25554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    sentRequestMillis = System.currentTimeMillis();
25654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
25754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
25854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  boolean hasRequestBody() {
2593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return HttpMethod.hasRequestBody(request.method());
26054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
26154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
26254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Returns the request body or null if this request doesn't have a body. */
2633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final Sink getRequestBody() {
2643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (responseSource == null) throw new IllegalStateException();
26554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return requestBodyOut;
26654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
26754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final BufferedSink getBufferedRequestBody() {
2693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    BufferedSink result = bufferedRequestBody;
2703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (result != null) return result;
2713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Sink requestBody = getRequestBody();
2723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return requestBody != null
2733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        ? (bufferedRequestBody = Okio.buffer(requestBody))
2743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        : null;
2753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
2763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
27754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final boolean hasResponse() {
2783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return response != null;
27954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
28054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final ResponseSource responseSource() {
2823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return responseSource;
28354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
28454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final Request getRequest() {
2863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return request;
28754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
28854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** Returns the engine's response. */
2903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  // TODO: the returned body will always be null.
2913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final Response getResponse() {
2923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (response == null) throw new IllegalStateException();
2933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return response;
29454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
29554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
2963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final Source getResponseBody() {
2973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (response == null) throw new IllegalStateException();
2983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return responseBody;
29954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
30054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
3013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final InputStream getResponseBodyBytes() {
3023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    InputStream result = responseBodyBytes;
3033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return result != null
3043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        ? result
3053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        : (responseBodyBytes = Okio.buffer(getResponseBody()).inputStream());
30654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
30754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
30854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final Connection getConnection() {
30954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return connection;
31054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
31154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
31254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
3133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Report and attempt to recover from {@code e}. Returns a new HTTP engine
3143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * that should be used for the retry if {@code e} is recoverable, or null if
3153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * the failure is permanent.
31654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
3173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public HttpEngine recover(IOException e) {
3183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (routeSelector != null && connection != null) {
3193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      routeSelector.connectFailed(connection, e);
3203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
3213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
3223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    boolean canRetryRequestBody = requestBodyOut == null || requestBodyOut instanceof RetryableSink;
3233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (routeSelector == null && connection == null // No connection.
3243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        || routeSelector != null && !routeSelector.hasNext() // No more routes to attempt.
3253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        || !isRecoverable(e)
3263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        || !canRetryRequestBody) {
3273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return null;
3283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
3293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
3303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Connection connection = close();
3313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
3323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // For failure recovery, use the same route selector with a new connection.
333c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    return new HttpEngine(client, originalRequest, bufferRequestBody, connection, routeSelector,
3343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        (RetryableSink) requestBodyOut);
3353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
3363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
3373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private boolean isRecoverable(IOException e) {
3383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // If the problem was a CertificateException from the X509TrustManager,
3393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // do not retry, we didn't have an abrupt server-initiated exception.
3403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    boolean sslFailure =
3413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        e instanceof SSLHandshakeException && e.getCause() instanceof CertificateException;
3423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    boolean protocolFailure = e instanceof ProtocolException;
3433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return !sslFailure && !protocolFailure;
3443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
3453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
3463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
3473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Returns the route used to retrieve the response. Null if we haven't
3483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * connected yet, or if no connection was necessary.
3493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
3503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public Route getRoute() {
3513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return route;
35254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
35354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
35454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void maybeCache() throws IOException {
355a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    OkResponseCache responseCache = client.getOkResponseCache();
356a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    if (responseCache == null) return;
357a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
35854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Should we cache this response for this request?
3593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (!CacheStrategy.isCacheable(response, request)) {
3603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      responseCache.maybeRemove(request);
36154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return;
36254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
36354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
36454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Offer this request to the cache.
3653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    cacheRequest = responseCache.put(cacheableResponse());
36654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
36754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
36854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
3693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Configure the socket connection to be either pooled or closed when it is
3703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * either exhausted or closed. If it is unneeded when this is called, it will
3713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * be released immediately.
37254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
3733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final void releaseConnection() throws IOException {
374c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller    if (transport != null && connection != null) {
3753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      transport.releaseConnectionOnIdle();
37654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
3773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = null;
37854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
37954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
38054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
3813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Release any resources held by this engine. If a connection is still held by
3823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * this engine, it is returned.
38354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
3843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public final Connection close() {
3853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (bufferedRequestBody != null) {
3863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // This also closes the wrapped requestBodyOut.
3873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      closeQuietly(bufferedRequestBody);
3883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } else if (requestBodyOut != null) {
3893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      closeQuietly(requestBodyOut);
390c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
391c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
3923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // If this engine never achieved a response body, its connection cannot be reused.
3933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (responseBody == null) {
3943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      closeQuietly(connection);
3953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      connection = null;
3963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return null;
3973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
398c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
3993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Close the response body. This will recycle the connection if it is eligible.
4003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    closeQuietly(responseBody);
4013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
4023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Clear the buffer held by the response body input stream adapter.
4033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    closeQuietly(responseBodyBytes);
4043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
4053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Close the connection if it cannot be reused.
4063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (transport != null && !transport.canReuseConnection()) {
4073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      closeQuietly(connection);
4083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      connection = null;
4093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return null;
41054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
4113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
4123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Connection result = connection;
4133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection = null;
4143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return result;
41554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
416c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
4173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
4183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Initialize the response content stream from the response transfer source.
4193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * These two sources are the same unless we're doing transparent gzip, in
4203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * which case the content source is decompressed.
4213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   *
4223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * <p>Whenever we do transparent gzip we also strip the corresponding headers.
4233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * We strip the Content-Encoding header to prevent the application from
4243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * attempting to double decompress. We strip the Content-Length header because
4253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * it is the length of the compressed content, but the application is only
4263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * interested in the length of the uncompressed content.
4273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   *
4283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * <p>This method should only be used for non-empty response bodies. Response
4293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * codes like "304 Not Modified" can include "Content-Encoding: gzip" without
4303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * a response body and we will crash if we attempt to decompress the zero-byte
4313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * source.
4323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
4333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private void initContentStream(Source transferSource) throws IOException {
4343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    responseTransferSource = transferSource;
4353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (transparentGzip && "gzip".equalsIgnoreCase(response.header("Content-Encoding"))) {
4363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      response = response.newBuilder()
4373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          .removeHeader("Content-Encoding")
4383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          .removeHeader("Content-Length")
4393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          .build();
4403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      responseBody = new GzipSource(transferSource);
44154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
4423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      responseBody = transferSource;
44354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
44454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
44554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
44654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
44754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Returns true if the response must have a (possibly 0-length) body.
44854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * See RFC 2616 section 4.3.
44954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
45054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final boolean hasResponseBody() {
45154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // HEAD requests never yield a body regardless of the response headers.
4523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (request.method().equals("HEAD")) {
45354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return false;
45454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
45554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
4563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    int responseCode = response.code();
45754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if ((responseCode < HTTP_CONTINUE || responseCode >= 200)
4583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && responseCode != HTTP_NO_CONTENT
4593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && responseCode != HTTP_NOT_MODIFIED) {
46054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return true;
46154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
46254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
46354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // If the Content-Length or Transfer-Encoding headers disagree with the
46454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // response code, the response is malformed. For best compatibility, we
46554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // honor the headers.
4663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (OkHeaders.contentLength(response) != -1
4673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        || "chunked".equalsIgnoreCase(response.header("Transfer-Encoding"))) {
46854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return true;
46954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
47054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
47154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return false;
47254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
47354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
47454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
4753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Populates request with defaults and cookies.
47654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   *
47754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * <p>This client doesn't specify a default {@code Accept} header because it
47854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * doesn't know what content types the application is interested in.
47954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
48054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void prepareRawRequestHeaders() throws IOException {
4813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Request.Builder result = request.newBuilder();
48254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
4833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (request.getUserAgent() == null) {
4843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      result.setUserAgent(getDefaultUserAgent());
48554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
48654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
4873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (request.header("Host") == null) {
4883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      result.header("Host", hostHeader(request.url()));
48954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
49054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
49154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if ((connection == null || connection.getHttpMinorVersion() != 0)
4923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && request.header("Connection") == null) {
4933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      result.header("Connection", "Keep-Alive");
49454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
49554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
4963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (request.header("Accept-Encoding") == null) {
49754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      transparentGzip = true;
4983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      result.header("Accept-Encoding", "gzip");
49954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
50054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
5013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (hasRequestBody() && request.header("Content-Type") == null) {
5023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      result.header("Content-Type", "application/x-www-form-urlencoded");
50354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
50454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
505a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    CookieHandler cookieHandler = client.getCookieHandler();
50654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (cookieHandler != null) {
5073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // Capture the request headers added so far so that they can be offered to the CookieHandler.
5083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // This is mostly to stay close to the RI; it is unlikely any of the headers above would
5093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // affect cookie choice besides "Host".
5103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      Map<String, List<String>> headers = OkHeaders.toMultimap(result.build().headers(), null);
51154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
5123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      Map<String, List<String>> cookies = cookieHandler.get(request.uri(), headers);
51354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
5143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // Add any new cookies to the request.
5153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      OkHeaders.addCookies(result, cookies);
516c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
517c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
5183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    request = result.build();
51954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
52054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
52154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public static String getDefaultUserAgent() {
52254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String agent = System.getProperty("http.agent");
52354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return agent != null ? agent : ("Java" + System.getProperty("java.version"));
52454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
52554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
5263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public static String hostHeader(URL url) {
5273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return getEffectivePort(url) != getDefaultPort(url.getProtocol())
5283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        ? url.getHost() + ":" + url.getPort()
5293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        : url.getHost();
53054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
53154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
53254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
53354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Flushes the remaining request header and body, parses the HTTP response
53454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * headers and starts reading the HTTP response body if it exists.
53554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
53654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final void readResponse() throws IOException {
5373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (response != null) return;
5383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (responseSource == null) throw new IllegalStateException("call sendRequest() first!");
5393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (!responseSource.requiresConnection()) return;
54054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
5413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Flush the request body if there's data outstanding.
5423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (bufferedRequestBody != null && bufferedRequestBody.buffer().size() > 0) {
5433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      bufferedRequestBody.flush();
54454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
54554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
54654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (sentRequestMillis == -1) {
5473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (OkHeaders.contentLength(request) == -1 && requestBodyOut instanceof RetryableSink) {
5483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        // We might not learn the Content-Length until the request body has been buffered.
5493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        long contentLength = ((RetryableSink) requestBodyOut).contentLength();
5503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        request = request.newBuilder()
5513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            .header("Content-Length", Long.toString(contentLength))
5523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            .build();
55354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
5543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      transport.writeRequestHeaders(request);
55554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
55654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
55754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (requestBodyOut != null) {
5583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (bufferedRequestBody != null) {
5593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        // This also closes the wrapped requestBodyOut.
5603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        bufferedRequestBody.close();
5613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      } else {
5623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        requestBodyOut.close();
5633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
5643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (requestBodyOut instanceof RetryableSink) {
5653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        transport.writeRequestBody((RetryableSink) requestBodyOut);
56654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
56754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
56854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
56954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transport.flushRequest();
57054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
5713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    response = transport.readResponseHeaders()
5723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .request(request)
5733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .handshake(connection.getHandshake())
5743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .header(OkHeaders.SENT_MILLIS, Long.toString(sentRequestMillis))
5753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .header(OkHeaders.RECEIVED_MILLIS, Long.toString(System.currentTimeMillis()))
5763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .setResponseSource(responseSource)
5773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        .build();
5783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    connection.setHttpMinorVersion(response.httpMinorVersion());
5793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    receiveHeaders(response.headers());
58054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
58154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
5823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (validatingResponse.validate(response)) {
5833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        transport.emptyTransferStream();
5843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        releaseConnection();
5853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        response = combine(validatingResponse, response);
5863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
5873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        // Update the cache after combining headers but before stripping the
5883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        // Content-Encoding header (as performed by initContentStream()).
589a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        OkResponseCache responseCache = client.getOkResponseCache();
590a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        responseCache.trackConditionalCacheHit();
5913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        responseCache.update(validatingResponse, cacheableResponse());
592166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath
5933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (validatingResponse.body() != null) {
5943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          initContentStream(validatingResponse.body().source());
5953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
59654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return;
59754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } else {
5983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        closeQuietly(validatingResponse.body());
59954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
600c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
601c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
6023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (!hasResponseBody()) {
6033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      // Don't call initContentStream() when the response doesn't have any content.
6043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      responseTransferSource = transport.getTransferStream(cacheRequest);
6053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      responseBody = responseTransferSource;
6063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return;
607c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
608c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
6093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    maybeCache();
61054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    initContentStream(transport.getTransferStream(cacheRequest));
61154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
612c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
6133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
6143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Combines cached headers with a network headers as defined by RFC 2616,
6153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * 13.5.3.
6163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
6173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private static Response combine(Response cached, Response network) throws IOException {
6183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Headers.Builder result = new Headers.Builder();
6193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
6203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Headers cachedHeaders = cached.headers();
6213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    for (int i = 0; i < cachedHeaders.size(); i++) {
6223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      String fieldName = cachedHeaders.name(i);
6233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      String value = cachedHeaders.value(i);
6243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if ("Warning".equals(fieldName) && value.startsWith("1")) {
6253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        continue; // drop 100-level freshness warnings
6263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
6273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (!isEndToEnd(fieldName) || network.header(fieldName) == null) {
6283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        result.add(fieldName, value);
6293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
6303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
6313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
6323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Headers networkHeaders = network.headers();
6333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    for (int i = 0; i < networkHeaders.size(); i++) {
6343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      String fieldName = networkHeaders.name(i);
6353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (isEndToEnd(fieldName)) {
6363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        result.add(fieldName, networkHeaders.value(i));
6373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
6383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
6393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
6403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return cached.newBuilder().headers(result.build()).build();
6413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
6423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
6433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
6443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Returns true if {@code fieldName} is an end-to-end HTTP header, as
6453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * defined by RFC 2616, 13.5.1.
6463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
6473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private static boolean isEndToEnd(String fieldName) {
6483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return !"Connection".equalsIgnoreCase(fieldName)
6493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && !"Keep-Alive".equalsIgnoreCase(fieldName)
6503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && !"Proxy-Authenticate".equalsIgnoreCase(fieldName)
6513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && !"Proxy-Authorization".equalsIgnoreCase(fieldName)
6523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && !"TE".equalsIgnoreCase(fieldName)
6533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && !"Trailers".equalsIgnoreCase(fieldName)
6543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && !"Transfer-Encoding".equalsIgnoreCase(fieldName)
6553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && !"Upgrade".equalsIgnoreCase(fieldName);
6563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
6573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
6583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private TunnelRequest getTunnelConfig() {
6593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (!request.isHttps()) return null;
6603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
6613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String userAgent = request.getUserAgent();
6623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (userAgent == null) userAgent = getDefaultUserAgent();
6633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
6643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    URL url = request.url();
6653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return new TunnelRequest(url.getHost(), getEffectivePort(url), userAgent,
6663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        request.getProxyAuthorization());
66754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
6687899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
6693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public void receiveHeaders(Headers headers) throws IOException {
670a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    CookieHandler cookieHandler = client.getCookieHandler();
67154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (cookieHandler != null) {
6723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      cookieHandler.put(request.uri(), OkHeaders.toMultimap(headers, null));
6737899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
67454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
675c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath}
676