OLD | NEW |
| (Empty) |
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 nullptr; | |
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, nullptr, 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 | |
OLD | NEW |