HttpEngine.java revision a82f42bbeedd0b07f3892f3b0efaa8122dc8f264
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;
22a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport com.squareup.okhttp.OkHttpClient;
23a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport com.squareup.okhttp.OkResponseCache;
242231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.ResponseSource;
252231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.TunnelRequest;
2654cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport com.squareup.okhttp.internal.Dns;
272231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.internal.Platform;
282231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.internal.Util;
29c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.ByteArrayInputStream;
30c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.IOException;
31c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.InputStream;
32c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.OutputStream;
33c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CacheRequest;
34c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CacheResponse;
35c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CookieHandler;
36a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport java.net.HttpURLConnection;
37c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.Proxy;
38c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URI;
39c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URISyntaxException;
40c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URL;
412231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.UnknownHostException;
42c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Collections;
43c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Date;
44c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.HashMap;
45c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.List;
46c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Map;
47c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.zip.GZIPInputStream;
487899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport javax.net.ssl.HostnameVerifier;
49c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport javax.net.ssl.SSLSocketFactory;
50c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
5154cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport static com.squareup.okhttp.internal.Util.EMPTY_BYTE_ARRAY;
5254cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport static com.squareup.okhttp.internal.Util.getDefaultPort;
5354cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport static com.squareup.okhttp.internal.Util.getEffectivePort;
5454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
55c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/**
56c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Handles a single HTTP request/response pair. Each HTTP engine follows this
57c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * lifecycle:
58c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <ol>
5954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * <li>It is created.
6054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * <li>The HTTP request message is sent with sendRequest(). Once the request
6154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * is sent it is an error to modify the request headers. After
6254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * sendRequest() has been called the request body can be written to if
6354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * it exists.
6454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * <li>The HTTP response message is read with readResponse(). After the
6554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * response has been read the response headers and body can be read.
6654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * All responses have a response body input stream, though in some
6754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson * instances this stream is empty.
68c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * </ol>
69c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
70c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>The request and response may be served by the HTTP response cache, by the
71c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * network, or by both in the event of a conditional GET.
72c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
73c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>This class may hold a socket connection that needs to be released or
74c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * recycled. By default, this socket connection is held when the last byte of
75c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * the response is consumed. To release the connection when it is no longer
76c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * required, use {@link #automaticallyReleaseConnectionToPool()}.
77c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */
78c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathpublic class HttpEngine {
7954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static final CacheResponse GATEWAY_TIMEOUT_RESPONSE = new CacheResponse() {
8054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public Map<String, List<String>> getHeaders() throws IOException {
8154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      Map<String, List<String>> result = new HashMap<String, List<String>>();
8254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      result.put(null, Collections.singletonList("HTTP/1.1 504 Gateway Timeout"));
8354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return result;
84c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
8554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public InputStream getBody() throws IOException {
8654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return new ByteArrayInputStream(EMPTY_BYTE_ARRAY);
87c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
8854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  };
8954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public static final int HTTP_CONTINUE = 100;
9054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
91a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  protected final Policy policy;
92a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  protected final OkHttpClient client;
9354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
9454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  protected final String method;
9554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
9654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private ResponseSource responseSource;
9754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
9854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  protected Connection connection;
9954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  protected RouteSelector routeSelector;
10054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private OutputStream requestBodyOut;
10154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
10254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private Transport transport;
10354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
10454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private InputStream responseTransferIn;
10554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private InputStream responseBodyIn;
10654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
10754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private CacheResponse cacheResponse;
10854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private CacheRequest cacheRequest;
10954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
11054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** The time when the request headers were written, or -1 if they haven't been written yet. */
11154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  long sentRequestMillis = -1;
11254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
11354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
11454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * True if this client added an "Accept-Encoding: gzip" header field and is
11554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * therefore responsible for also decompressing the transfer stream.
11654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
11754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private boolean transparentGzip;
11854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
11954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  final URI uri;
12054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
12154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  final RequestHeaders requestHeaders;
12254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
12354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Null until a response is received from the network or the cache. */
12454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  ResponseHeaders responseHeaders;
12554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
12654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // The cache response currently being validated on a conditional get. Null
12754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // if the cached response doesn't exist or doesn't need validation. If the
12854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // conditional get succeeds, these will be used for the response headers and
12954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // body. If it fails, these be closed and set to null.
13054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private ResponseHeaders cachedResponseHeaders;
13154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private InputStream cachedResponseBody;
13254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
13354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
13454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * True if the socket connection should be released to the connection pool
13554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * when the response has been fully read.
13654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
13754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private boolean automaticallyReleaseConnectionToPool;
13854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
13954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** True if the socket connection is no longer needed by this engine. */
14054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private boolean connectionReleased;
14154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
14254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
14354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @param requestHeaders the client's supplied request headers. This class
144a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath   *     creates a private copy that it can mutate.
14554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @param connection the connection used for an intermediate response
146a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath   *     immediately prior to this request/response pair, such as a same-host
147a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath   *     redirect. This engine assumes ownership of the connection and must
148a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath   *     release it when it is unneeded.
14954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
150a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  public HttpEngine(OkHttpClient client, Policy policy, String method, RawHeaders requestHeaders,
15154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      Connection connection, RetryableOutputStream requestBodyOut) throws IOException {
152a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    this.client = client;
15354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this.policy = policy;
15454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this.method = method;
15554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this.connection = connection;
15654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this.requestBodyOut = requestBodyOut;
15754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
15854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
15954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      uri = Platform.get().toUriLenient(policy.getURL());
16054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (URISyntaxException e) {
161faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      throw new IOException(e.getMessage());
16254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
16354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
16454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this.requestHeaders = new RequestHeaders(uri, new RawHeaders(requestHeaders));
16554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
16654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
16754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public URI getUri() {
16854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return uri;
16954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
17054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
17154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
17254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Figures out what the response source will be, and opens a socket to that
17354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * source if necessary. Prepares the request headers and gets ready to start
17454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * writing the request body if it exists.
17554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
17654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final void sendRequest() throws IOException {
17754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (responseSource != null) {
17854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return;
17954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
18054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
18154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    prepareRawRequestHeaders();
18254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    initResponseSource();
183a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    OkResponseCache responseCache = client.getOkResponseCache();
184a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    if (responseCache != null) {
185a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      responseCache.trackResponse(responseSource);
18654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
18754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
18854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // The raw response source may require the network, but the request
18954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // headers may forbid network use. In that case, dispose of the network
19054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // response and use a GATEWAY_TIMEOUT response instead, as specified
19154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // by http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4.
19254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (requestHeaders.isOnlyIfCached() && responseSource.requiresConnection()) {
19354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
19454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        Util.closeQuietly(cachedResponseBody);
19554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
19654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.responseSource = ResponseSource.CACHE;
19754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.cacheResponse = GATEWAY_TIMEOUT_RESPONSE;
19854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      RawHeaders rawResponseHeaders = RawHeaders.fromMultimap(cacheResponse.getHeaders(), true);
19954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      setResponse(new ResponseHeaders(uri, rawResponseHeaders), cacheResponse.getBody());
20054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
20154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
20254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (responseSource.requiresConnection()) {
20354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      sendSocketRequest();
20454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else if (connection != null) {
205a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      client.getConnectionPool().recycle(connection);
20654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection = null;
20754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
20854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
20954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
21054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
21154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Initialize the source for this response. It may be corrected later if the
21254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * request headers forbids network use.
21354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
21454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void initResponseSource() throws IOException {
21554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    responseSource = ResponseSource.NETWORK;
216a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    if (!policy.getUseCaches()) return;
21754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
218a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    OkResponseCache responseCache = client.getOkResponseCache();
219a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    if (responseCache == null) return;
220a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
221a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    CacheResponse candidate = responseCache.get(
222a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        uri, method, requestHeaders.getHeaders().toMultimap(false));
223a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    if (candidate == null) return;
22454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
22554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Map<String, List<String>> responseHeadersMap = candidate.getHeaders();
22654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    cachedResponseBody = candidate.getBody();
22754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (!acceptCacheResponseType(candidate)
22854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        || responseHeadersMap == null
22954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        || cachedResponseBody == null) {
23054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      Util.closeQuietly(cachedResponseBody);
23154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return;
23254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
23354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
23454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RawHeaders rawResponseHeaders = RawHeaders.fromMultimap(responseHeadersMap, true);
23554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    cachedResponseHeaders = new ResponseHeaders(uri, rawResponseHeaders);
23654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    long now = System.currentTimeMillis();
23754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this.responseSource = cachedResponseHeaders.chooseResponseSource(now, requestHeaders);
23854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (responseSource == ResponseSource.CACHE) {
23954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.cacheResponse = candidate;
24054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      setResponse(cachedResponseHeaders, cachedResponseBody);
24154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
24254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.cacheResponse = candidate;
24354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else if (responseSource == ResponseSource.NETWORK) {
24454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      Util.closeQuietly(cachedResponseBody);
24554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
24654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new AssertionError();
24754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
24854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
24954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
25054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void sendSocketRequest() throws IOException {
25154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (connection == null) {
25254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connect();
25354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
25454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
25554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (transport != null) {
25654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new IllegalStateException();
25754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
25854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
25954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transport = (Transport) connection.newTransport(this);
26054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
26154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (hasRequestBody() && requestBodyOut == null) {
26254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // Create a request body if we don't have one already. We'll already
26354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // have one if we're retrying a failed POST.
26454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      requestBodyOut = transport.createRequestBody();
26554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
26654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
26754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
26854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Connect to the origin server either directly or via a proxy. */
26954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  protected final void connect() throws IOException {
27054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (connection != null) {
27154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return;
27254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
27354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (routeSelector == null) {
27454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      String uriHost = uri.getHost();
27554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (uriHost == null) {
27654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throw new UnknownHostException(uri.toString());
27754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
2787407d6984ce69693097befc9b72609a8156463bbNarayan Kamath      SSLSocketFactory sslSocketFactory = null;
2797407d6984ce69693097befc9b72609a8156463bbNarayan Kamath      HostnameVerifier hostnameVerifier = null;
2807407d6984ce69693097befc9b72609a8156463bbNarayan Kamath      if (uri.getScheme().equalsIgnoreCase("https")) {
281a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        sslSocketFactory = client.getSslSocketFactory();
282a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        hostnameVerifier = client.getHostnameVerifier();
2837407d6984ce69693097befc9b72609a8156463bbNarayan Kamath      }
2847407d6984ce69693097befc9b72609a8156463bbNarayan Kamath      Address address = new Address(uriHost, getEffectivePort(uri), sslSocketFactory,
285a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath          hostnameVerifier, client.getAuthenticator(), client.getProxy(), client.getTransports());
286a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      routeSelector = new RouteSelector(address, uri, client.getProxySelector(),
287a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath          client.getConnectionPool(), Dns.DEFAULT, client.getRoutesDatabase());
28854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
28954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connection = routeSelector.next();
29054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (!connection.isConnected()) {
291a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      connection.connect(client.getConnectTimeout(), client.getReadTimeout(), getTunnelConfig());
292a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      client.getConnectionPool().maybeShare(connection);
293a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      client.getRoutesDatabase().connected(connection.getRoute());
29454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
29554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    connected(connection);
296a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    if (connection.getRoute().getProxy() != client.getProxy()) {
2977407d6984ce69693097befc9b72609a8156463bbNarayan Kamath      // Update the request line if the proxy changed; it may need a host name.
29854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      requestHeaders.getHeaders().setRequestLine(getRequestLine());
29954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
30054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
30154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
30254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
30354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Called after a socket connection has been created or retrieved from the
30454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * pool. Subclasses use this hook to get a reference to the TLS data.
30554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
30654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  protected void connected(Connection connection) {
30754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
30854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
30954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
31054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Called immediately before the transport transmits HTTP request headers.
31154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * This is used to observe the sent time should the request be cached.
31254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
31354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public void writingRequestHeaders() {
31454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (sentRequestMillis != -1) {
31554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new IllegalStateException();
31654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
31754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    sentRequestMillis = System.currentTimeMillis();
31854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
31954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
32054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
32154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @param body the response body, or null if it doesn't exist or isn't
32254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * available.
32354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
32454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void setResponse(ResponseHeaders headers, InputStream body) throws IOException {
32554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (this.responseBodyIn != null) {
32654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new IllegalStateException();
32754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
32854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this.responseHeaders = headers;
32954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (body != null) {
33054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      initContentStream(body);
33154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
33254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
33354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
33454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  boolean hasRequestBody() {
33554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return method.equals("POST") || method.equals("PUT");
33654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
33754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
33854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Returns the request body or null if this request doesn't have a body. */
33954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final OutputStream getRequestBody() {
34054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (responseSource == null) {
34154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new IllegalStateException();
34254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
34354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return requestBodyOut;
34454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
34554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
34654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final boolean hasResponse() {
34754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return responseHeaders != null;
34854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
34954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
35054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final RequestHeaders getRequestHeaders() {
35154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return requestHeaders;
35254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
35354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
35454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final ResponseHeaders getResponseHeaders() {
35554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (responseHeaders == null) {
35654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new IllegalStateException();
35754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
35854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return responseHeaders;
35954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
36054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
36154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final int getResponseCode() {
36254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (responseHeaders == null) {
36354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new IllegalStateException();
36454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
36554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return responseHeaders.getHeaders().getResponseCode();
36654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
36754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
36854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final InputStream getResponseBody() {
36954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (responseHeaders == null) {
37054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new IllegalStateException();
37154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
37254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return responseBodyIn;
37354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
37454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
37554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final CacheResponse getCacheResponse() {
37654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return cacheResponse;
37754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
37854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
37954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final Connection getConnection() {
38054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return connection;
38154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
38254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
38354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
38454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Returns true if {@code cacheResponse} is of the right type. This
38554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * condition is necessary but not sufficient for the cached response to
38654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * be used.
38754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
38854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  protected boolean acceptCacheResponseType(CacheResponse cacheResponse) {
38954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return true;
39054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
39154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
39254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void maybeCache() throws IOException {
39354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Are we caching at all?
394a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    if (!policy.getUseCaches()) return;
395a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    OkResponseCache responseCache = client.getOkResponseCache();
396a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    if (responseCache == null) return;
397a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
398a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    HttpURLConnection connectionToCache = policy.getHttpConnectionToCache();
39954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
40054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Should we cache this response for this request?
40154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (!responseHeaders.isCacheable(requestHeaders)) {
402a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      responseCache.maybeRemove(connectionToCache.getRequestMethod(), uri);
40354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return;
40454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
40554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
40654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Offer this request to the cache.
407a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    cacheRequest = responseCache.put(uri, connectionToCache);
40854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
40954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
41054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
41154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Cause the socket connection to be released to the connection pool when
41254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * it is no longer needed. If it is already unneeded, it will be pooled
41354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * immediately. Otherwise the connection is held so that redirects can be
41454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * handled by the same connection.
41554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
41654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final void automaticallyReleaseConnectionToPool() {
41754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    automaticallyReleaseConnectionToPool = true;
41854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (connection != null && connectionReleased) {
419a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      client.getConnectionPool().recycle(connection);
42054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connection = null;
42154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
42254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
42354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
42454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
42554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Releases this engine so that its resources may be either reused or
42654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * closed. Also call {@link #automaticallyReleaseConnectionToPool} unless
42754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * the connection will be used to follow a redirect.
42854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
42954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final void release(boolean streamCancelled) {
43054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // If the response body comes from the cache, close it.
43154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (responseBodyIn == cachedResponseBody) {
43254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      Util.closeQuietly(responseBodyIn);
433c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
434c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
43554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (!connectionReleased && connection != null) {
43654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      connectionReleased = true;
437c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
43854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (transport == null || !transport.makeReusable(streamCancelled, requestBodyOut,
43954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          responseTransferIn)) {
44054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        Util.closeQuietly(connection);
44154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        connection = null;
44254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } else if (automaticallyReleaseConnectionToPool) {
443a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        client.getConnectionPool().recycle(connection);
44454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        connection = null;
44554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
44654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
44754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
448c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
44954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void initContentStream(InputStream transferStream) throws IOException {
45054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    responseTransferIn = transferStream;
45154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (transparentGzip && responseHeaders.isContentEncodingGzip()) {
45254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // If the response was transparently gzipped, remove the gzip header field
45354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // so clients don't double decompress. http://b/3009828
45454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      //
45554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // Also remove the Content-Length in this case because it contains the
45654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // length 528 of the gzipped response. This isn't terribly useful and is
45754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // dangerous because 529 clients can query the content length, but not
45854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // the content encoding.
45954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      responseHeaders.stripContentEncoding();
46054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      responseHeaders.stripContentLength();
46154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      responseBodyIn = new GZIPInputStream(transferStream);
46254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
46354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      responseBodyIn = transferStream;
46454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
46554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
46654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
46754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
46854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Returns true if the response must have a (possibly 0-length) body.
46954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * See RFC 2616 section 4.3.
47054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
47154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final boolean hasResponseBody() {
47254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    int responseCode = responseHeaders.getHeaders().getResponseCode();
47354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
47454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // HEAD requests never yield a body regardless of the response headers.
47554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (method.equals("HEAD")) {
47654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return false;
47754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
47854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
47954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if ((responseCode < HTTP_CONTINUE || responseCode >= 200)
48054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        && responseCode != HttpURLConnectionImpl.HTTP_NO_CONTENT
48154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        && responseCode != HttpURLConnectionImpl.HTTP_NOT_MODIFIED) {
48254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return true;
48354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
48454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
48554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // If the Content-Length or Transfer-Encoding headers disagree with the
48654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // response code, the response is malformed. For best compatibility, we
48754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // honor the headers.
48854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (responseHeaders.getContentLength() != -1 || responseHeaders.isChunked()) {
48954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return true;
49054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
49154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
49254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return false;
49354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
49454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
49554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
49654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Populates requestHeaders with defaults and cookies.
49754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   *
49854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * <p>This client doesn't specify a default {@code Accept} header because it
49954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * doesn't know what content types the application is interested in.
50054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
50154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void prepareRawRequestHeaders() throws IOException {
50254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    requestHeaders.getHeaders().setRequestLine(getRequestLine());
50354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
50454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (requestHeaders.getUserAgent() == null) {
50554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      requestHeaders.setUserAgent(getDefaultUserAgent());
50654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
50754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
50854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (requestHeaders.getHost() == null) {
50954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      requestHeaders.setHost(getOriginAddress(policy.getURL()));
51054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
51154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
51254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if ((connection == null || connection.getHttpMinorVersion() != 0)
51354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        && requestHeaders.getConnection() == null) {
51454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      requestHeaders.setConnection("Keep-Alive");
51554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
51654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
51754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (requestHeaders.getAcceptEncoding() == null) {
51854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      transparentGzip = true;
51954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      requestHeaders.setAcceptEncoding("gzip");
52054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
52154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
52254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (hasRequestBody() && requestHeaders.getContentType() == null) {
52354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      requestHeaders.setContentType("application/x-www-form-urlencoded");
52454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
52554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
52654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    long ifModifiedSince = policy.getIfModifiedSince();
52754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (ifModifiedSince != 0) {
52854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      requestHeaders.setIfModifiedSince(new Date(ifModifiedSince));
52954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
53054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
531a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    CookieHandler cookieHandler = client.getCookieHandler();
53254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (cookieHandler != null) {
53354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      requestHeaders.addCookies(
53454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          cookieHandler.get(uri, requestHeaders.getHeaders().toMultimap(false)));
53554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
53654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
53754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
53854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
53954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Returns the request status line, like "GET / HTTP/1.1". This is exposed
54054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * to the application by {@link HttpURLConnectionImpl#getHeaderFields}, so
54154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * it needs to be set even if the transport is SPDY.
54254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
54354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  String getRequestLine() {
54454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String protocol =
54554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        (connection == null || connection.getHttpMinorVersion() != 0) ? "HTTP/1.1" : "HTTP/1.0";
54654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return method + " " + requestString() + " " + protocol;
54754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
54854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
54954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private String requestString() {
55054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URL url = policy.getURL();
55154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (includeAuthorityInRequestLine()) {
55254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return url.toString();
55354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
55454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return requestPath(url);
55554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
55654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
55754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
55854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
55954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Returns the path to request, like the '/' in 'GET / HTTP/1.1'. Never
56054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * empty, even if the request URL is. Includes the query component if it
56154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * exists.
56254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
56354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public static String requestPath(URL url) {
56454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String fileOnly = url.getFile();
56554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (fileOnly == null) {
56654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return "/";
56754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else if (!fileOnly.startsWith("/")) {
56854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return "/" + fileOnly;
56954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
57054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return fileOnly;
571c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
57254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
573c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
57454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
57554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Returns true if the request line should contain the full URL with host
57654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * and port (like "GET http://android.com/foo HTTP/1.1") or only the path
57754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * (like "GET /foo HTTP/1.1").
57854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   *
57954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * <p>This is non-final because for HTTPS it's never necessary to supply the
58054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * full URL, even if a proxy is in use.
58154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
58254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  protected boolean includeAuthorityInRequestLine() {
5837407d6984ce69693097befc9b72609a8156463bbNarayan Kamath    return connection == null
5847407d6984ce69693097befc9b72609a8156463bbNarayan Kamath        ? policy.usingProxy() // A proxy was requested.
585faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        : connection.getRoute().getProxy().type() == Proxy.Type.HTTP; // A proxy was selected.
58654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
58754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
58854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public static String getDefaultUserAgent() {
58954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String agent = System.getProperty("http.agent");
59054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return agent != null ? agent : ("Java" + System.getProperty("java.version"));
59154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
59254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
59354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public static String getOriginAddress(URL url) {
59454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    int port = url.getPort();
59554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String result = url.getHost();
59654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (port > 0 && port != getDefaultPort(url.getProtocol())) {
59754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      result = result + ":" + port;
59854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
59954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return result;
60054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
60154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
60254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
60354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Flushes the remaining request header and body, parses the HTTP response
60454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * headers and starts reading the HTTP response body if it exists.
60554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
60654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final void readResponse() throws IOException {
60754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (hasResponse()) {
608faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      responseHeaders.setResponseSource(responseSource);
60954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return;
61054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
61154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
61254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (responseSource == null) {
61354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new IllegalStateException("readResponse() without sendRequest()");
61454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
61554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
61654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (!responseSource.requiresConnection()) {
61754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return;
61854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
61954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
62054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (sentRequestMillis == -1) {
62154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (requestBodyOut instanceof RetryableOutputStream) {
62254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        int contentLength = ((RetryableOutputStream) requestBodyOut).contentLength();
62354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        requestHeaders.setContentLength(contentLength);
62454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
62554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      transport.writeRequestHeaders();
62654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
62754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
62854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (requestBodyOut != null) {
62954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      requestBodyOut.close();
63054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (requestBodyOut instanceof RetryableOutputStream) {
63154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        transport.writeRequestBody((RetryableOutputStream) requestBodyOut);
63254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
63354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
63454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
63554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    transport.flushRequest();
63654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
63754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    responseHeaders = transport.readResponseHeaders();
63854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    responseHeaders.setLocalTimestamps(sentRequestMillis, System.currentTimeMillis());
639faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    responseHeaders.setResponseSource(responseSource);
64054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
64154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
64254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (cachedResponseHeaders.validate(responseHeaders)) {
64354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        release(false);
64454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        ResponseHeaders combinedHeaders = cachedResponseHeaders.combine(responseHeaders);
64554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        setResponse(combinedHeaders, cachedResponseBody);
646a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        OkResponseCache responseCache = client.getOkResponseCache();
647a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        responseCache.trackConditionalCacheHit();
648a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        responseCache.update(cacheResponse, policy.getHttpConnectionToCache());
64954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return;
65054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } else {
65154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        Util.closeQuietly(cachedResponseBody);
65254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
653c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
654c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
65554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (hasResponseBody()) {
65654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      maybeCache(); // reentrant. this calls into user code which may call back into this!
657c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
658c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
65954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    initContentStream(transport.getTransferStream(cacheRequest));
66054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
661c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
66254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  protected TunnelRequest getTunnelConfig() {
66354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return null;
66454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
6657899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
66654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public void receiveHeaders(RawHeaders headers) throws IOException {
667a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    CookieHandler cookieHandler = client.getCookieHandler();
66854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (cookieHandler != null) {
66954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      cookieHandler.put(uri, headers.toMultimap(true));
6707899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
67154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
672c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath}
673