1c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/*
2c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Copyright (C) 2010 The Android Open Source Project
3c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
4c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Licensed under the Apache License, Version 2.0 (the "License");
5c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * you may not use this file except in compliance with the License.
6c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * You may obtain a copy of the License at
7c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
8c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *      http://www.apache.org/licenses/LICENSE-2.0
9c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
10c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Unless required by applicable law or agreed to in writing, software
11c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS,
12c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * See the License for the specific language governing permissions and
14c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * limitations under the License.
15c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */
16c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
17faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathpackage com.squareup.okhttp;
182231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
192231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.internal.Base64;
202231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.internal.DiskLruCache;
212231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport com.squareup.okhttp.internal.StrictLineReader;
2254cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport com.squareup.okhttp.internal.Util;
23faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport com.squareup.okhttp.internal.http.HttpEngine;
24faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport com.squareup.okhttp.internal.http.HttpURLConnectionImpl;
25a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport com.squareup.okhttp.internal.http.HttpsEngine;
26faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport com.squareup.okhttp.internal.http.HttpsURLConnectionImpl;
27faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport com.squareup.okhttp.internal.http.RawHeaders;
28faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport com.squareup.okhttp.internal.http.ResponseHeaders;
29c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.BufferedWriter;
30c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.ByteArrayInputStream;
31c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.File;
32c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.FilterInputStream;
33c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.FilterOutputStream;
34c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.IOException;
35c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.InputStream;
36c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.OutputStream;
37c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.OutputStreamWriter;
38c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.UnsupportedEncodingException;
39c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.Writer;
40c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CacheRequest;
41c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CacheResponse;
422231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonimport java.net.HttpURLConnection;
43c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.ResponseCache;
44c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.SecureCacheResponse;
45c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URI;
46c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URLConnection;
47c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.security.MessageDigest;
48c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.security.NoSuchAlgorithmException;
49c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.security.Principal;
50c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.security.cert.Certificate;
51c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.security.cert.CertificateEncodingException;
52c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.security.cert.CertificateException;
53c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.security.cert.CertificateFactory;
54c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.security.cert.X509Certificate;
55c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Arrays;
56c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.List;
57c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Map;
58c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport javax.net.ssl.SSLPeerUnverifiedException;
5974623587bf099735cf16ec2298005f12fd3fcb07jwilsonimport javax.net.ssl.SSLSocket;
60c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
6154cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport static com.squareup.okhttp.internal.Util.US_ASCII;
6254cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport static com.squareup.okhttp.internal.Util.UTF_8;
6354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
64c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/**
65faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * Caches HTTP and HTTPS responses to the filesystem so they may be reused,
66faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * saving time and bandwidth.
67faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *
68faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * <h3>Cache Optimization</h3>
69faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * To measure cache effectiveness, this class tracks three statistics:
70faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * <ul>
71faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *     <li><strong>{@link #getRequestCount() Request Count:}</strong> the number
72faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *         of HTTP requests issued since this cache was created.
73faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *     <li><strong>{@link #getNetworkCount() Network Count:}</strong> the
74faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *         number of those requests that required network use.
75faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *     <li><strong>{@link #getHitCount() Hit Count:}</strong> the number of
76faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *         those requests whose responses were served by the cache.
77faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * </ul>
78faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * Sometimes a request will result in a conditional cache hit. If the cache
79faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * contains a stale copy of the response, the client will issue a conditional
80faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * {@code GET}. The server will then send either the updated response if it has
81faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * changed, or a short 'not modified' response if the client's copy is still
82faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * valid. Such responses increment both the network count and hit count.
83faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *
84faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * <p>The best way to improve the cache hit rate is by configuring the web
85faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * server to return cacheable responses. Although this client honors all <a
86faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * href="http://www.ietf.org/rfc/rfc2616.txt">HTTP/1.1 (RFC 2068)</a> cache
87faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * headers, it doesn't cache partial responses.
88faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *
89faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * <h3>Force a Network Response</h3>
90faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * In some situations, such as after a user clicks a 'refresh' button, it may be
91faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * necessary to skip the cache, and fetch data directly from the server. To force
92faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * a full refresh, add the {@code no-cache} directive: <pre>   {@code
93faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *         connection.addRequestProperty("Cache-Control", "no-cache");
94faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * }</pre>
95faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * If it is only necessary to force a cached response to be validated by the
96faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * server, use the more efficient {@code max-age=0} instead: <pre>   {@code
97faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *         connection.addRequestProperty("Cache-Control", "max-age=0");
98faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * }</pre>
99faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *
100faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * <h3>Force a Cache Response</h3>
101faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * Sometimes you'll want to show resources if they are available immediately,
102faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * but not otherwise. This can be used so your application can show
103faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * <i>something</i> while waiting for the latest data to be downloaded. To
104faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * restrict a request to locally-cached resources, add the {@code
105faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * only-if-cached} directive: <pre>   {@code
106faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *     try {
107faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *         connection.addRequestProperty("Cache-Control", "only-if-cached");
108faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *         InputStream cached = connection.getInputStream();
109faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *         // the resource was cached! show it
110faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *     } catch (FileNotFoundException e) {
111faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *         // the resource was not cached
112faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *     }
113faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * }</pre>
114faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * This technique works even better in situations where a stale response is
115faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * better than no response. To permit stale cached responses, use the {@code
116faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * max-stale} directive with the maximum staleness in seconds: <pre>   {@code
117faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *         int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
118faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *         connection.addRequestProperty("Cache-Control", "max-stale=" + maxStale);
119faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * }</pre>
120c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */
121faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathpublic final class HttpResponseCache extends ResponseCache {
12254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static final char[] DIGITS =
12354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
12454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
12554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // TODO: add APIs to iterate the cache?
12654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static final int VERSION = 201105;
12754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static final int ENTRY_METADATA = 0;
12854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static final int ENTRY_BODY = 1;
12954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static final int ENTRY_COUNT = 2;
13054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
13154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private final DiskLruCache cache;
13254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
13354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /* read and write statistics, all guarded by 'this' */
13454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private int writeSuccessCount;
13554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private int writeAbortCount;
13654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private int networkCount;
13754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private int hitCount;
13854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private int requestCount;
13954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
140faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  /**
141faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath   * Although this class only exposes the limited ResponseCache API, it
142faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath   * implements the full OkResponseCache interface. This field is used as a
143faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath   * package private handle to the complete implementation. It delegates to
144faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath   * public and private members of this type.
145faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath   */
146faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  final OkResponseCache okResponseCache = new OkResponseCache() {
147faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    @Override public CacheResponse get(URI uri, String requestMethod,
148faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        Map<String, List<String>> requestHeaders) throws IOException {
149faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      return HttpResponseCache.this.get(uri, requestMethod, requestHeaders);
150faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    }
151faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
152faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    @Override public CacheRequest put(URI uri, URLConnection connection) throws IOException {
153faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      return HttpResponseCache.this.put(uri, connection);
154faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    }
155faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
156a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    @Override public void maybeRemove(String requestMethod, URI uri) throws IOException {
157a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      HttpResponseCache.this.maybeRemove(requestMethod, uri);
158a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    }
159a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
160faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    @Override public void update(
161faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        CacheResponse conditionalCacheHit, HttpURLConnection connection) throws IOException {
162faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      HttpResponseCache.this.update(conditionalCacheHit, connection);
163faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    }
164faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
165faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    @Override public void trackConditionalCacheHit() {
166faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      HttpResponseCache.this.trackConditionalCacheHit();
167faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    }
168faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
169faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    @Override public void trackResponse(ResponseSource source) {
170faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      HttpResponseCache.this.trackResponse(source);
171faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    }
172faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  };
173faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
17454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public HttpResponseCache(File directory, long maxSize) throws IOException {
17554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    cache = DiskLruCache.open(directory, VERSION, ENTRY_COUNT, maxSize);
17654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
17754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
17854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private String uriToKey(URI uri) {
17954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
18054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      MessageDigest messageDigest = MessageDigest.getInstance("MD5");
18154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      byte[] md5bytes = messageDigest.digest(uri.toString().getBytes("UTF-8"));
18254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return bytesToHexString(md5bytes);
18354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (NoSuchAlgorithmException e) {
18454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new AssertionError(e);
18554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (UnsupportedEncodingException e) {
18654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new AssertionError(e);
18754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
18854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
18954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
19054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static String bytesToHexString(byte[] bytes) {
19154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    char[] digits = DIGITS;
19254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    char[] buf = new char[bytes.length * 2];
19354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    int c = 0;
19454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (byte b : bytes) {
19554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      buf[c++] = digits[(b >> 4) & 0xf];
19654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      buf[c++] = digits[b & 0xf];
19754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
19854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return new String(buf);
19954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
20054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
20154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Override public CacheResponse get(URI uri, String requestMethod,
20254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      Map<String, List<String>> requestHeaders) {
20354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String key = uriToKey(uri);
20454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    DiskLruCache.Snapshot snapshot;
20554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Entry entry;
20654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
20754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      snapshot = cache.get(key);
20854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (snapshot == null) {
20954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return null;
21054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
21154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      entry = new Entry(snapshot.getInputStream(ENTRY_METADATA));
21254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException e) {
21354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // Give up because the cache cannot be read.
21454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return null;
21554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
2162231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
21754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (!entry.matches(uri, requestMethod, requestHeaders)) {
21854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      snapshot.close();
21954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return null;
22054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
221c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
22254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return entry.isHttps() ? new EntrySecureCacheResponse(entry, snapshot)
22354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        : new EntryCacheResponse(entry, snapshot);
22454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
225c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
22654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Override public CacheRequest put(URI uri, URLConnection urlConnection) throws IOException {
22754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (!(urlConnection instanceof HttpURLConnection)) {
22854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return null;
22954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
230c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
23154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpURLConnection httpConnection = (HttpURLConnection) urlConnection;
23254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String requestMethod = httpConnection.getRequestMethod();
23354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
234a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    if (maybeRemove(requestMethod, uri)) {
23554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return null;
236a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    }
237a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    if (!requestMethod.equals("GET")) {
23854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // Don't cache non-GET responses. We're technically allowed to cache
23954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // HEAD requests and some POST requests, but the complexity of doing
24054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // so is high and the benefit is low.
24154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return null;
242c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
243c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
24454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpEngine httpEngine = getHttpEngine(httpConnection);
24554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (httpEngine == null) {
24654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // Don't cache unless the HTTP implementation is ours.
24754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return null;
248c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
249c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
25054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    ResponseHeaders response = httpEngine.getResponseHeaders();
25154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (response.hasVaryAll()) {
25254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return null;
2532231db3e6bb54447a9b14cf004a6cb03c373651cjwilson    }
2542231db3e6bb54447a9b14cf004a6cb03c373651cjwilson
25554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RawHeaders varyHeaders =
25654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        httpEngine.getRequestHeaders().getHeaders().getAll(response.getVaryFields());
25754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Entry entry = new Entry(uri, varyHeaders, httpConnection);
25854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    DiskLruCache.Editor editor = null;
25954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
260a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      editor = cache.edit(uriToKey(uri));
26154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (editor == null) {
26254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return null;
26354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
26454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      entry.writeTo(editor);
26554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return new CacheRequestImpl(editor);
26654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException e) {
26754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      abortQuietly(editor);
26854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return null;
26954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
27054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
27154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
272a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  /**
273a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath   * Returns true if the supplied {@code requestMethod} potentially invalidates an entry in the
274a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath   * cache.
275a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath   */
276a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  private boolean maybeRemove(String requestMethod, URI uri) {
277a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    if (requestMethod.equals("POST") || requestMethod.equals("PUT") || requestMethod.equals(
278a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        "DELETE")) {
279a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      try {
280a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        cache.remove(uriToKey(uri));
281a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      } catch (IOException ignored) {
282a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        // The cache cannot be written.
283a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      }
284a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      return true;
285a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    }
286a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    return false;
287a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  }
288a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
289faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  private void update(CacheResponse conditionalCacheHit, HttpURLConnection httpConnection)
29054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throws IOException {
29154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    HttpEngine httpEngine = getHttpEngine(httpConnection);
29254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    URI uri = httpEngine.getUri();
29354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    ResponseHeaders response = httpEngine.getResponseHeaders();
29454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    RawHeaders varyHeaders =
29554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        httpEngine.getRequestHeaders().getHeaders().getAll(response.getVaryFields());
29654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Entry entry = new Entry(uri, varyHeaders, httpConnection);
29754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    DiskLruCache.Snapshot snapshot = (conditionalCacheHit instanceof EntryCacheResponse)
29854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        ? ((EntryCacheResponse) conditionalCacheHit).snapshot
29954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        : ((EntrySecureCacheResponse) conditionalCacheHit).snapshot;
30054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    DiskLruCache.Editor editor = null;
30154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
30254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      editor = snapshot.edit(); // returns null if snapshot is not current
30354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (editor != null) {
30454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        entry.writeTo(editor);
30554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        editor.commit();
30654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
30754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException e) {
30854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      abortQuietly(editor);
30954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
31054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
31154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
31254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void abortQuietly(DiskLruCache.Editor editor) {
31354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    // Give up because the cache cannot be written.
31454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
31554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (editor != null) {
31654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        editor.abort();
31754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
31854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (IOException ignored) {
31954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
32054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
32154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
32254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private HttpEngine getHttpEngine(URLConnection httpConnection) {
32354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (httpConnection instanceof HttpURLConnectionImpl) {
32454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return ((HttpURLConnectionImpl) httpConnection).getHttpEngine();
32554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else if (httpConnection instanceof HttpsURLConnectionImpl) {
32654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return ((HttpsURLConnectionImpl) httpConnection).getHttpEngine();
32754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
32854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return null;
32954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
33054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
331c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
332faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  /**
333faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath   * Closes the cache and deletes all of its stored values. This will delete
334faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath   * all files in the cache directory including files that weren't created by
335faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath   * the cache.
336faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath   */
337faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  public void delete() throws IOException {
338faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    cache.delete();
33954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
340c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
34154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public synchronized int getWriteAbortCount() {
34254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return writeAbortCount;
34354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
344c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
34554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public synchronized int getWriteSuccessCount() {
34654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return writeSuccessCount;
34754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
34854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
349faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  public long getSize() {
350faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    return cache.size();
351faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
352faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
353faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  public long getMaxSize() {
354faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    return cache.getMaxSize();
355faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
356faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
357faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  public void flush() throws IOException {
358faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    cache.flush();
359faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
360faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
361faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  public void close() throws IOException {
362faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    cache.close();
363faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
364faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
365faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  public File getDirectory() {
366faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    return cache.getDirectory();
367faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
368faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
369faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  public boolean isClosed() {
370faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    return cache.isClosed();
371faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
372faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
373faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  private synchronized void trackResponse(ResponseSource source) {
37454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    requestCount++;
375c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
37654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    switch (source) {
37754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      case CACHE:
37854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        hitCount++;
37954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        break;
38054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      case CONDITIONAL_CACHE:
38154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      case NETWORK:
38254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        networkCount++;
38354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        break;
38454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
38554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
38654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
387faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  private synchronized void trackConditionalCacheHit() {
38854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    hitCount++;
38954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
39054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
39154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public synchronized int getNetworkCount() {
39254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return networkCount;
39354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
39454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
39554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public synchronized int getHitCount() {
39654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return hitCount;
39754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
39854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
39954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public synchronized int getRequestCount() {
40054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return requestCount;
40154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
40254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
40354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private final class CacheRequestImpl extends CacheRequest {
40454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final DiskLruCache.Editor editor;
40554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private OutputStream cacheOut;
40654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private boolean done;
40754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private OutputStream body;
40854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
40954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    public CacheRequestImpl(final DiskLruCache.Editor editor) throws IOException {
41054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.editor = editor;
41154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.cacheOut = editor.newOutputStream(ENTRY_BODY);
41254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.body = new FilterOutputStream(cacheOut) {
41354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        @Override public void close() throws IOException {
41454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          synchronized (HttpResponseCache.this) {
41554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            if (done) {
41654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson              return;
417c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath            }
41854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            done = true;
41954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            writeSuccessCount++;
42054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          }
42154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          super.close();
42254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          editor.commit();
423c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
424c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
425a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        @Override public void write(byte[] buffer, int offset, int length) throws IOException {
42654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          // Since we don't override "write(int oneByte)", we can write directly to "out"
42754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          // and avoid the inefficient implementation from the FilterOutputStream.
42854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          out.write(buffer, offset, length);
429c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
43054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      };
43154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
432c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
43354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public void abort() {
43454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      synchronized (HttpResponseCache.this) {
43554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        if (done) {
43654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          return;
43754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        }
43854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        done = true;
43954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        writeAbortCount++;
44054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
44154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      Util.closeQuietly(cacheOut);
44254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      try {
44354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        editor.abort();
44454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } catch (IOException ignored) {
44554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
44654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
447c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
44854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public OutputStream getBody() throws IOException {
44954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return body;
450c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
45154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
45254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
45354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static final class Entry {
45454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final String uri;
45554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final RawHeaders varyHeaders;
45654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final String requestMethod;
45754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final RawHeaders responseHeaders;
45854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final String cipherSuite;
45954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final Certificate[] peerCertificates;
46054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final Certificate[] localCertificates;
461c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
462c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
46354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * Reads an entry from an input stream. A typical entry looks like this:
46454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * <pre>{@code
46554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   http://google.com/foo
46654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   GET
46754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   2
46854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   Accept-Language: fr-CA
46954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   Accept-Charset: UTF-8
47054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   HTTP/1.1 200 OK
47154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   3
47254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   Content-Type: image/png
47354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   Content-Length: 100
47454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   Cache-Control: max-age=600
47554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * }</pre>
47654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *
47754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * <p>A typical HTTPS file looks like this:
47854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * <pre>{@code
47954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   https://google.com/foo
48054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   GET
48154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   2
48254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   Accept-Language: fr-CA
48354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   Accept-Charset: UTF-8
48454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   HTTP/1.1 200 OK
48554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   3
48654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   Content-Type: image/png
48754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   Content-Length: 100
48854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   Cache-Control: max-age=600
48954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *
49054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   AES_256_WITH_MD5
49154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   2
49254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   base64-encoded peerCertificate[0]
49354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   base64-encoded peerCertificate[1]
49454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *   -1
49554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * }</pre>
49654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * The file is newline separated. The first two lines are the URL and
49754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * the request method. Next is the number of HTTP Vary request header
49854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * lines, followed by those lines.
49954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *
50054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * <p>Next is the response status line, followed by the number of HTTP
50154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * response header lines, followed by those lines.
50254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     *
50354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * <p>HTTPS responses also contain SSL session information. This begins
50454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * with a blank line, and then a line containing the cipher suite. Next
50554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * is the length of the peer certificate chain. These certificates are
50654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * base64-encoded and appear each on their own line. The next line
50754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * contains the length of the local certificate chain. These
50854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * certificates are also base64-encoded and appear each on their own
50954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * line. A length of -1 is used to encode a null array.
510c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
51154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    public Entry(InputStream in) throws IOException {
51254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      try {
51354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        StrictLineReader reader = new StrictLineReader(in, US_ASCII);
51454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        uri = reader.readLine();
51554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        requestMethod = reader.readLine();
51654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        varyHeaders = new RawHeaders();
51754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        int varyRequestHeaderLineCount = reader.readInt();
51854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        for (int i = 0; i < varyRequestHeaderLineCount; i++) {
51954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          varyHeaders.addLine(reader.readLine());
52054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        }
52154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
52254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        responseHeaders = new RawHeaders();
52354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        responseHeaders.setStatusLine(reader.readLine());
52454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        int responseHeaderLineCount = reader.readInt();
52554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        for (int i = 0; i < responseHeaderLineCount; i++) {
52654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          responseHeaders.addLine(reader.readLine());
52754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        }
52854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
52954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        if (isHttps()) {
53054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          String blank = reader.readLine();
531faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath          if (blank.length() > 0) {
53254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            throw new IOException("expected \"\" but was \"" + blank + "\"");
53354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          }
53454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          cipherSuite = reader.readLine();
53554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          peerCertificates = readCertArray(reader);
53654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          localCertificates = readCertArray(reader);
53754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        } else {
53854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          cipherSuite = null;
53954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          peerCertificates = null;
54054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          localCertificates = null;
541c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
54254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } finally {
54354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        in.close();
54454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
545c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
546c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
54754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    public Entry(URI uri, RawHeaders varyHeaders, HttpURLConnection httpConnection)
54854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throws IOException {
54954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.uri = uri.toString();
55054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.varyHeaders = varyHeaders;
55154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.requestMethod = httpConnection.getRequestMethod();
55254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.responseHeaders = RawHeaders.fromMultimap(httpConnection.getHeaderFields(), true);
55354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
55474623587bf099735cf16ec2298005f12fd3fcb07jwilson      SSLSocket sslSocket = getSslSocket(httpConnection);
55574623587bf099735cf16ec2298005f12fd3fcb07jwilson      if (sslSocket != null) {
55674623587bf099735cf16ec2298005f12fd3fcb07jwilson        cipherSuite = sslSocket.getSession().getCipherSuite();
55754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        Certificate[] peerCertificatesNonFinal = null;
558c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        try {
55974623587bf099735cf16ec2298005f12fd3fcb07jwilson          peerCertificatesNonFinal = sslSocket.getSession().getPeerCertificates();
56054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        } catch (SSLPeerUnverifiedException ignored) {
56154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        }
56254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        peerCertificates = peerCertificatesNonFinal;
56374623587bf099735cf16ec2298005f12fd3fcb07jwilson        localCertificates = sslSocket.getSession().getLocalCertificates();
56454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } else {
56554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        cipherSuite = null;
56654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        peerCertificates = null;
56754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        localCertificates = null;
56854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
569c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
570c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
57174623587bf099735cf16ec2298005f12fd3fcb07jwilson    /**
57274623587bf099735cf16ec2298005f12fd3fcb07jwilson     * Returns the SSL socket used by {@code httpConnection} for HTTPS, nor null
57374623587bf099735cf16ec2298005f12fd3fcb07jwilson     * if the connection isn't using HTTPS. Since we permit redirects across
57474623587bf099735cf16ec2298005f12fd3fcb07jwilson     * protocols (HTTP to HTTPS or vice versa), the implementation type of the
57574623587bf099735cf16ec2298005f12fd3fcb07jwilson     * connection doesn't necessarily match the implementation type of its HTTP
57674623587bf099735cf16ec2298005f12fd3fcb07jwilson     * engine.
57774623587bf099735cf16ec2298005f12fd3fcb07jwilson     */
57874623587bf099735cf16ec2298005f12fd3fcb07jwilson    private SSLSocket getSslSocket(HttpURLConnection httpConnection) {
57974623587bf099735cf16ec2298005f12fd3fcb07jwilson      HttpEngine engine = httpConnection instanceof HttpsURLConnectionImpl
58074623587bf099735cf16ec2298005f12fd3fcb07jwilson          ? ((HttpsURLConnectionImpl) httpConnection).getHttpEngine()
58174623587bf099735cf16ec2298005f12fd3fcb07jwilson          : ((HttpURLConnectionImpl) httpConnection).getHttpEngine();
582a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      return engine instanceof HttpsEngine
583a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath          ? ((HttpsEngine) engine).getSslSocket()
58474623587bf099735cf16ec2298005f12fd3fcb07jwilson          : null;
58574623587bf099735cf16ec2298005f12fd3fcb07jwilson    }
58674623587bf099735cf16ec2298005f12fd3fcb07jwilson
58754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    public void writeTo(DiskLruCache.Editor editor) throws IOException {
58854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      OutputStream out = editor.newOutputStream(ENTRY_METADATA);
58954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      Writer writer = new BufferedWriter(new OutputStreamWriter(out, UTF_8));
59054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
59154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      writer.write(uri + '\n');
59254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      writer.write(requestMethod + '\n');
59354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      writer.write(Integer.toString(varyHeaders.length()) + '\n');
59454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      for (int i = 0; i < varyHeaders.length(); i++) {
59554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        writer.write(varyHeaders.getFieldName(i) + ": " + varyHeaders.getValue(i) + '\n');
59654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
59754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
59854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      writer.write(responseHeaders.getStatusLine() + '\n');
59954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      writer.write(Integer.toString(responseHeaders.length()) + '\n');
60054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      for (int i = 0; i < responseHeaders.length(); i++) {
60154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        writer.write(responseHeaders.getFieldName(i) + ": " + responseHeaders.getValue(i) + '\n');
60254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
60354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
60454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (isHttps()) {
60554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        writer.write('\n');
60654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        writer.write(cipherSuite + '\n');
60754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        writeCertArray(writer, peerCertificates);
60854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        writeCertArray(writer, localCertificates);
60954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
61054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      writer.close();
611c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
612c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
61354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private boolean isHttps() {
61454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return uri.startsWith("https://");
615c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
616c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
61754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private Certificate[] readCertArray(StrictLineReader reader) throws IOException {
61854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      int length = reader.readInt();
61954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (length == -1) {
62054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return null;
62154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
62254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      try {
62354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
62454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        Certificate[] result = new Certificate[length];
62554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        for (int i = 0; i < result.length; i++) {
62654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          String line = reader.readLine();
62754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          byte[] bytes = Base64.decode(line.getBytes("US-ASCII"));
62854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          result[i] = certificateFactory.generateCertificate(new ByteArrayInputStream(bytes));
62954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        }
63054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return result;
63154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } catch (CertificateException e) {
632faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        throw new IOException(e.getMessage());
63354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
634c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
635c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
63654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private void writeCertArray(Writer writer, Certificate[] certificates) throws IOException {
63754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (certificates == null) {
63854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        writer.write("-1\n");
63954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return;
64054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
64154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      try {
64254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        writer.write(Integer.toString(certificates.length) + '\n');
64354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        for (Certificate certificate : certificates) {
64454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          byte[] bytes = certificate.getEncoded();
64554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          String line = Base64.encode(bytes);
64654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          writer.write(line + '\n');
64754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        }
64854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } catch (CertificateEncodingException e) {
649faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        throw new IOException(e.getMessage());
65054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
651c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
652c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
65354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    public boolean matches(URI uri, String requestMethod,
65454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        Map<String, List<String>> requestHeaders) {
65554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return this.uri.equals(uri.toString())
65654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          && this.requestMethod.equals(requestMethod)
65754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          && new ResponseHeaders(uri, responseHeaders).varyMatches(varyHeaders.toMultimap(false),
65854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          requestHeaders);
659c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
66054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
66154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
66254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
66354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Returns an input stream that reads the body of a snapshot, closing the
66454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * snapshot when the stream is closed.
66554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
66654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static InputStream newBodyInputStream(final DiskLruCache.Snapshot snapshot) {
66754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return new FilterInputStream(snapshot.getInputStream(ENTRY_BODY)) {
66854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      @Override public void close() throws IOException {
66954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        snapshot.close();
67054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        super.close();
67154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
67254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    };
67354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
674c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
67554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  static class EntryCacheResponse extends CacheResponse {
67654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final Entry entry;
67754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final DiskLruCache.Snapshot snapshot;
67854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final InputStream in;
679c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
68054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    public EntryCacheResponse(Entry entry, DiskLruCache.Snapshot snapshot) {
68154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.entry = entry;
68254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.snapshot = snapshot;
68354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.in = newBodyInputStream(snapshot);
684c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
685c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
68654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public Map<String, List<String>> getHeaders() {
68754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return entry.responseHeaders.toMultimap(true);
688c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
689c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
69054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public InputStream getBody() {
69154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return in;
692c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
69354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
694c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
69554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  static class EntrySecureCacheResponse extends SecureCacheResponse {
69654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final Entry entry;
69754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final DiskLruCache.Snapshot snapshot;
69854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final InputStream in;
699c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
70054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    public EntrySecureCacheResponse(Entry entry, DiskLruCache.Snapshot snapshot) {
70154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.entry = entry;
70254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.snapshot = snapshot;
70354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.in = newBodyInputStream(snapshot);
704c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
705c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
70654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public Map<String, List<String>> getHeaders() {
70754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return entry.responseHeaders.toMultimap(true);
708c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
709c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
71054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public InputStream getBody() {
71154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return in;
712c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
713c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
71454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public String getCipherSuite() {
71554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return entry.cipherSuite;
716c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
717c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
71854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public List<Certificate> getServerCertificateChain()
71954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throws SSLPeerUnverifiedException {
72054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (entry.peerCertificates == null || entry.peerCertificates.length == 0) {
72154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throw new SSLPeerUnverifiedException(null);
72254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
72354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return Arrays.asList(entry.peerCertificates.clone());
72454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
725c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
72654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
72754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (entry.peerCertificates == null || entry.peerCertificates.length == 0) {
72854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throw new SSLPeerUnverifiedException(null);
72954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
73054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return ((X509Certificate) entry.peerCertificates[0]).getSubjectX500Principal();
73154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
732c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
73354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public List<Certificate> getLocalCertificateChain() {
73454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (entry.localCertificates == null || entry.localCertificates.length == 0) {
73554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return null;
73654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
73754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return Arrays.asList(entry.localCertificates.clone());
73854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
739c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
74054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    @Override public Principal getLocalPrincipal() {
74154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (entry.localCertificates == null || entry.localCertificates.length == 0) {
74254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        return null;
74354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
74454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return ((X509Certificate) entry.localCertificates[0]).getSubjectX500Principal();
745c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
74654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
747c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath}
748