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