Initialize UserMetricsRecorder on Windows Ash and Ozone
[chromium-blink-merge.git] / net / tools / quic / quic_in_memory_cache.cc
blobcb96335819780352f448c80e26e43086ff3a9d32
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
3 // found in the LICENSE file.
5 #include "net/tools/quic/quic_in_memory_cache.h"
7 #include "base/files/file_enumerator.h"
8 #include "base/files/file_util.h"
9 #include "base/stl_util.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "net/tools/balsa/balsa_headers.h"
13 using base::FilePath;
14 using base::StringPiece;
15 using std::string;
17 // Specifies the directory used during QuicInMemoryCache
18 // construction to seed the cache. Cache directory can be
19 // generated using `wget -p --save-headers <url>
21 namespace net {
22 namespace tools {
24 std::string FLAGS_quic_in_memory_cache_dir = "";
26 namespace {
28 // BalsaVisitor implementation (glue) which caches response bodies.
29 class CachingBalsaVisitor : public NoOpBalsaVisitor {
30 public:
31 CachingBalsaVisitor() : done_framing_(false) {}
32 void ProcessBodyData(const char* input, size_t size) override {
33 AppendToBody(input, size);
35 void MessageDone() override { done_framing_ = true; }
36 void HandleHeaderError(BalsaFrame* framer) override { UnhandledError(); }
37 void HandleHeaderWarning(BalsaFrame* framer) override { UnhandledError(); }
38 void HandleChunkingError(BalsaFrame* framer) override { UnhandledError(); }
39 void HandleBodyError(BalsaFrame* framer) override { UnhandledError(); }
40 void UnhandledError() {
41 LOG(DFATAL) << "Unhandled error framing HTTP.";
43 void AppendToBody(const char* input, size_t size) {
44 body_.append(input, size);
46 bool done_framing() const { return done_framing_; }
47 const string& body() const { return body_; }
49 private:
50 bool done_framing_;
51 string body_;
54 } // namespace
56 // static
57 QuicInMemoryCache* QuicInMemoryCache::GetInstance() {
58 return Singleton<QuicInMemoryCache>::get();
61 const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse(
62 const BalsaHeaders& request_headers) const {
63 ResponseMap::const_iterator it = responses_.find(GetKey(request_headers));
64 if (it == responses_.end()) {
65 return nullptr;
67 return it->second;
70 void QuicInMemoryCache::AddSimpleResponse(StringPiece method,
71 StringPiece path,
72 StringPiece version,
73 StringPiece response_code,
74 StringPiece response_detail,
75 StringPiece body) {
76 BalsaHeaders request_headers, response_headers;
77 request_headers.SetRequestFirstlineFromStringPieces(method,
78 path,
79 version);
80 response_headers.SetRequestFirstlineFromStringPieces(version,
81 response_code,
82 response_detail);
83 response_headers.AppendHeader("content-length",
84 base::IntToString(body.length()));
86 AddResponse(request_headers, response_headers, body);
89 void QuicInMemoryCache::AddResponse(const BalsaHeaders& request_headers,
90 const BalsaHeaders& response_headers,
91 StringPiece response_body) {
92 VLOG(1) << "Adding response for: " << GetKey(request_headers);
93 if (ContainsKey(responses_, GetKey(request_headers))) {
94 LOG(DFATAL) << "Response for given request already exists!";
95 return;
97 Response* new_response = new Response();
98 new_response->set_headers(response_headers);
99 new_response->set_body(response_body);
100 responses_[GetKey(request_headers)] = new_response;
103 void QuicInMemoryCache::AddSpecialResponse(StringPiece method,
104 StringPiece path,
105 StringPiece version,
106 SpecialResponseType response_type) {
107 BalsaHeaders request_headers, response_headers;
108 request_headers.SetRequestFirstlineFromStringPieces(method,
109 path,
110 version);
111 AddResponse(request_headers, response_headers, "");
112 responses_[GetKey(request_headers)]->response_type_ = response_type;
115 QuicInMemoryCache::QuicInMemoryCache() {
116 Initialize();
119 void QuicInMemoryCache::ResetForTests() {
120 STLDeleteValues(&responses_);
121 Initialize();
124 void QuicInMemoryCache::Initialize() {
125 // If there's no defined cache dir, we have no initialization to do.
126 if (FLAGS_quic_in_memory_cache_dir.empty()) {
127 VLOG(1) << "No cache directory found. Skipping initialization.";
128 return;
130 VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: "
131 << FLAGS_quic_in_memory_cache_dir;
133 FilePath directory(FLAGS_quic_in_memory_cache_dir);
134 base::FileEnumerator file_list(directory,
135 true,
136 base::FileEnumerator::FILES);
138 FilePath file = file_list.Next();
139 while (!file.empty()) {
140 // Need to skip files in .svn directories
141 if (file.value().find("/.svn/") != std::string::npos) {
142 file = file_list.Next();
143 continue;
146 BalsaHeaders request_headers, response_headers;
148 string file_contents;
149 base::ReadFileToString(file, &file_contents);
151 // Frame HTTP.
152 CachingBalsaVisitor caching_visitor;
153 BalsaFrame framer;
154 framer.set_balsa_headers(&response_headers);
155 framer.set_balsa_visitor(&caching_visitor);
156 size_t processed = 0;
157 while (processed < file_contents.length() &&
158 !caching_visitor.done_framing()) {
159 processed += framer.ProcessInput(file_contents.c_str() + processed,
160 file_contents.length() - processed);
163 if (!caching_visitor.done_framing()) {
164 LOG(DFATAL) << "Did not frame entire message from file: " << file.value()
165 << " (" << processed << " of " << file_contents.length()
166 << " bytes).";
168 if (processed < file_contents.length()) {
169 // Didn't frame whole file. Assume remainder is body.
170 // This sometimes happens as a result of incompatibilities between
171 // BalsaFramer and wget's serialization of HTTP sans content-length.
172 caching_visitor.AppendToBody(file_contents.c_str() + processed,
173 file_contents.length() - processed);
174 processed += file_contents.length();
177 StringPiece base = file.value();
178 if (response_headers.HasHeader("X-Original-Url")) {
179 base = response_headers.GetHeader("X-Original-Url");
180 response_headers.RemoveAllOfHeader("X-Original-Url");
181 // Remove the protocol so that the string is of the form host + path,
182 // which is parsed properly below.
183 if (StringPieceUtils::StartsWithIgnoreCase(base, "https://")) {
184 base.remove_prefix(8);
185 } else if (StringPieceUtils::StartsWithIgnoreCase(base, "http://")) {
186 base.remove_prefix(7);
189 int path_start = base.find_first_of('/');
190 DCHECK_LT(0, path_start);
191 StringPiece host(base.substr(0, path_start));
192 StringPiece path(base.substr(path_start));
193 if (path[path.length() - 1] == ',') {
194 path.remove_suffix(1);
196 // Set up request headers. Assume method is GET and protocol is HTTP/1.1.
197 request_headers.SetRequestFirstlineFromStringPieces("GET",
198 path,
199 "HTTP/1.1");
200 request_headers.ReplaceOrAppendHeader("host", host);
202 VLOG(1) << "Inserting 'http://" << GetKey(request_headers)
203 << "' into QuicInMemoryCache.";
205 AddResponse(request_headers, response_headers, caching_visitor.body());
207 file = file_list.Next();
211 QuicInMemoryCache::~QuicInMemoryCache() {
212 STLDeleteValues(&responses_);
215 string QuicInMemoryCache::GetKey(const BalsaHeaders& request_headers) const {
216 StringPiece uri = request_headers.request_uri();
217 if (uri.size() == 0) {
218 return "";
220 StringPiece host;
221 if (uri[0] == '/') {
222 host = request_headers.GetHeader("host");
223 } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "https://")) {
224 uri.remove_prefix(8);
225 } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "http://")) {
226 uri.remove_prefix(7);
228 return host.as_string() + uri.as_string();
231 } // namespace tools
232 } // namespace net