• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Response()26 QuicInMemoryCache::Response::Response() : response_type_(REGULAR_RESPONSE) {
27 }
28 
~Response()29 QuicInMemoryCache::Response::~Response() {
30 }
31 
32 // static
GetInstance()33 QuicInMemoryCache* QuicInMemoryCache::GetInstance() {
34   return Singleton<QuicInMemoryCache>::get();
35 }
36 
GetResponse(const GURL & url) const37 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 
AddSimpleResponse(StringPiece path,StringPiece version,StringPiece response_code,StringPiece response_detail,StringPiece body)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 
AddResponse(const GURL & url,scoped_refptr<HttpResponseHeaders> response_headers,StringPiece response_body)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 
AddSpecialResponse(StringPiece path,SpecialResponseType response_type)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 
QuicInMemoryCache()90 QuicInMemoryCache::QuicInMemoryCache() {
91   Initialize();
92 }
93 
ResetForTests()94 void QuicInMemoryCache::ResetForTests() {
95   STLDeleteValues(&responses_);
96   Initialize();
97 }
98 
Initialize()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 
~QuicInMemoryCache()174 QuicInMemoryCache::~QuicInMemoryCache() {
175   STLDeleteValues(&responses_);
176 }
177 
GetKey(const GURL & url) const178 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