Home | History | Annotate | Download | only in http
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package com.squareup.okhttp.internal.http;
     19 
     20 import com.squareup.okhttp.Address;
     21 import com.squareup.okhttp.CertificatePinner;
     22 import com.squareup.okhttp.Connection;
     23 import com.squareup.okhttp.ConnectionPool;
     24 import com.squareup.okhttp.Headers;
     25 import com.squareup.okhttp.Interceptor;
     26 import com.squareup.okhttp.MediaType;
     27 import com.squareup.okhttp.OkHttpClient;
     28 import com.squareup.okhttp.Protocol;
     29 import com.squareup.okhttp.Request;
     30 import com.squareup.okhttp.Response;
     31 import com.squareup.okhttp.ResponseBody;
     32 import com.squareup.okhttp.Route;
     33 import com.squareup.okhttp.internal.Internal;
     34 import com.squareup.okhttp.internal.InternalCache;
     35 import com.squareup.okhttp.internal.Util;
     36 import com.squareup.okhttp.internal.Version;
     37 import java.io.IOException;
     38 import java.io.InterruptedIOException;
     39 import java.net.CookieHandler;
     40 import java.net.ProtocolException;
     41 import java.net.Proxy;
     42 import java.net.URL;
     43 import java.net.UnknownHostException;
     44 import java.security.cert.CertificateException;
     45 import java.util.Date;
     46 import java.util.List;
     47 import java.util.Map;
     48 import javax.net.ssl.HostnameVerifier;
     49 import javax.net.ssl.SSLHandshakeException;
     50 import javax.net.ssl.SSLPeerUnverifiedException;
     51 import javax.net.ssl.SSLSocketFactory;
     52 import okio.Buffer;
     53 import okio.BufferedSink;
     54 import okio.BufferedSource;
     55 import okio.GzipSource;
     56 import okio.Okio;
     57 import okio.Sink;
     58 import okio.Source;
     59 import okio.Timeout;
     60 
     61 import static com.squareup.okhttp.internal.Util.closeQuietly;
     62 import static com.squareup.okhttp.internal.Util.getDefaultPort;
     63 import static com.squareup.okhttp.internal.Util.getEffectivePort;
     64 import static com.squareup.okhttp.internal.http.StatusLine.HTTP_CONTINUE;
     65 import static com.squareup.okhttp.internal.http.StatusLine.HTTP_PERM_REDIRECT;
     66 import static com.squareup.okhttp.internal.http.StatusLine.HTTP_TEMP_REDIRECT;
     67 import static java.net.HttpURLConnection.HTTP_MOVED_PERM;
     68 import static java.net.HttpURLConnection.HTTP_MOVED_TEMP;
     69 import static java.net.HttpURLConnection.HTTP_MULT_CHOICE;
     70 import static java.net.HttpURLConnection.HTTP_NOT_MODIFIED;
     71 import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
     72 import static java.net.HttpURLConnection.HTTP_PROXY_AUTH;
     73 import static java.net.HttpURLConnection.HTTP_SEE_OTHER;
     74 import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
     75 import static java.util.concurrent.TimeUnit.MILLISECONDS;
     76 
     77 /**
     78  * Handles a single HTTP request/response pair. Each HTTP engine follows this
     79  * lifecycle:
     80  * <ol>
     81  * <li>It is created.
     82  * <li>The HTTP request message is sent with sendRequest(). Once the request
     83  * is sent it is an error to modify the request headers. After
     84  * sendRequest() has been called the request body can be written to if
     85  * it exists.
     86  * <li>The HTTP response message is read with readResponse(). After the
     87  * response has been read the response headers and body can be read.
     88  * All responses have a response body input stream, though in some
     89  * instances this stream is empty.
     90  * </ol>
     91  *
     92  * <p>The request and response may be served by the HTTP response cache, by the
     93  * network, or by both in the event of a conditional GET.
     94  */
     95 public final class HttpEngine {
     96   /**
     97    * How many redirects and auth challenges should we attempt? Chrome follows 21 redirects; Firefox,
     98    * curl, and wget follow 20; Safari follows 16; and HTTP/1.0 recommends 5.
     99    */
    100   public static final int MAX_FOLLOW_UPS = 20;
    101 
    102   private static final ResponseBody EMPTY_BODY = new ResponseBody() {
    103     @Override public MediaType contentType() {
    104       return null;
    105     }
    106     @Override public long contentLength() {
    107       return 0;
    108     }
    109     @Override public BufferedSource source() {
    110       return new Buffer();
    111     }
    112   };
    113 
    114   final OkHttpClient client;
    115 
    116   private Connection connection;
    117   private Address address;
    118   private RouteSelector routeSelector;
    119   private Route route;
    120   private final Response priorResponse;
    121 
    122   private Transport transport;
    123 
    124   /** The time when the request headers were written, or -1 if they haven't been written yet. */
    125   long sentRequestMillis = -1;
    126 
    127   /**
    128    * True if this client added an "Accept-Encoding: gzip" header field and is
    129    * therefore responsible for also decompressing the transfer stream.
    130    */
    131   private boolean transparentGzip;
    132 
    133   /**
    134    * True if the request body must be completely buffered before transmission;
    135    * false if it can be streamed. Buffering has two advantages: we don't need
    136    * the content-length in advance and we can retransmit if necessary. The
    137    * upside of streaming is that we can save memory.
    138    */
    139   public final boolean bufferRequestBody;
    140 
    141   /**
    142    * The original application-provided request. Never modified by OkHttp. When
    143    * follow-up requests are necessary, they are derived from this request.
    144    */
    145   private final Request userRequest;
    146 
    147   /**
    148    * The request to send on the network, or null for no network request. This is
    149    * derived from the user request, and customized to support OkHttp features
    150    * like compression and caching.
    151    */
    152   private Request networkRequest;
    153 
    154   /**
    155    * The cached response, or null if the cache doesn't exist or cannot be used
    156    * for this request. Conditional caching means this may be non-null even when
    157    * the network request is non-null. Never modified by OkHttp.
    158    */
    159   private Response cacheResponse;
    160 
    161   /**
    162    * The user-visible response. This is derived from either the network
    163    * response, cache response, or both. It is customized to support OkHttp
    164    * features like compression and caching.
    165    */
    166   private Response userResponse;
    167 
    168   private Sink requestBodyOut;
    169   private BufferedSink bufferedRequestBody;
    170   private final boolean callerWritesRequestBody;
    171   private final boolean forWebSocket;
    172 
    173   /** The cache request currently being populated from a network response. */
    174   private CacheRequest storeRequest;
    175   private CacheStrategy cacheStrategy;
    176 
    177   /**
    178    * @param request the HTTP request without a body. The body must be written via the engine's
    179    *     request body stream.
    180    * @param callerWritesRequestBody true for the {@code HttpURLConnection}-style interaction
    181    *     model where control flow is returned to the calling application to write the request body
    182    *     before the response body is readable.
    183    * @param connection the connection used for an intermediate response immediately prior to this
    184    *     request/response pair, such as a same-host redirect. This engine assumes ownership of the
    185    *     connection and must release it when it is unneeded.
    186    * @param routeSelector the route selector used for a failed attempt immediately preceding this
    187    */
    188   public HttpEngine(OkHttpClient client, Request request, boolean bufferRequestBody,
    189       boolean callerWritesRequestBody, boolean forWebSocket, Connection connection,
    190       RouteSelector routeSelector, RetryableSink requestBodyOut, Response priorResponse) {
    191     this.client = client;
    192     this.userRequest = request;
    193     this.bufferRequestBody = bufferRequestBody;
    194     this.callerWritesRequestBody = callerWritesRequestBody;
    195     this.forWebSocket = forWebSocket;
    196     this.connection = connection;
    197     this.routeSelector = routeSelector;
    198     this.requestBodyOut = requestBodyOut;
    199     this.priorResponse = priorResponse;
    200 
    201     if (connection != null) {
    202       Internal.instance.setOwner(connection, this);
    203       this.route = connection.getRoute();
    204     } else {
    205       this.route = null;
    206     }
    207   }
    208 
    209   /**
    210    * Figures out what the response source will be, and opens a socket to that
    211    * source if necessary. Prepares the request headers and gets ready to start
    212    * writing the request body if it exists.
    213    *
    214    * @throws RequestException if there was a problem with request setup. Unrecoverable.
    215    * @throws RouteException if the was a problem during connection via a specific route. Sometimes
    216    *     recoverable. See {@link #recover(RouteException)}.
    217    * @throws IOException if there was a problem while making a request. Sometimes recoverable. See
    218    *     {@link #recover(IOException)}.
    219    *
    220    */
    221   public void sendRequest() throws RequestException, RouteException, IOException {
    222     if (cacheStrategy != null) return; // Already sent.
    223     if (transport != null) throw new IllegalStateException();
    224 
    225     Request request = networkRequest(userRequest);
    226 
    227     InternalCache responseCache = Internal.instance.internalCache(client);
    228     Response cacheCandidate = responseCache != null
    229         ? responseCache.get(request)
    230         : null;
    231 
    232     long now = System.currentTimeMillis();
    233     cacheStrategy = new CacheStrategy.Factory(now, request, cacheCandidate).get();
    234     networkRequest = cacheStrategy.networkRequest;
    235     cacheResponse = cacheStrategy.cacheResponse;
    236 
    237     if (responseCache != null) {
    238       responseCache.trackResponse(cacheStrategy);
    239     }
    240 
    241     if (cacheCandidate != null && cacheResponse == null) {
    242       closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
    243     }
    244 
    245     if (networkRequest != null) {
    246       // Open a connection unless we inherited one from a redirect.
    247       if (connection == null) {
    248         connect();
    249       }
    250 
    251       transport = Internal.instance.newTransport(connection, this);
    252 
    253       // If the caller's control flow writes the request body, we need to create that stream
    254       // immediately. And that means we need to immediately write the request headers, so we can
    255       // start streaming the request body. (We may already have a request body if we're retrying a
    256       // failed POST.)
    257       if (callerWritesRequestBody && permitsRequestBody() && requestBodyOut == null) {
    258         long contentLength = OkHeaders.contentLength(request);
    259         if (bufferRequestBody) {
    260           if (contentLength > Integer.MAX_VALUE) {
    261             throw new IllegalStateException("Use setFixedLengthStreamingMode() or "
    262                 + "setChunkedStreamingMode() for requests larger than 2 GiB.");
    263           }
    264 
    265           if (contentLength != -1) {
    266             // Buffer a request body of a known length.
    267             transport.writeRequestHeaders(networkRequest);
    268             requestBodyOut = new RetryableSink((int) contentLength);
    269           } else {
    270             // Buffer a request body of an unknown length. Don't write request
    271             // headers until the entire body is ready; otherwise we can't set the
    272             // Content-Length header correctly.
    273             requestBodyOut = new RetryableSink();
    274           }
    275         } else {
    276           transport.writeRequestHeaders(networkRequest);
    277           requestBodyOut = transport.createRequestBody(networkRequest, contentLength);
    278         }
    279       }
    280 
    281     } else {
    282       // We aren't using the network. Recycle a connection we may have inherited from a redirect.
    283       if (connection != null) {
    284         Internal.instance.recycle(client.getConnectionPool(), connection);
    285         connection = null;
    286       }
    287 
    288       if (cacheResponse != null) {
    289         // We have a valid cached response. Promote it to the user response immediately.
    290         this.userResponse = cacheResponse.newBuilder()
    291             .request(userRequest)
    292             .priorResponse(stripBody(priorResponse))
    293             .cacheResponse(stripBody(cacheResponse))
    294             .build();
    295       } else {
    296         // We're forbidden from using the network, and the cache is insufficient.
    297         this.userResponse = new Response.Builder()
    298             .request(userRequest)
    299             .priorResponse(stripBody(priorResponse))
    300             .protocol(Protocol.HTTP_1_1)
    301             .code(504)
    302             .message("Unsatisfiable Request (only-if-cached)")
    303             .body(EMPTY_BODY)
    304             .build();
    305       }
    306 
    307       userResponse = unzip(userResponse);
    308     }
    309   }
    310 
    311   private static Response stripBody(Response response) {
    312     return response != null && response.body() != null
    313         ? response.newBuilder().body(null).build()
    314         : response;
    315   }
    316 
    317   /** Connect to the origin server either directly or via a proxy. */
    318   private void connect() throws RequestException, RouteException {
    319     if (connection != null) throw new IllegalStateException();
    320 
    321     if (routeSelector == null) {
    322       address = createAddress(client, networkRequest);
    323       try {
    324         routeSelector = RouteSelector.get(address, networkRequest, client);
    325       } catch (IOException e) {
    326         throw new RequestException(e);
    327       }
    328     }
    329 
    330     connection = nextConnection();
    331     route = connection.getRoute();
    332   }
    333 
    334   /**
    335    * Returns the next connection to attempt.
    336    *
    337    * @throws java.util.NoSuchElementException if there are no more routes to attempt.
    338    */
    339   private Connection nextConnection() throws RouteException {
    340     Connection connection = createNextConnection();
    341     Internal.instance.connectAndSetOwner(client, connection, this, networkRequest);
    342     return connection;
    343   }
    344 
    345   private Connection createNextConnection() throws RouteException {
    346     ConnectionPool pool = client.getConnectionPool();
    347 
    348     // Always prefer pooled connections over new connections.
    349     for (Connection pooled; (pooled = pool.get(address)) != null; ) {
    350       if (networkRequest.method().equals("GET") || Internal.instance.isReadable(pooled)) {
    351         return pooled;
    352       }
    353       closeQuietly(pooled.getSocket());
    354     }
    355 
    356     try {
    357       Route route = routeSelector.next();
    358       return new Connection(pool, route);
    359     } catch (IOException e) {
    360       throw new RouteException(e);
    361     }
    362   }
    363 
    364   /**
    365    * Called immediately before the transport transmits HTTP request headers.
    366    * This is used to observe the sent time should the request be cached.
    367    */
    368   public void writingRequestHeaders() {
    369     if (sentRequestMillis != -1) throw new IllegalStateException();
    370     sentRequestMillis = System.currentTimeMillis();
    371   }
    372 
    373   boolean permitsRequestBody() {
    374     return HttpMethod.permitsRequestBody(userRequest.method());
    375   }
    376 
    377   /** Returns the request body or null if this request doesn't have a body. */
    378   public Sink getRequestBody() {
    379     if (cacheStrategy == null) throw new IllegalStateException();
    380     return requestBodyOut;
    381   }
    382 
    383   public BufferedSink getBufferedRequestBody() {
    384     BufferedSink result = bufferedRequestBody;
    385     if (result != null) return result;
    386     Sink requestBody = getRequestBody();
    387     return requestBody != null
    388         ? (bufferedRequestBody = Okio.buffer(requestBody))
    389         : null;
    390   }
    391 
    392   public boolean hasResponse() {
    393     return userResponse != null;
    394   }
    395 
    396   public Request getRequest() {
    397     return userRequest;
    398   }
    399 
    400   /** Returns the engine's response. */
    401   // TODO: the returned body will always be null.
    402   public Response getResponse() {
    403     if (userResponse == null) throw new IllegalStateException();
    404     return userResponse;
    405   }
    406 
    407   public Connection getConnection() {
    408     return connection;
    409   }
    410 
    411   /**
    412    * Attempt to recover from failure to connect via a route. Returns a new HTTP engine
    413    * that should be used for the retry if there are other routes to try, or null if
    414    * there are no more routes to try.
    415    */
    416   public HttpEngine recover(RouteException e) {
    417     if (routeSelector != null && connection != null) {
    418       connectFailed(routeSelector, e.getLastConnectException());
    419     }
    420 
    421     if (routeSelector == null && connection == null // No connection.
    422         || routeSelector != null && !routeSelector.hasNext() // No more routes to attempt.
    423         || !isRecoverable(e)) {
    424       return null;
    425     }
    426 
    427     Connection connection = close();
    428 
    429     // For failure recovery, use the same route selector with a new connection.
    430     return new HttpEngine(client, userRequest, bufferRequestBody, callerWritesRequestBody,
    431         forWebSocket, connection, routeSelector, (RetryableSink) requestBodyOut, priorResponse);
    432   }
    433 
    434   private boolean isRecoverable(RouteException e) {
    435     // If the application has opted-out of recovery, don't recover.
    436     if (!client.getRetryOnConnectionFailure()) {
    437       return false;
    438     }
    439 
    440     // Problems with a route may mean the connection can be retried with a new route, or may
    441     // indicate a client-side or server-side issue that should not be retried. To tell, we must look
    442     // at the cause.
    443 
    444     IOException ioe = e.getLastConnectException();
    445 
    446     // TODO(nfuller): This is the same logic as in ConnectionSpecSelector
    447     // If there was a protocol problem, don't recover.
    448     if (ioe instanceof ProtocolException) {
    449       return false;
    450     }
    451 
    452     // Look for known client-side or negotiation errors that are unlikely to be fixed by trying
    453     // again with a different route.
    454     if (ioe instanceof SSLHandshakeException) {
    455       // If the problem was a CertificateException from the X509TrustManager,
    456       // do not retry.
    457       if (ioe.getCause() instanceof CertificateException) {
    458         return false;
    459       }
    460     }
    461     if (ioe instanceof SSLPeerUnverifiedException) {
    462       // e.g. a certificate pinning error.
    463       return false;
    464     }
    465     // TODO(nfuller): End of common code.
    466 
    467     // An example of one we might want to retry with a different route is a problem connecting to a
    468     // proxy and would manifest as a standard IOException. Unless it is one we know we should not
    469     // retry, we return true and try a new route.
    470     return true;
    471   }
    472 
    473   /**
    474    * Report and attempt to recover from a failure to communicate with a server. Returns a new
    475    * HTTP engine that should be used for the retry if {@code e} is recoverable, or null if
    476    * the failure is permanent. Requests with a body can only be recovered if the
    477    * body is buffered.
    478    */
    479   public HttpEngine recover(IOException e, Sink requestBodyOut) {
    480     if (routeSelector != null && connection != null) {
    481       connectFailed(routeSelector, e);
    482     }
    483 
    484     boolean canRetryRequestBody = requestBodyOut == null || requestBodyOut instanceof RetryableSink;
    485     if (routeSelector == null && connection == null // No connection.
    486         || routeSelector != null && !routeSelector.hasNext() // No more routes to attempt.
    487         || !isRecoverable(e)
    488         || !canRetryRequestBody) {
    489       return null;
    490     }
    491 
    492     Connection connection = close();
    493 
    494     // For failure recovery, use the same route selector with a new connection.
    495     return new HttpEngine(client, userRequest, bufferRequestBody, callerWritesRequestBody,
    496         forWebSocket, connection, routeSelector, (RetryableSink) requestBodyOut, priorResponse);
    497   }
    498 
    499   private void connectFailed(RouteSelector routeSelector, IOException e) {
    500     // If this is a recycled connection, don't count its failure against the route.
    501     if (Internal.instance.recycleCount(connection) > 0) return;
    502     Route failedRoute = connection.getRoute();
    503     routeSelector.connectFailed(failedRoute, e);
    504   }
    505 
    506   public HttpEngine recover(IOException e) {
    507     return recover(e, requestBodyOut);
    508   }
    509 
    510   private boolean isRecoverable(IOException e) {
    511     // If the application has opted-out of recovery, don't recover.
    512     if (!client.getRetryOnConnectionFailure()) {
    513       return false;
    514     }
    515 
    516     // If there was a protocol problem, don't recover.
    517     if (e instanceof ProtocolException) {
    518       return false;
    519     }
    520 
    521     // If there was an interruption or timeout, don't recover.
    522     if (e instanceof InterruptedIOException) {
    523       return false;
    524     }
    525 
    526     return true;
    527   }
    528 
    529   /**
    530    * Returns the route used to retrieve the response. Null if we haven't
    531    * connected yet, or if no connection was necessary.
    532    */
    533   public Route getRoute() {
    534     return route;
    535   }
    536 
    537   private void maybeCache() throws IOException {
    538     InternalCache responseCache = Internal.instance.internalCache(client);
    539     if (responseCache == null) return;
    540 
    541     // Should we cache this response for this request?
    542     if (!CacheStrategy.isCacheable(userResponse, networkRequest)) {
    543       if (HttpMethod.invalidatesCache(networkRequest.method())) {
    544         try {
    545           responseCache.remove(networkRequest);
    546         } catch (IOException ignored) {
    547           // The cache cannot be written.
    548         }
    549       }
    550       return;
    551     }
    552 
    553     // Offer this request to the cache.
    554     storeRequest = responseCache.put(stripBody(userResponse));
    555   }
    556 
    557   /**
    558    * Configure the socket connection to be either pooled or closed when it is
    559    * either exhausted or closed. If it is unneeded when this is called, it will
    560    * be released immediately.
    561    */
    562   public void releaseConnection() throws IOException {
    563     if (transport != null && connection != null) {
    564       transport.releaseConnectionOnIdle();
    565     }
    566     connection = null;
    567   }
    568 
    569   /**
    570    * Immediately closes the socket connection if it's currently held by this
    571    * engine. Use this to interrupt an in-flight request from any thread. It's
    572    * the caller's responsibility to close the request body and response body
    573    * streams; otherwise resources may be leaked.
    574    */
    575   public void disconnect() {
    576     if (transport != null) {
    577       try {
    578         transport.disconnect(this);
    579       } catch (IOException ignored) {
    580       }
    581     }
    582   }
    583 
    584   /**
    585    * Release any resources held by this engine. If a connection is still held by
    586    * this engine, it is returned.
    587    */
    588   public Connection close() {
    589     if (bufferedRequestBody != null) {
    590       // This also closes the wrapped requestBodyOut.
    591       closeQuietly(bufferedRequestBody);
    592     } else if (requestBodyOut != null) {
    593       closeQuietly(requestBodyOut);
    594     }
    595 
    596     // If this engine never achieved a response body, its connection cannot be reused.
    597     if (userResponse == null) {
    598       if (connection != null) closeQuietly(connection.getSocket()); // TODO: does this break SPDY?
    599       connection = null;
    600       return null;
    601     }
    602 
    603     // Close the response body. This will recycle the connection if it is eligible.
    604     closeQuietly(userResponse.body());
    605 
    606     // Close the connection if it cannot be reused.
    607     if (transport != null && connection != null && !transport.canReuseConnection()) {
    608       closeQuietly(connection.getSocket());
    609       connection = null;
    610       return null;
    611     }
    612 
    613     // Prevent this engine from disconnecting a connection it no longer owns.
    614     if (connection != null && !Internal.instance.clearOwner(connection)) {
    615       connection = null;
    616     }
    617 
    618     Connection result = connection;
    619     connection = null;
    620     return result;
    621   }
    622 
    623   /**
    624    * Returns a new response that does gzip decompression on {@code response}, if transparent gzip
    625    * was both offered by OkHttp and used by the origin server.
    626    *
    627    * <p>In addition to decompression, this will also strip the corresponding headers. We strip the
    628    * Content-Encoding header to prevent the application from attempting to double decompress. We
    629    * strip the Content-Length header because it is the length of the compressed content, but the
    630    * application is only interested in the length of the uncompressed content.
    631    *
    632    * <p>This method should only be used for non-empty response bodies. Response codes like "304 Not
    633    * Modified" can include "Content-Encoding: gzip" without a response body and we will crash if we
    634    * attempt to decompress the zero-byte source.
    635    */
    636   private Response unzip(final Response response) throws IOException {
    637     if (!transparentGzip || !"gzip".equalsIgnoreCase(userResponse.header("Content-Encoding"))) {
    638       return response;
    639     }
    640 
    641     if (response.body() == null) {
    642       return response;
    643     }
    644 
    645     GzipSource responseBody = new GzipSource(response.body().source());
    646     Headers strippedHeaders = response.headers().newBuilder()
    647         .removeAll("Content-Encoding")
    648         .removeAll("Content-Length")
    649         .build();
    650     return response.newBuilder()
    651         .headers(strippedHeaders)
    652         .body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)))
    653         .build();
    654   }
    655 
    656   /**
    657    * Returns true if the response must have a (possibly 0-length) body.
    658    * See RFC 2616 section 4.3.
    659    */
    660   public static boolean hasBody(Response response) {
    661     // HEAD requests never yield a body regardless of the response headers.
    662     if (response.request().method().equals("HEAD")) {
    663       return false;
    664     }
    665 
    666     int responseCode = response.code();
    667     if ((responseCode < HTTP_CONTINUE || responseCode >= 200)
    668         && responseCode != HTTP_NO_CONTENT
    669         && responseCode != HTTP_NOT_MODIFIED) {
    670       return true;
    671     }
    672 
    673     // If the Content-Length or Transfer-Encoding headers disagree with the
    674     // response code, the response is malformed. For best compatibility, we
    675     // honor the headers.
    676     if (OkHeaders.contentLength(response) != -1
    677         || "chunked".equalsIgnoreCase(response.header("Transfer-Encoding"))) {
    678       return true;
    679     }
    680 
    681     return false;
    682   }
    683 
    684   /**
    685    * Populates request with defaults and cookies.
    686    *
    687    * <p>This client doesn't specify a default {@code Accept} header because it
    688    * doesn't know what content types the application is interested in.
    689    */
    690   private Request networkRequest(Request request) throws IOException {
    691     Request.Builder result = request.newBuilder();
    692 
    693     if (request.header("Host") == null) {
    694       result.header("Host", hostHeader(request.url()));
    695     }
    696 
    697     if ((connection == null || connection.getProtocol() != Protocol.HTTP_1_0)
    698         && request.header("Connection") == null) {
    699       result.header("Connection", "Keep-Alive");
    700     }
    701 
    702     if (request.header("Accept-Encoding") == null) {
    703       transparentGzip = true;
    704       result.header("Accept-Encoding", "gzip");
    705     }
    706 
    707     CookieHandler cookieHandler = client.getCookieHandler();
    708     if (cookieHandler != null) {
    709       // Capture the request headers added so far so that they can be offered to the CookieHandler.
    710       // This is mostly to stay close to the RI; it is unlikely any of the headers above would
    711       // affect cookie choice besides "Host".
    712       Map<String, List<String>> headers = OkHeaders.toMultimap(result.build().headers(), null);
    713 
    714       Map<String, List<String>> cookies = cookieHandler.get(request.uri(), headers);
    715 
    716       // Add any new cookies to the request.
    717       OkHeaders.addCookies(result, cookies);
    718     }
    719 
    720     if (request.header("User-Agent") == null) {
    721       result.header("User-Agent", Version.userAgent());
    722     }
    723 
    724     return result.build();
    725   }
    726 
    727   public static String hostHeader(URL url) {
    728     return getEffectivePort(url) != getDefaultPort(url.getProtocol())
    729         ? url.getHost() + ":" + url.getPort()
    730         : url.getHost();
    731   }
    732 
    733   /**
    734    * Flushes the remaining request header and body, parses the HTTP response
    735    * headers and starts reading the HTTP response body if it exists.
    736    */
    737   public void readResponse() throws IOException {
    738     if (userResponse != null) {
    739       return; // Already ready.
    740     }
    741     if (networkRequest == null && cacheResponse == null) {
    742       throw new IllegalStateException("call sendRequest() first!");
    743     }
    744     if (networkRequest == null) {
    745       return; // No network response to read.
    746     }
    747 
    748     Response networkResponse;
    749 
    750     if (forWebSocket) {
    751       transport.writeRequestHeaders(networkRequest);
    752       networkResponse = readNetworkResponse();
    753 
    754     } else if (!callerWritesRequestBody) {
    755       networkResponse = new NetworkInterceptorChain(0, networkRequest).proceed(networkRequest);
    756 
    757     } else {
    758       // Emit the request body's buffer so that everything is in requestBodyOut.
    759       if (bufferedRequestBody != null && bufferedRequestBody.buffer().size() > 0) {
    760         bufferedRequestBody.emit();
    761       }
    762 
    763       // Emit the request headers if we haven't yet. We might have just learned the Content-Length.
    764       if (sentRequestMillis == -1) {
    765         if (OkHeaders.contentLength(networkRequest) == -1
    766             && requestBodyOut instanceof RetryableSink) {
    767           long contentLength = ((RetryableSink) requestBodyOut).contentLength();
    768           networkRequest = networkRequest.newBuilder()
    769               .header("Content-Length", Long.toString(contentLength))
    770               .build();
    771         }
    772         transport.writeRequestHeaders(networkRequest);
    773       }
    774 
    775       // Write the request body to the socket.
    776       if (requestBodyOut != null) {
    777         if (bufferedRequestBody != null) {
    778           // This also closes the wrapped requestBodyOut.
    779           bufferedRequestBody.close();
    780         } else {
    781           requestBodyOut.close();
    782         }
    783         if (requestBodyOut instanceof RetryableSink) {
    784           transport.writeRequestBody((RetryableSink) requestBodyOut);
    785         }
    786       }
    787 
    788       networkResponse = readNetworkResponse();
    789     }
    790 
    791     receiveHeaders(networkResponse.headers());
    792 
    793     // If we have a cache response too, then we're doing a conditional get.
    794     if (cacheResponse != null) {
    795       if (validate(cacheResponse, networkResponse)) {
    796         userResponse = cacheResponse.newBuilder()
    797             .request(userRequest)
    798             .priorResponse(stripBody(priorResponse))
    799             .headers(combine(cacheResponse.headers(), networkResponse.headers()))
    800             .cacheResponse(stripBody(cacheResponse))
    801             .networkResponse(stripBody(networkResponse))
    802             .build();
    803         networkResponse.body().close();
    804         releaseConnection();
    805 
    806         // Update the cache after combining headers but before stripping the
    807         // Content-Encoding header (as performed by initContentStream()).
    808         InternalCache responseCache = Internal.instance.internalCache(client);
    809         responseCache.trackConditionalCacheHit();
    810         responseCache.update(cacheResponse, stripBody(userResponse));
    811         userResponse = unzip(userResponse);
    812         return;
    813       } else {
    814         closeQuietly(cacheResponse.body());
    815       }
    816     }
    817 
    818     userResponse = networkResponse.newBuilder()
    819         .request(userRequest)
    820         .priorResponse(stripBody(priorResponse))
    821         .cacheResponse(stripBody(cacheResponse))
    822         .networkResponse(stripBody(networkResponse))
    823         .build();
    824 
    825     if (hasBody(userResponse)) {
    826       maybeCache();
    827       userResponse = unzip(cacheWritingResponse(storeRequest, userResponse));
    828     }
    829   }
    830 
    831   class NetworkInterceptorChain implements Interceptor.Chain {
    832     private final int index;
    833     private final Request request;
    834     private int calls;
    835 
    836     NetworkInterceptorChain(int index, Request request) {
    837       this.index = index;
    838       this.request = request;
    839     }
    840 
    841     @Override public Connection connection() {
    842       return connection;
    843     }
    844 
    845     @Override public Request request() {
    846       return request;
    847     }
    848 
    849     @Override public Response proceed(Request request) throws IOException {
    850       calls++;
    851 
    852       if (index > 0) {
    853         Interceptor caller = client.networkInterceptors().get(index - 1);
    854         Address address = connection().getRoute().getAddress();
    855 
    856         // Confirm that the interceptor uses the connection we've already prepared.
    857         if (!request.url().getHost().equals(address.getUriHost())
    858             || getEffectivePort(request.url()) != address.getUriPort()) {
    859           throw new IllegalStateException("network interceptor " + caller
    860               + " must retain the same host and port");
    861         }
    862 
    863         // Confirm that this is the interceptor's first call to chain.proceed().
    864         if (calls > 1) {
    865           throw new IllegalStateException("network interceptor " + caller
    866               + " must call proceed() exactly once");
    867         }
    868       }
    869 
    870       if (index < client.networkInterceptors().size()) {
    871         // There's another interceptor in the chain. Call that.
    872         NetworkInterceptorChain chain = new NetworkInterceptorChain(index + 1, request);
    873         Interceptor interceptor = client.networkInterceptors().get(index);
    874         Response interceptedResponse = interceptor.intercept(chain);
    875 
    876         // Confirm that the interceptor made the required call to chain.proceed().
    877         if (chain.calls != 1) {
    878           throw new IllegalStateException("network interceptor " + interceptor
    879               + " must call proceed() exactly once");
    880         }
    881 
    882         return interceptedResponse;
    883       }
    884 
    885       transport.writeRequestHeaders(request);
    886 
    887       //Update the networkRequest with the possibly updated interceptor request.
    888       networkRequest = request;
    889 
    890       if (permitsRequestBody() && request.body() != null) {
    891         Sink requestBodyOut = transport.createRequestBody(request, request.body().contentLength());
    892         BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
    893         request.body().writeTo(bufferedRequestBody);
    894         bufferedRequestBody.close();
    895       }
    896 
    897       return readNetworkResponse();
    898     }
    899   }
    900 
    901   private Response readNetworkResponse() throws IOException {
    902     transport.finishRequest();
    903 
    904     Response networkResponse = transport.readResponseHeaders()
    905         .request(networkRequest)
    906         .handshake(connection.getHandshake())
    907         .header(OkHeaders.SENT_MILLIS, Long.toString(sentRequestMillis))
    908         .header(OkHeaders.RECEIVED_MILLIS, Long.toString(System.currentTimeMillis()))
    909         .build();
    910 
    911     if (!forWebSocket) {
    912       networkResponse = networkResponse.newBuilder()
    913           .body(transport.openResponseBody(networkResponse))
    914           .build();
    915     }
    916 
    917     Internal.instance.setProtocol(connection, networkResponse.protocol());
    918     return networkResponse;
    919   }
    920 
    921   /**
    922    * Returns a new source that writes bytes to {@code cacheRequest} as they are read by the source
    923    * consumer. This is careful to discard bytes left over when the stream is closed; otherwise we
    924    * may never exhaust the source stream and therefore not complete the cached response.
    925    */
    926   private Response cacheWritingResponse(final CacheRequest cacheRequest, Response response)
    927       throws IOException {
    928     // Some apps return a null body; for compatibility we treat that like a null cache request.
    929     if (cacheRequest == null) return response;
    930     Sink cacheBodyUnbuffered = cacheRequest.body();
    931     if (cacheBodyUnbuffered == null) return response;
    932 
    933     final BufferedSource source = response.body().source();
    934     final BufferedSink cacheBody = Okio.buffer(cacheBodyUnbuffered);
    935 
    936     Source cacheWritingSource = new Source() {
    937       boolean cacheRequestClosed;
    938 
    939       @Override public long read(Buffer sink, long byteCount) throws IOException {
    940         long bytesRead;
    941         try {
    942           bytesRead = source.read(sink, byteCount);
    943         } catch (IOException e) {
    944           if (!cacheRequestClosed) {
    945             cacheRequestClosed = true;
    946             cacheRequest.abort(); // Failed to write a complete cache response.
    947           }
    948           throw e;
    949         }
    950 
    951         if (bytesRead == -1) {
    952           if (!cacheRequestClosed) {
    953             cacheRequestClosed = true;
    954             cacheBody.close(); // The cache response is complete!
    955           }
    956           return -1;
    957         }
    958 
    959         sink.copyTo(cacheBody.buffer(), sink.size() - bytesRead, bytesRead);
    960         cacheBody.emitCompleteSegments();
    961         return bytesRead;
    962       }
    963 
    964       @Override public Timeout timeout() {
    965         return source.timeout();
    966       }
    967 
    968       @Override public void close() throws IOException {
    969         if (!cacheRequestClosed
    970             && !Util.discard(this, Transport.DISCARD_STREAM_TIMEOUT_MILLIS, MILLISECONDS)) {
    971           cacheRequestClosed = true;
    972           cacheRequest.abort();
    973         }
    974         source.close();
    975       }
    976     };
    977 
    978     return response.newBuilder()
    979         .body(new RealResponseBody(response.headers(), Okio.buffer(cacheWritingSource)))
    980         .build();
    981   }
    982 
    983   /**
    984    * Returns true if {@code cached} should be used; false if {@code network}
    985    * response should be used.
    986    */
    987   private static boolean validate(Response cached, Response network) {
    988     if (network.code() == HTTP_NOT_MODIFIED) {
    989       return true;
    990     }
    991 
    992     // The HTTP spec says that if the network's response is older than our
    993     // cached response, we may return the cache's response. Like Chrome (but
    994     // unlike Firefox), this client prefers to return the newer response.
    995     Date lastModified = cached.headers().getDate("Last-Modified");
    996     if (lastModified != null) {
    997       Date networkLastModified = network.headers().getDate("Last-Modified");
    998       if (networkLastModified != null
    999           && networkLastModified.getTime() < lastModified.getTime()) {
   1000         return true;
   1001       }
   1002     }
   1003 
   1004     return false;
   1005   }
   1006 
   1007   /**
   1008    * Combines cached headers with a network headers as defined by RFC 2616,
   1009    * 13.5.3.
   1010    */
   1011   private static Headers combine(Headers cachedHeaders, Headers networkHeaders) throws IOException {
   1012     Headers.Builder result = new Headers.Builder();
   1013 
   1014     for (int i = 0, size = cachedHeaders.size(); i < size; i++) {
   1015       String fieldName = cachedHeaders.name(i);
   1016       String value = cachedHeaders.value(i);
   1017       if ("Warning".equalsIgnoreCase(fieldName) && value.startsWith("1")) {
   1018         continue; // Drop 100-level freshness warnings.
   1019       }
   1020       if (!OkHeaders.isEndToEnd(fieldName) || networkHeaders.get(fieldName) == null) {
   1021         result.add(fieldName, value);
   1022       }
   1023     }
   1024 
   1025     for (int i = 0, size = networkHeaders.size(); i < size; i++) {
   1026       String fieldName = networkHeaders.name(i);
   1027       if ("Content-Length".equalsIgnoreCase(fieldName)) {
   1028         continue; // Ignore content-length headers of validating responses.
   1029       }
   1030       if (OkHeaders.isEndToEnd(fieldName)) {
   1031         result.add(fieldName, networkHeaders.value(i));
   1032       }
   1033     }
   1034 
   1035     return result.build();
   1036   }
   1037 
   1038   public void receiveHeaders(Headers headers) throws IOException {
   1039     CookieHandler cookieHandler = client.getCookieHandler();
   1040     if (cookieHandler != null) {
   1041       cookieHandler.put(userRequest.uri(), OkHeaders.toMultimap(headers, null));
   1042     }
   1043   }
   1044 
   1045   /**
   1046    * Figures out the HTTP request to make in response to receiving this engine's
   1047    * response. This will either add authentication headers or follow redirects.
   1048    * If a follow-up is either unnecessary or not applicable, this returns null.
   1049    */
   1050   public Request followUpRequest() throws IOException {
   1051     if (userResponse == null) throw new IllegalStateException();
   1052     Proxy selectedProxy = getRoute() != null
   1053         ? getRoute().getProxy()
   1054         : client.getProxy();
   1055     int responseCode = userResponse.code();
   1056 
   1057     switch (responseCode) {
   1058       case HTTP_PROXY_AUTH:
   1059         if (selectedProxy.type() != Proxy.Type.HTTP) {
   1060           throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy");
   1061         }
   1062         // fall-through
   1063       case HTTP_UNAUTHORIZED:
   1064         return OkHeaders.processAuthHeader(client.getAuthenticator(), userResponse, selectedProxy);
   1065 
   1066       case HTTP_PERM_REDIRECT:
   1067       case HTTP_TEMP_REDIRECT:
   1068         // "If the 307 or 308 status code is received in response to a request other than GET
   1069         // or HEAD, the user agent MUST NOT automatically redirect the request"
   1070         if (!userRequest.method().equals("GET") && !userRequest.method().equals("HEAD")) {
   1071             return null;
   1072         }
   1073         // fall-through
   1074       case HTTP_MULT_CHOICE:
   1075       case HTTP_MOVED_PERM:
   1076       case HTTP_MOVED_TEMP:
   1077       case HTTP_SEE_OTHER:
   1078         // Does the client allow redirects?
   1079         if (!client.getFollowRedirects()) return null;
   1080 
   1081         String location = userResponse.header("Location");
   1082         if (location == null) return null;
   1083         URL url = new URL(userRequest.url(), location);
   1084 
   1085         // Don't follow redirects to unsupported protocols.
   1086         if (!url.getProtocol().equals("https") && !url.getProtocol().equals("http")) return null;
   1087 
   1088         // If configured, don't follow redirects between SSL and non-SSL.
   1089         boolean sameProtocol = url.getProtocol().equals(userRequest.url().getProtocol());
   1090         if (!sameProtocol && !client.getFollowSslRedirects()) return null;
   1091 
   1092         // Redirects don't include a request body.
   1093         Request.Builder requestBuilder = userRequest.newBuilder();
   1094         if (HttpMethod.permitsRequestBody(userRequest.method())) {
   1095           requestBuilder.method("GET", null);
   1096           requestBuilder.removeHeader("Transfer-Encoding");
   1097           requestBuilder.removeHeader("Content-Length");
   1098           requestBuilder.removeHeader("Content-Type");
   1099         }
   1100 
   1101         // When redirecting across hosts, drop all authentication headers. This
   1102         // is potentially annoying to the application layer since they have no
   1103         // way to retain them.
   1104         if (!sameConnection(url)) {
   1105           requestBuilder.removeHeader("Authorization");
   1106         }
   1107 
   1108         return requestBuilder.url(url).build();
   1109 
   1110       default:
   1111         return null;
   1112     }
   1113   }
   1114 
   1115   /**
   1116    * Returns true if an HTTP request for {@code followUp} can reuse the
   1117    * connection used by this engine.
   1118    */
   1119   public boolean sameConnection(URL followUp) {
   1120     URL url = userRequest.url();
   1121     return url.getHost().equals(followUp.getHost())
   1122         && getEffectivePort(url) == getEffectivePort(followUp)
   1123         && url.getProtocol().equals(followUp.getProtocol());
   1124   }
   1125 
   1126   private static Address createAddress(OkHttpClient client, Request request)
   1127       throws RequestException {
   1128     String uriHost = request.url().getHost();
   1129     if (uriHost == null || uriHost.length() == 0) {
   1130       throw new RequestException(new UnknownHostException(request.url().toString()));
   1131     }
   1132 
   1133     SSLSocketFactory sslSocketFactory = null;
   1134     HostnameVerifier hostnameVerifier = null;
   1135     CertificatePinner certificatePinner = null;
   1136     if (request.isHttps()) {
   1137       sslSocketFactory = client.getSslSocketFactory();
   1138       hostnameVerifier = client.getHostnameVerifier();
   1139       certificatePinner = client.getCertificatePinner();
   1140     }
   1141 
   1142     return new Address(uriHost, getEffectivePort(request.url()),
   1143         client.getSocketFactory(), sslSocketFactory, hostnameVerifier, certificatePinner,
   1144         client.getAuthenticator(), client.getProxy(), client.getProtocols(),
   1145         client.getConnectionSpecs(), client.getProxySelector());
   1146   }
   1147 }
   1148