Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(313)

Side by Side Diff: net/tools/quic/quic_in_memory_cache.cc

Issue 1819853003: QUIC - extend QuicInMemoryCache so that simple server can do server push. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix asan detected bug Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/tools/quic/quic_in_memory_cache.h ('k') | net/tools/quic/quic_in_memory_cache_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 "base/strings/string_util.h" 11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h" 12 #include "base/strings/stringprintf.h"
13 #include "net/http/http_response_headers.h" 13 #include "net/http/http_response_headers.h"
14 #include "net/http/http_util.h" 14 #include "net/http/http_util.h"
15 #include "net/quic/quic_bug_tracker.h" 15 #include "net/quic/quic_bug_tracker.h"
16 #include "net/spdy/spdy_http_utils.h" 16 #include "net/spdy/spdy_http_utils.h"
17 17
18 using base::FilePath; 18 using base::FilePath;
19 using base::IntToString; 19 using base::IntToString;
20 using base::StringPiece; 20 using base::StringPiece;
21 using std::string; 21 using std::string;
22 22
23 namespace net { 23 namespace net {
24 24
25 namespace {
26
27 class ResourceFileImpl : public net::QuicInMemoryCache::ResourceFile {
28 public:
29 ResourceFileImpl(const base::FilePath& file_name) : ResourceFile(file_name) {}
30
31 void Read() override {
32 base::ReadFileToString(FilePath(file_name_), &file_contents_);
33
34 int file_len = static_cast<int>(file_contents_.length());
35 int headers_end =
36 HttpUtil::LocateEndOfHeaders(file_contents_.data(), file_len);
37 if (headers_end < 1) {
38 LOG(DFATAL) << "Headers invalid or empty, ignoring: "
39 << file_name_.value();
40 return;
41 }
42 http_headers_ = new HttpResponseHeaders(
43 HttpUtil::AssembleRawHeaders(file_contents_.data(), headers_end));
44
45 if (http_headers_->GetNormalizedHeader("X-Original-Url", &url_)) {
46 x_original_url_ = StringPiece(url_);
47 HandleXOriginalUrl();
48 }
49
50 // X-Push-URL header is a relatively quick way to support sever push
51 // in the toy server. A production server should use link=preload
52 // stuff as described in https://w3c.github.io/preload/.
53 StringPiece x_push_url("X-Push-Url");
54 if (http_headers_->HasHeader(x_push_url)) {
55 size_t iter = 0;
56 std::unique_ptr<std::string> push_url(new string());
57 while (
58 http_headers_->EnumerateHeader(&iter, x_push_url, push_url.get())) {
59 push_urls_.push_back(StringPiece(*push_url));
60 push_url_values_.push_back(std::move(push_url));
61 push_url.reset(new string());
62 }
63 }
64
65 body_ = StringPiece(file_contents_.data() + headers_end,
66 file_contents_.size() - headers_end);
67
68 CreateSpdyHeadersFromHttpResponse(*http_headers_, HTTP2, &spdy_headers_);
69 }
70
71 private:
72 scoped_refptr<HttpResponseHeaders> http_headers_;
73 std::string url_;
74 std::list<std::unique_ptr<string>> push_url_values_;
75
76 DISALLOW_COPY_AND_ASSIGN(ResourceFileImpl);
77 };
78
79 } // namespace
80
25 QuicInMemoryCache::ServerPushInfo::ServerPushInfo( 81 QuicInMemoryCache::ServerPushInfo::ServerPushInfo(
26 GURL request_url, 82 GURL request_url,
27 const SpdyHeaderBlock& headers, 83 const SpdyHeaderBlock& headers,
28 net::SpdyPriority priority, 84 net::SpdyPriority priority,
29 string body) 85 string body)
30 : request_url(request_url), 86 : request_url(request_url),
31 headers(headers), 87 headers(headers),
32 priority(priority), 88 priority(priority),
33 body(body) {} 89 body(body) {}
34 90
35 QuicInMemoryCache::ServerPushInfo::ServerPushInfo(const ServerPushInfo& other) = 91 QuicInMemoryCache::ServerPushInfo::ServerPushInfo(const ServerPushInfo& other) =
36 default; 92 default;
37 93
38 QuicInMemoryCache::Response::Response() : response_type_(REGULAR_RESPONSE) {} 94 QuicInMemoryCache::Response::Response() : response_type_(REGULAR_RESPONSE) {}
39 95
40 QuicInMemoryCache::Response::~Response() {} 96 QuicInMemoryCache::Response::~Response() {}
41 97
98 QuicInMemoryCache::ResourceFile::ResourceFile(const base::FilePath& file_name)
99 : file_name_(file_name), file_name_string_(file_name.AsUTF8Unsafe()) {}
100
101 QuicInMemoryCache::ResourceFile::~ResourceFile() {}
102
103 void QuicInMemoryCache::ResourceFile::SetHostPathFromBase(StringPiece base) {
104 size_t path_start = base.find_first_of('/');
105 DCHECK_LT(0UL, path_start);
106 host_ = base.substr(0, path_start);
107 size_t query_start = base.find_first_of(',');
108 if (query_start > 0) {
109 path_ = base.substr(path_start, query_start - 1);
110 } else {
111 path_ = base.substr(path_start);
112 }
113 }
114
115 StringPiece QuicInMemoryCache::ResourceFile::RemoveScheme(StringPiece url) {
116 if (url.starts_with("https://")) {
117 url.remove_prefix(8);
118 } else if (url.starts_with("http://")) {
119 url.remove_prefix(7);
120 }
121 return url;
122 }
123
124 void QuicInMemoryCache::ResourceFile::HandleXOriginalUrl() {
125 StringPiece url(x_original_url_);
126 // Remove the protocol so we can add it below.
127 url = RemoveScheme(url);
128 SetHostPathFromBase(url);
129 }
130
42 // static 131 // static
43 QuicInMemoryCache* QuicInMemoryCache::GetInstance() { 132 QuicInMemoryCache* QuicInMemoryCache::GetInstance() {
44 return base::Singleton<QuicInMemoryCache>::get(); 133 return base::Singleton<QuicInMemoryCache>::get();
45 } 134 }
46 135
47 const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse( 136 const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse(
48 StringPiece host, 137 StringPiece host,
49 StringPiece path) const { 138 StringPiece path) const {
50 ResponseMap::const_iterator it = responses_.find(GetKey(host, path)); 139 ResponseMap::const_iterator it = responses_.find(GetKey(host, path));
51 if (it == responses_.end()) { 140 if (it == responses_.end()) {
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
119 208
120 void QuicInMemoryCache::InitializeFromDirectory(const string& cache_directory) { 209 void QuicInMemoryCache::InitializeFromDirectory(const string& cache_directory) {
121 if (cache_directory.empty()) { 210 if (cache_directory.empty()) {
122 QUIC_BUG << "cache_directory must not be empty."; 211 QUIC_BUG << "cache_directory must not be empty.";
123 return; 212 return;
124 } 213 }
125 VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: " 214 VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: "
126 << cache_directory; 215 << cache_directory;
127 FilePath directory(FilePath::FromUTF8Unsafe(cache_directory)); 216 FilePath directory(FilePath::FromUTF8Unsafe(cache_directory));
128 base::FileEnumerator file_list(directory, true, base::FileEnumerator::FILES); 217 base::FileEnumerator file_list(directory, true, base::FileEnumerator::FILES);
129 218 list<std::unique_ptr<ResourceFile>> resource_files;
130 for (FilePath file_iter = file_list.Next(); !file_iter.empty(); 219 for (FilePath file_iter = file_list.Next(); !file_iter.empty();
131 file_iter = file_list.Next()) { 220 file_iter = file_list.Next()) {
132 // Need to skip files in .svn directories 221 // Need to skip files in .svn directories
133 if (file_iter.value().find(FILE_PATH_LITERAL("/.svn/")) != string::npos) { 222 if (file_iter.value().find(FILE_PATH_LITERAL("/.svn/")) != string::npos) {
134 continue; 223 continue;
135 } 224 }
136 225
226 std::unique_ptr<ResourceFile> resource_file(
227 new ResourceFileImpl(file_iter));
228
137 // Tease apart filename into host and path. 229 // Tease apart filename into host and path.
138 string file = file_iter.AsUTF8Unsafe(); 230 StringPiece base(resource_file->file_name());
139 file.erase(0, cache_directory.length()); 231 base.remove_prefix(cache_directory.length());
140 if (file[0] == '/') { 232 if (base[0] == '/') {
141 file.erase(0, 1); 233 base.remove_prefix(1);
142 } 234 }
143 235
144 string file_contents; 236 resource_file->SetHostPathFromBase(base);
145 base::ReadFileToString(file_iter, &file_contents); 237 resource_file->Read();
146 int file_len = static_cast<int>(file_contents.length()); 238
147 int headers_end = 239 AddResponse(resource_file->host(), resource_file->path(),
148 HttpUtil::LocateEndOfHeaders(file_contents.data(), file_len); 240 resource_file->spdy_headers(), resource_file->body());
149 if (headers_end < 1) { 241
150 LOG(DFATAL) << "Headers invalid or empty, ignoring: " << file; 242 resource_files.push_back(std::move(resource_file));
151 continue; 243 }
244
245 for (const auto& resource_file : resource_files) {
246 list<ServerPushInfo> push_resources;
247 for (const auto& push_url : resource_file->push_urls()) {
248 GURL url(push_url);
249 const Response* response = GetResponse(url.host(), url.path());
250 if (!response) {
251 QUIC_BUG << "Push URL '" << push_url << "' not found.";
252 return;
253 }
254 push_resources.push_back(ServerPushInfo(url, response->headers(),
255 net::kV3LowestPriority,
256 response->body().as_string()));
152 } 257 }
153 258 MaybeAddServerPushResources(resource_file->host(), resource_file->path(),
154 string raw_headers = 259 push_resources);
155 HttpUtil::AssembleRawHeaders(file_contents.data(), headers_end);
156
157 scoped_refptr<HttpResponseHeaders> response_headers =
158 new HttpResponseHeaders(raw_headers);
159
160 string base;
161 if (response_headers->GetNormalizedHeader("X-Original-Url", &base)) {
162 response_headers->RemoveHeader("X-Original-Url");
163 // Remove the protocol so we can add it below.
164 if (base::StartsWith(base, "https://",
165 base::CompareCase::INSENSITIVE_ASCII)) {
166 base = base.substr(8);
167 } else if (base::StartsWith(base, "http://",
168 base::CompareCase::INSENSITIVE_ASCII)) {
169 base = base.substr(7);
170 }
171 } else {
172 base = file;
173 }
174
175 size_t path_start = base.find_first_of('/');
176 StringPiece host(StringPiece(base).substr(0, path_start));
177 StringPiece path(StringPiece(base).substr(path_start));
178 if (path.back() == ',') {
179 path.remove_suffix(1);
180 }
181 StringPiece body(file_contents.data() + headers_end,
182 file_contents.size() - headers_end);
183 SpdyHeaderBlock header_block;
184 CreateSpdyHeadersFromHttpResponse(*response_headers, HTTP2, &header_block);
185 AddResponse(host, path, header_block, body);
186 } 260 }
187 } 261 }
188 262
189 list<ServerPushInfo> QuicInMemoryCache::GetServerPushResources( 263 list<ServerPushInfo> QuicInMemoryCache::GetServerPushResources(
190 string request_url) { 264 string request_url) {
191 list<ServerPushInfo> resources; 265 list<ServerPushInfo> resources;
192 auto resource_range = server_push_resources_.equal_range(request_url); 266 auto resource_range = server_push_resources_.equal_range(request_url);
193 for (auto it = resource_range.first; it != resource_range.second; ++it) { 267 for (auto it = resource_range.first; it != resource_range.second; ++it) {
194 resources.push_back(it->second); 268 resources.push_back(it->second);
195 } 269 }
270 DVLOG(1) << "Found " << resources.size() << " push resources for "
271 << request_url;
196 return resources; 272 return resources;
197 } 273 }
198 274
199 QuicInMemoryCache::~QuicInMemoryCache() { 275 QuicInMemoryCache::~QuicInMemoryCache() {
200 STLDeleteValues(&responses_); 276 STLDeleteValues(&responses_);
201 } 277 }
202 278
203 void QuicInMemoryCache::AddResponseImpl( 279 void QuicInMemoryCache::AddResponseImpl(
204 StringPiece host, 280 StringPiece host,
205 StringPiece path, 281 StringPiece path,
206 SpecialResponseType response_type, 282 SpecialResponseType response_type,
207 const SpdyHeaderBlock& response_headers, 283 const SpdyHeaderBlock& response_headers,
208 StringPiece response_body, 284 StringPiece response_body,
209 const SpdyHeaderBlock& response_trailers) { 285 const SpdyHeaderBlock& response_trailers) {
210 DCHECK(!host.empty()) << "Host must be populated, e.g. \"www.google.com\""; 286 DCHECK(!host.empty()) << "Host must be populated, e.g. \"www.google.com\"";
211 string key = GetKey(host, path); 287 string key = GetKey(host, path);
212 if (ContainsKey(responses_, key)) { 288 if (ContainsKey(responses_, key)) {
213 QUIC_BUG << "Response for '" << key << "' already exists!"; 289 QUIC_BUG << "Response for '" << key << "' already exists!";
214 return; 290 return;
215 } 291 }
216 Response* new_response = new Response(); 292 Response* new_response = new Response();
217 new_response->set_response_type(response_type); 293 new_response->set_response_type(response_type);
218 new_response->set_headers(response_headers); 294 new_response->set_headers(response_headers);
219 new_response->set_body(response_body); 295 new_response->set_body(response_body);
220 new_response->set_trailers(response_trailers); 296 new_response->set_trailers(response_trailers);
297 DVLOG(1) << "Add response with key " << key;
221 responses_[key] = new_response; 298 responses_[key] = new_response;
222 } 299 }
223 300
224 string QuicInMemoryCache::GetKey(StringPiece host, StringPiece path) const { 301 string QuicInMemoryCache::GetKey(StringPiece host, StringPiece path) const {
225 return host.as_string() + path.as_string(); 302 return host.as_string() + path.as_string();
226 } 303 }
227 304
228 void QuicInMemoryCache::MaybeAddServerPushResources( 305 void QuicInMemoryCache::MaybeAddServerPushResources(
229 StringPiece request_host, 306 StringPiece request_host,
230 StringPiece request_path, 307 StringPiece request_path,
231 list<ServerPushInfo> push_resources) { 308 list<ServerPushInfo> push_resources) {
232 string request_url = request_host.as_string() + request_path.as_string(); 309 string request_url = GetKey(request_host, request_path);
233 310
234 for (const auto& push_resource : push_resources) { 311 for (const auto& push_resource : push_resources) {
235 if (PushResourceExistsInCache(request_url, push_resource)) { 312 if (PushResourceExistsInCache(request_url, push_resource)) {
236 continue; 313 continue;
237 } 314 }
238 315
239 DVLOG(1) << "Add request-resource association."; 316 DVLOG(1) << "Add request-resource association: request url " << request_url
317 << " push url " << push_resource.request_url
318 << " response headers " << push_resource.headers.DebugString();
240 server_push_resources_.insert(std::make_pair(request_url, push_resource)); 319 server_push_resources_.insert(std::make_pair(request_url, push_resource));
241 string host = push_resource.request_url.host(); 320 string host = push_resource.request_url.host();
242 if (host.empty()) { 321 if (host.empty()) {
243 host = request_host.as_string(); 322 host = request_host.as_string();
244 } 323 }
245 string path = push_resource.request_url.path(); 324 string path = push_resource.request_url.path();
246 if (responses_.find(GetKey(host, path)) == responses_.end()) { 325 if (responses_.find(GetKey(host, path)) == responses_.end()) {
247 // Add a server push response to responses map, if it is not in the map. 326 // Add a server push response to responses map, if it is not in the map.
248 SpdyHeaderBlock headers = push_resource.headers; 327 SpdyHeaderBlock headers = push_resource.headers;
249 StringPiece body = push_resource.body; 328 StringPiece body = push_resource.body;
250 DVLOG(1) << "Add response for push resource: host " << host << " path " 329 DVLOG(1) << "Add response for push resource: host " << host << " path "
251 << path << " body " << body; 330 << path;
252 AddResponse(host, path, headers, body); 331 AddResponse(host, path, headers, body);
253 } 332 }
254 } 333 }
255 } 334 }
256 335
257 bool QuicInMemoryCache::PushResourceExistsInCache(string original_request_url, 336 bool QuicInMemoryCache::PushResourceExistsInCache(string original_request_url,
258 ServerPushInfo resource) { 337 ServerPushInfo resource) {
259 auto resource_range = 338 auto resource_range =
260 server_push_resources_.equal_range(original_request_url); 339 server_push_resources_.equal_range(original_request_url);
261 for (auto it = resource_range.first; it != resource_range.second; ++it) { 340 for (auto it = resource_range.first; it != resource_range.second; ++it) {
262 ServerPushInfo push_resource = it->second; 341 ServerPushInfo push_resource = it->second;
263 if (push_resource.request_url.spec() == resource.request_url.spec()) { 342 if (push_resource.request_url.spec() == resource.request_url.spec()) {
264 return true; 343 return true;
265 } 344 }
266 } 345 }
267 return false; 346 return false;
268 } 347 }
269 348
270 } // namespace net 349 } // namespace net
OLDNEW
« no previous file with comments | « net/tools/quic/quic_in_memory_cache.h ('k') | net/tools/quic/quic_in_memory_cache_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698