Home | History | Annotate | Download | only in http
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package tests.http;
     18 
     19 import java.io.ByteArrayInputStream;
     20 import java.io.ByteArrayOutputStream;
     21 import java.io.IOException;
     22 import java.io.InputStream;
     23 import java.io.OutputStream;
     24 import java.net.CacheRequest;
     25 import java.net.CacheResponse;
     26 import java.net.ResponseCache;
     27 import java.net.URI;
     28 import java.net.URLConnection;
     29 import java.util.ArrayList;
     30 import java.util.HashMap;
     31 import java.util.LinkedHashMap;
     32 import java.util.List;
     33 import java.util.Map;
     34 
     35 /**
     36  * Cache all responses in memory by URI.
     37  */
     38 public final class DefaultResponseCache extends ResponseCache {
     39     private final Map<URI, Entry> entries = new HashMap<URI, Entry>();
     40     private int abortCount;
     41     private int successCount;
     42     private int hitCount;
     43     private int missCount;
     44 
     45     @Override public synchronized CacheResponse get(URI uri, String requestMethod,
     46             Map<String, List<String>> requestHeaders) throws IOException {
     47         // TODO: honor the request headers in the cache key
     48         Entry entry = entries.get(uri);
     49         if (entry != null) {
     50             hitCount++;
     51             return entry.asResponse();
     52         } else {
     53             missCount++;
     54             return null;
     55         }
     56     }
     57 
     58     @Override public CacheRequest put(URI uri, URLConnection urlConnection)
     59             throws IOException {
     60         // TODO: honor the response headers for cache invalidation
     61         return new Entry(uri, urlConnection).asRequest();
     62     }
     63 
     64     public synchronized Map<URI, Entry> getContents() {
     65         return new HashMap<URI, Entry>(entries);
     66     }
     67 
     68     /**
     69      * Returns the number of requests that were aborted before they were closed.
     70      */
     71     public synchronized int getAbortCount() {
     72         return abortCount;
     73     }
     74 
     75     /**
     76      * Returns the number of requests that were closed successfully.
     77      */
     78     public synchronized int getSuccessCount() {
     79         return successCount;
     80     }
     81 
     82     /**
     83      * Returns the number of responses served by the cache.
     84      */
     85     public synchronized int getHitCount() {
     86         return hitCount;
     87     }
     88 
     89     /**
     90      * Returns the number of responses that couldn't be served by the cache.
     91      */
     92     public synchronized int getMissCount() {
     93         return missCount;
     94     }
     95 
     96     public final class Entry {
     97         private final ByteArrayOutputStream bytesOut = new ByteArrayOutputStream() {
     98             private boolean closed;
     99             @Override public void close() throws IOException {
    100                 synchronized (DefaultResponseCache.this) {
    101                     if (closed) {
    102                         return;
    103                     }
    104 
    105                     super.close();
    106                     entries.put(uri, Entry.this);
    107                     successCount++;
    108                     closed = true;
    109                 }
    110             }
    111         };
    112         private final Map<String, List<String>> headers;
    113         private final URI uri;
    114 
    115         private Entry(URI uri, URLConnection conn) {
    116             this.uri = uri;
    117             this.headers = deepCopy(conn.getHeaderFields());
    118         }
    119 
    120         public CacheRequest asRequest() {
    121             return new CacheRequest() {
    122                 private boolean aborted;
    123                 @Override public void abort() {
    124                     synchronized (DefaultResponseCache.this) {
    125                         if (aborted) {
    126                             return;
    127                         }
    128 
    129                         abortCount++;
    130                         aborted = true;
    131                     }
    132                 }
    133                 @Override public OutputStream getBody() throws IOException {
    134                     return bytesOut;
    135                 }
    136             };
    137         }
    138 
    139         public CacheResponse asResponse() {
    140             return new CacheResponse() {
    141                 @Override public InputStream getBody() throws IOException {
    142                     return new ByteArrayInputStream(getBytes());
    143                 }
    144                 @Override public Map<String, List<String>> getHeaders() throws IOException {
    145                     return deepCopy(headers);
    146                 }
    147             };
    148         }
    149 
    150         public byte[] getBytes() {
    151             return bytesOut.toByteArray();
    152         }
    153     }
    154 
    155     private static Map<String, List<String>> deepCopy(Map<String, List<String>> input) {
    156         Map<String, List<String>> result = new LinkedHashMap<String, List<String>>(input);
    157         for (Map.Entry<String, List<String>> entry : result.entrySet()) {
    158             entry.setValue(new ArrayList<String>(entry.getValue()));
    159         }
    160         return result;
    161     }
    162 }
    163