Home | History | Annotate | Download | only in quic
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "net/quic/quic_in_memory_cache.h"
      6 
      7 #include "base/files/file_enumerator.h"
      8 #include "base/stl_util.h"
      9 #include "base/strings/string_number_conversions.h"
     10 #include "base/strings/string_util.h"
     11 #include "net/http/http_util.h"
     12 #include "url/gurl.h"
     13 
     14 using base::FilePath;
     15 using base::StringPiece;
     16 using std::string;
     17 
     18 // Specifies the directory used during QuicInMemoryCache
     19 // construction to seed the cache. Cache directory can be
     20 // generated using `wget -p --save-headers <url>
     21 
     22 namespace net {
     23 
     24 FilePath::StringType g_quic_in_memory_cache_dir = FILE_PATH_LITERAL("");
     25 
     26 QuicInMemoryCache::Response::Response() : response_type_(REGULAR_RESPONSE) {
     27 }
     28 
     29 QuicInMemoryCache::Response::~Response() {
     30 }
     31 
     32 // static
     33 QuicInMemoryCache* QuicInMemoryCache::GetInstance() {
     34   return Singleton<QuicInMemoryCache>::get();
     35 }
     36 
     37 const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse(
     38     const GURL& url) const {
     39   ResponseMap::const_iterator it = responses_.find(GetKey(url));
     40   if (it == responses_.end()) {
     41     return NULL;
     42   }
     43   return it->second;
     44 }
     45 
     46 void QuicInMemoryCache::AddSimpleResponse(StringPiece path,
     47                                           StringPiece version,
     48                                           StringPiece response_code,
     49                                           StringPiece response_detail,
     50                                           StringPiece body) {
     51   GURL url("http://" + path.as_string());
     52 
     53   string status_line = version.as_string() + " " +
     54                        response_code.as_string() + " " +
     55                        response_detail.as_string();
     56 
     57   string header = "content-length: " +
     58                   base::Uint64ToString(static_cast<uint64>(body.length()));
     59 
     60   scoped_refptr<HttpResponseHeaders> response_headers =
     61       new HttpResponseHeaders(status_line + '\0' + header + '\0' + '\0');
     62 
     63   AddResponse(url, response_headers, body);
     64 }
     65 
     66 void QuicInMemoryCache::AddResponse(
     67     const GURL& url,
     68     scoped_refptr<HttpResponseHeaders> response_headers,
     69     StringPiece response_body) {
     70   string key = GetKey(url);
     71   VLOG(1) << "Adding response for: " << key;
     72   if (ContainsKey(responses_, key)) {
     73     LOG(DFATAL) << "Response for given request already exists!";
     74     return;
     75   }
     76   Response* new_response = new Response();
     77   new_response->set_headers(response_headers);
     78   new_response->set_body(response_body);
     79   responses_[key] = new_response;
     80 }
     81 
     82 void QuicInMemoryCache::AddSpecialResponse(StringPiece path,
     83                                            SpecialResponseType response_type) {
     84   GURL url("http://" + path.as_string());
     85 
     86   AddResponse(url, NULL, string());
     87   responses_[GetKey(url)]->response_type_ = response_type;
     88 }
     89 
     90 QuicInMemoryCache::QuicInMemoryCache() {
     91   Initialize();
     92 }
     93 
     94 void QuicInMemoryCache::ResetForTests() {
     95   STLDeleteValues(&responses_);
     96   Initialize();
     97 }
     98 
     99 void QuicInMemoryCache::Initialize() {
    100   // If there's no defined cache dir, we have no initialization to do.
    101   if (g_quic_in_memory_cache_dir.size() == 0) {
    102     VLOG(1) << "No cache directory found. Skipping initialization.";
    103     return;
    104   }
    105   VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: "
    106           << g_quic_in_memory_cache_dir;
    107 
    108   FilePath directory(g_quic_in_memory_cache_dir);
    109   base::FileEnumerator file_list(directory,
    110                                  true,
    111                                  base::FileEnumerator::FILES);
    112 
    113   FilePath file = file_list.Next();
    114   for (; !file.empty(); file = file_list.Next()) {
    115     // Need to skip files in .svn directories
    116     if (file.value().find(FILE_PATH_LITERAL("/.svn/")) != string::npos) {
    117       continue;
    118     }
    119 
    120     string file_contents;
    121     base::ReadFileToString(file, &file_contents);
    122 
    123     if (file_contents.length() > INT_MAX) {
    124       LOG(WARNING) << "File '" << file.value() << "' too large: "
    125                    << file_contents.length();
    126       continue;
    127     }
    128     int file_len = static_cast<int>(file_contents.length());
    129 
    130     int headers_end = HttpUtil::LocateEndOfHeaders(file_contents.data(),
    131                                                    file_len);
    132     if (headers_end < 1) {
    133       LOG(DFATAL) << "Headers invalid or empty, ignoring: " << file.value();
    134       continue;
    135     }
    136 
    137     string raw_headers =
    138         HttpUtil::AssembleRawHeaders(file_contents.data(), headers_end);
    139 
    140     scoped_refptr<HttpResponseHeaders> response_headers =
    141         new HttpResponseHeaders(raw_headers);
    142 
    143     string base;
    144     if (response_headers->GetNormalizedHeader("X-Original-Url", &base)) {
    145       response_headers->RemoveHeader("X-Original-Url");
    146       // Remove the protocol so we can add it below.
    147       if (StartsWithASCII(base, "https://", false)) {
    148         base = base.substr(8);
    149       } else if (StartsWithASCII(base, "http://", false)) {
    150         base = base.substr(7);
    151       }
    152     } else {
    153       base = file.AsUTF8Unsafe();
    154     }
    155     if (base.length() == 0 || base[0] == '/') {
    156       LOG(DFATAL) << "Invalid path, ignoring: " << base;
    157       continue;
    158     }
    159     if (base[base.length() - 1] == ',') {
    160       base = base.substr(0, base.length() - 1);
    161     }
    162 
    163     GURL url("http://" + base);
    164 
    165     VLOG(1) << "Inserting '" << GetKey(url) << "' into QuicInMemoryCache.";
    166 
    167     StringPiece body(file_contents.data() + headers_end,
    168                      file_contents.size() - headers_end);
    169 
    170     AddResponse(url, response_headers, body);
    171   }
    172 }
    173 
    174 QuicInMemoryCache::~QuicInMemoryCache() {
    175   STLDeleteValues(&responses_);
    176 }
    177 
    178 string QuicInMemoryCache::GetKey(const GURL& url) const {
    179   // Take everything but the scheme portion of the URL.
    180   return url.host() + url.PathForRequest();
    181 }
    182 
    183 }  // namespace net
    184