OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/tools/quic/quic_in_memory_cache.h" | 5 #include "net/tools/quic/quic_in_memory_cache.h" |
6 | 6 |
7 #include "base/files/file_enumerator.h" | 7 #include "base/files/file_enumerator.h" |
8 #include "base/files/file_util.h" | 8 #include "base/files/file_util.h" |
9 #include "base/stl_util.h" | 9 #include "base/stl_util.h" |
10 #include "base/strings/string_number_conversions.h" | 10 #include "base/strings/string_number_conversions.h" |
| 11 #include "net/tools/balsa/balsa_frame.h" |
11 #include "net/tools/balsa/balsa_headers.h" | 12 #include "net/tools/balsa/balsa_headers.h" |
| 13 #include "net/tools/balsa/noop_balsa_visitor.h" |
| 14 #include "net/tools/quic/spdy_utils.h" |
12 | 15 |
13 using base::FilePath; | 16 using base::FilePath; |
14 using base::StringPiece; | 17 using base::StringPiece; |
15 using std::string; | 18 using std::string; |
16 | 19 |
17 namespace net { | 20 namespace net { |
18 namespace tools { | 21 namespace tools { |
19 | 22 |
20 // Specifies the directory used during QuicInMemoryCache | |
21 // construction to seed the cache. Cache directory can be | |
22 // generated using `wget -p --save-headers <url> | |
23 string FLAGS_quic_in_memory_cache_dir = ""; | |
24 | |
25 namespace { | 23 namespace { |
26 | 24 |
27 // BalsaVisitor implementation (glue) which caches response bodies. | 25 // BalsaVisitor implementation (glue) which caches response bodies. |
28 class CachingBalsaVisitor : public NoOpBalsaVisitor { | 26 class CachingBalsaVisitor : public NoOpBalsaVisitor { |
29 public: | 27 public: |
30 CachingBalsaVisitor() : done_framing_(false) {} | 28 CachingBalsaVisitor() : done_framing_(false) {} |
31 void ProcessBodyData(const char* input, size_t size) override { | 29 void ProcessBodyData(const char* input, size_t size) override { |
32 AppendToBody(input, size); | 30 AppendToBody(input, size); |
33 } | 31 } |
34 void MessageDone() override { done_framing_ = true; } | 32 void MessageDone() override { done_framing_ = true; } |
(...skipping 10 matching lines...) Expand all Loading... |
45 bool done_framing() const { return done_framing_; } | 43 bool done_framing() const { return done_framing_; } |
46 const string& body() const { return body_; } | 44 const string& body() const { return body_; } |
47 | 45 |
48 private: | 46 private: |
49 bool done_framing_; | 47 bool done_framing_; |
50 string body_; | 48 string body_; |
51 }; | 49 }; |
52 | 50 |
53 } // namespace | 51 } // namespace |
54 | 52 |
| 53 QuicInMemoryCache::Response::Response() : response_type_(REGULAR_RESPONSE) {} |
| 54 |
| 55 QuicInMemoryCache::Response::~Response() {} |
| 56 |
55 // static | 57 // static |
56 QuicInMemoryCache* QuicInMemoryCache::GetInstance() { | 58 QuicInMemoryCache* QuicInMemoryCache::GetInstance() { |
57 return Singleton<QuicInMemoryCache>::get(); | 59 return Singleton<QuicInMemoryCache>::get(); |
58 } | 60 } |
59 | 61 |
60 const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse( | 62 const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse( |
61 StringPiece host, | 63 StringPiece host, |
62 StringPiece path) const { | 64 StringPiece path) const { |
63 ResponseMap::const_iterator it = responses_.find(GetKey(host, path)); | 65 ResponseMap::const_iterator it = responses_.find(GetKey(host, path)); |
64 if (it == responses_.end()) { | 66 if (it == responses_.end()) { |
65 return nullptr; | 67 return nullptr; |
66 } | 68 } |
67 return it->second; | 69 return it->second; |
68 } | 70 } |
69 | 71 |
70 void QuicInMemoryCache::AddSimpleResponse(StringPiece host, | 72 void QuicInMemoryCache::AddSimpleResponse(StringPiece host, |
71 StringPiece path, | 73 StringPiece path, |
72 int response_code, | 74 int response_code, |
73 StringPiece response_detail, | 75 StringPiece response_detail, |
74 StringPiece body) { | 76 StringPiece body) { |
75 BalsaHeaders response_headers; | 77 SpdyHeaderBlock response_headers; |
76 response_headers.SetRequestFirstlineFromStringPieces( | 78 response_headers[":version"] = "HTTP/1.1"; |
77 "HTTP/1.1", base::IntToString(response_code), response_detail); | 79 string status = base::IntToString(response_code) + " "; |
78 response_headers.AppendHeader("content-length", | 80 response_detail.AppendToString(&status); |
79 base::IntToString(body.length())); | 81 response_headers[":status"] = status; |
80 | 82 response_headers["content-length"] = base::IntToString(body.length()); |
81 AddResponse(host, path, response_headers, body); | 83 AddResponse(host, path, response_headers, body); |
82 } | 84 } |
83 | 85 |
84 void QuicInMemoryCache::AddResponse(StringPiece host, | 86 void QuicInMemoryCache::AddResponse(StringPiece host, |
85 StringPiece path, | 87 StringPiece path, |
86 const BalsaHeaders& response_headers, | 88 const SpdyHeaderBlock& response_headers, |
87 StringPiece response_body) { | 89 StringPiece response_body) { |
88 AddResponseImpl(host, path, REGULAR_RESPONSE, response_headers, | 90 AddResponseImpl(host, path, REGULAR_RESPONSE, response_headers, |
89 response_body); | 91 response_body); |
90 } | 92 } |
91 | 93 |
92 void QuicInMemoryCache::AddSpecialResponse(StringPiece host, | 94 void QuicInMemoryCache::AddSpecialResponse(StringPiece host, |
93 StringPiece path, | 95 StringPiece path, |
94 SpecialResponseType response_type) { | 96 SpecialResponseType response_type) { |
95 AddResponseImpl(host, path, response_type, BalsaHeaders(), ""); | 97 AddResponseImpl(host, path, response_type, SpdyHeaderBlock(), ""); |
96 } | 98 } |
97 | 99 |
98 QuicInMemoryCache::QuicInMemoryCache() { | 100 QuicInMemoryCache::QuicInMemoryCache() {} |
99 Initialize(); | |
100 } | |
101 | 101 |
102 void QuicInMemoryCache::ResetForTests() { | 102 void QuicInMemoryCache::ResetForTests() { |
103 STLDeleteValues(&responses_); | 103 STLDeleteValues(&responses_); |
104 Initialize(); | |
105 } | 104 } |
106 | 105 |
107 void QuicInMemoryCache::Initialize() { | 106 void QuicInMemoryCache::InitializeFromDirectory(const string& cache_directory) { |
108 // If there's no defined cache dir, we have no initialization to do. | 107 if (cache_directory.empty()) { |
109 if (FLAGS_quic_in_memory_cache_dir.empty()) { | 108 LOG(DFATAL) << "cache_directory must not be empty."; |
110 VLOG(1) << "No cache directory found. Skipping initialization."; | |
111 return; | 109 return; |
112 } | 110 } |
113 VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: " | 111 VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: " |
114 << FLAGS_quic_in_memory_cache_dir; | 112 << cache_directory; |
115 | 113 FilePath directory(cache_directory); |
116 FilePath directory(FLAGS_quic_in_memory_cache_dir); | |
117 base::FileEnumerator file_list(directory, | 114 base::FileEnumerator file_list(directory, |
118 true, | 115 true, |
119 base::FileEnumerator::FILES); | 116 base::FileEnumerator::FILES); |
120 | 117 |
121 for (FilePath file = file_list.Next(); !file.empty(); | 118 for (FilePath file_iter = file_list.Next(); !file_iter.empty(); |
122 file = file_list.Next()) { | 119 file_iter = file_list.Next()) { |
| 120 BalsaHeaders request_headers, response_headers; |
123 // Need to skip files in .svn directories | 121 // Need to skip files in .svn directories |
124 if (file.value().find("/.svn/") != string::npos) { | 122 if (file_iter.value().find("/.svn/") != string::npos) { |
125 continue; | 123 continue; |
126 } | 124 } |
127 | 125 |
128 BalsaHeaders request_headers, response_headers; | 126 // Tease apart filename into host and path. |
| 127 StringPiece file(file_iter.value()); |
| 128 file.remove_prefix(cache_directory.length()); |
| 129 if (file[0] == '/') { |
| 130 file.remove_prefix(1); |
| 131 } |
129 | 132 |
130 string file_contents; | 133 string file_contents; |
131 base::ReadFileToString(file, &file_contents); | 134 base::ReadFileToString(file_iter, &file_contents); |
132 | 135 |
133 // Frame HTTP. | 136 // Frame HTTP. |
134 CachingBalsaVisitor caching_visitor; | 137 CachingBalsaVisitor caching_visitor; |
135 BalsaFrame framer; | 138 BalsaFrame framer; |
136 framer.set_balsa_headers(&response_headers); | 139 framer.set_balsa_headers(&response_headers); |
137 framer.set_balsa_visitor(&caching_visitor); | 140 framer.set_balsa_visitor(&caching_visitor); |
138 size_t processed = 0; | 141 size_t processed = 0; |
139 while (processed < file_contents.length() && | 142 while (processed < file_contents.length() && |
140 !caching_visitor.done_framing()) { | 143 !caching_visitor.done_framing()) { |
141 processed += framer.ProcessInput(file_contents.c_str() + processed, | 144 processed += framer.ProcessInput(file_contents.c_str() + processed, |
142 file_contents.length() - processed); | 145 file_contents.length() - processed); |
143 } | 146 } |
144 | 147 |
145 if (!caching_visitor.done_framing()) { | 148 if (!caching_visitor.done_framing()) { |
146 LOG(DFATAL) << "Did not frame entire message from file: " << file.value() | 149 LOG(DFATAL) << "Did not frame entire message from file: " << file |
147 << " (" << processed << " of " << file_contents.length() | 150 << " (" << processed << " of " << file_contents.length() |
148 << " bytes)."; | 151 << " bytes)."; |
149 } | 152 } |
150 if (processed < file_contents.length()) { | 153 if (processed < file_contents.length()) { |
151 // Didn't frame whole file. Assume remainder is body. | 154 // Didn't frame whole file. Assume remainder is body. |
152 // This sometimes happens as a result of incompatibilities between | 155 // This sometimes happens as a result of incompatibilities between |
153 // BalsaFramer and wget's serialization of HTTP sans content-length. | 156 // BalsaFramer and wget's serialization of HTTP sans content-length. |
154 caching_visitor.AppendToBody(file_contents.c_str() + processed, | 157 caching_visitor.AppendToBody(file_contents.c_str() + processed, |
155 file_contents.length() - processed); | 158 file_contents.length() - processed); |
156 processed += file_contents.length(); | 159 processed += file_contents.length(); |
157 } | 160 } |
158 | 161 |
159 StringPiece base = file.value(); | 162 StringPiece base = file; |
160 if (response_headers.HasHeader("X-Original-Url")) { | 163 if (response_headers.HasHeader("X-Original-Url")) { |
161 base = response_headers.GetHeader("X-Original-Url"); | 164 base = response_headers.GetHeader("X-Original-Url"); |
162 response_headers.RemoveAllOfHeader("X-Original-Url"); | 165 response_headers.RemoveAllOfHeader("X-Original-Url"); |
163 // Remove the protocol so that the string is of the form host + path, | 166 // Remove the protocol so that the string is of the form host + path, |
164 // which is parsed properly below. | 167 // which is parsed properly below. |
165 if (StringPieceUtils::StartsWithIgnoreCase(base, "https://")) { | 168 if (StringPieceUtils::StartsWithIgnoreCase(base, "https://")) { |
166 base.remove_prefix(8); | 169 base.remove_prefix(8); |
167 } else if (StringPieceUtils::StartsWithIgnoreCase(base, "http://")) { | 170 } else if (StringPieceUtils::StartsWithIgnoreCase(base, "http://")) { |
168 base.remove_prefix(7); | 171 base.remove_prefix(7); |
169 } | 172 } |
170 } | 173 } |
171 int path_start = base.find_first_of('/'); | 174 int path_start = base.find_first_of('/'); |
172 DCHECK_LT(0, path_start); | 175 DCHECK_LT(0, path_start); |
173 StringPiece host(base.substr(0, path_start)); | 176 StringPiece host(base.substr(0, path_start)); |
174 StringPiece path(base.substr(path_start)); | 177 StringPiece path(base.substr(path_start)); |
175 if (path[path.length() - 1] == ',') { | 178 if (path[path.length() - 1] == ',') { |
176 path.remove_suffix(1); | 179 path.remove_suffix(1); |
177 } | 180 } |
178 AddResponse(host, path, response_headers, caching_visitor.body()); | 181 AddResponse(host, path, |
| 182 SpdyUtils::ResponseHeadersToSpdyHeaders(response_headers), |
| 183 caching_visitor.body()); |
179 } | 184 } |
180 } | 185 } |
181 | 186 |
182 QuicInMemoryCache::~QuicInMemoryCache() { | 187 QuicInMemoryCache::~QuicInMemoryCache() { |
183 STLDeleteValues(&responses_); | 188 STLDeleteValues(&responses_); |
184 } | 189 } |
185 | 190 |
186 void QuicInMemoryCache::AddResponseImpl( | 191 void QuicInMemoryCache::AddResponseImpl( |
187 StringPiece host, | 192 StringPiece host, |
188 StringPiece path, | 193 StringPiece path, |
189 SpecialResponseType response_type, | 194 SpecialResponseType response_type, |
190 const BalsaHeaders& response_headers, | 195 const SpdyHeaderBlock& response_headers, |
191 StringPiece response_body) { | 196 StringPiece response_body) { |
192 string key = GetKey(host, path); | 197 string key = GetKey(host, path); |
193 VLOG(1) << "Adding response for: " << key; | 198 VLOG(1) << "Adding response for: " << key; |
194 if (ContainsKey(responses_, key)) { | 199 if (ContainsKey(responses_, key)) { |
195 LOG(DFATAL) << "Response for '" << key << "' already exists!"; | 200 LOG(DFATAL) << "Response for '" << key << "' already exists!"; |
196 return; | 201 return; |
197 } | 202 } |
198 Response* new_response = new Response(); | 203 Response* new_response = new Response(); |
199 new_response->set_response_type(response_type); | 204 new_response->set_response_type(response_type); |
200 new_response->set_headers(response_headers); | 205 new_response->set_headers(response_headers); |
201 new_response->set_body(response_body); | 206 new_response->set_body(response_body); |
202 responses_[key] = new_response; | 207 responses_[key] = new_response; |
203 } | 208 } |
204 | 209 |
205 string QuicInMemoryCache::GetKey(StringPiece host, StringPiece path) const { | 210 string QuicInMemoryCache::GetKey(StringPiece host, StringPiece path) const { |
206 return host.as_string() + path.as_string(); | 211 return host.as_string() + path.as_string(); |
207 } | 212 } |
208 | 213 |
209 } // namespace tools | 214 } // namespace tools |
210 } // namespace net | 215 } // namespace net |
OLD | NEW |