Windows should animate when they are about to get docked at screen edges.
[chromium-blink-merge.git] / net / tools / quic / quic_in_memory_cache.cc
blobb840d79370d03a989d2bad22aa9fa56883fc60d2
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/file_util.h"
8 #include "base/files/file_enumerator.h"
9 #include "base/stl_util.h"
11 using base::FilePath;
12 using base::StringPiece;
13 using std::string;
15 // Specifies the directory used during QuicInMemoryCache
16 // construction to seed the cache. Cache directory can be
17 // generated using `wget -p --save-headers <url>
19 namespace net {
20 namespace tools {
22 std::string FLAGS_quic_in_memory_cache_dir = "/tmp/quic-data";
24 namespace {
26 // BalsaVisitor implementation (glue) which caches response bodies.
27 class CachingBalsaVisitor : public BalsaVisitorInterface {
28 public:
29 CachingBalsaVisitor() : done_framing_(false) {}
30 virtual void ProcessBodyData(const char* input, size_t size) OVERRIDE {
31 AppendToBody(input, size);
33 virtual void ProcessTrailers(const BalsaHeaders& trailer) {
34 LOG(DFATAL) << "Trailers not supported.";
36 virtual void MessageDone() OVERRIDE {
37 done_framing_ = true;
39 virtual void HandleHeaderError(BalsaFrame* framer) OVERRIDE {
40 UnhandledError();
42 virtual void HandleHeaderWarning(BalsaFrame* framer) OVERRIDE {
43 UnhandledError();
45 virtual void HandleTrailerError(BalsaFrame* framer) { UnhandledError(); }
46 virtual void HandleTrailerWarning(BalsaFrame* framer) { UnhandledError(); }
47 virtual void HandleChunkingError(BalsaFrame* framer) OVERRIDE {
48 UnhandledError();
50 virtual void HandleBodyError(BalsaFrame* framer) OVERRIDE {
51 UnhandledError();
53 void UnhandledError() {
54 LOG(DFATAL) << "Unhandled error framing HTTP.";
56 virtual void ProcessBodyInput(const char*, size_t) OVERRIDE {}
57 virtual void ProcessHeaderInput(const char*, size_t) OVERRIDE {}
58 virtual void ProcessTrailerInput(const char*, size_t) OVERRIDE {}
59 virtual void ProcessHeaders(const net::BalsaHeaders&) OVERRIDE {}
60 virtual void ProcessRequestFirstLine(
61 const char*, size_t, const char*, size_t,
62 const char*, size_t, const char*, size_t) OVERRIDE {}
63 virtual void ProcessResponseFirstLine(
64 const char*, size_t, const char*,
65 size_t, const char*, size_t, const char*, size_t) OVERRIDE {}
66 virtual void ProcessChunkLength(size_t) OVERRIDE {}
67 virtual void ProcessChunkExtensions(const char*, size_t) OVERRIDE {}
68 virtual void HeaderDone() OVERRIDE {}
70 void AppendToBody(const char* input, size_t size) {
71 body_.append(input, size);
73 bool done_framing() const { return done_framing_; }
74 const string& body() const { return body_; }
76 private:
77 bool done_framing_;
78 string body_;
81 } // namespace
83 QuicInMemoryCache* QuicInMemoryCache::GetInstance() {
84 return Singleton<QuicInMemoryCache>::get();
87 const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse(
88 const BalsaHeaders& request_headers) const {
89 ResponseMap::const_iterator it = responses_.find(GetKey(request_headers));
90 if (it == responses_.end()) {
91 return NULL;
93 return it->second;
96 void QuicInMemoryCache::AddResponse(const BalsaHeaders& request_headers,
97 const BalsaHeaders& response_headers,
98 StringPiece response_body) {
99 LOG(INFO) << "Adding response for: " << GetKey(request_headers);
100 if (ContainsKey(responses_, GetKey(request_headers))) {
101 LOG(DFATAL) << "Response for given request already exists!";
103 Response* new_response = new Response();
104 new_response->set_headers(response_headers);
105 new_response->set_body(response_body);
106 responses_[GetKey(request_headers)] = new_response;
109 void QuicInMemoryCache::ResetForTests() {
110 STLDeleteValues(&responses_);
111 Initialize();
114 QuicInMemoryCache::QuicInMemoryCache() {
115 Initialize();
118 void QuicInMemoryCache::Initialize() {
119 // If there's no defined cache dir, we have no initialization to do.
120 if (FLAGS_quic_in_memory_cache_dir.empty()) {
121 LOG(WARNING) << "No cache directory found. Skipping initialization.";
122 return;
124 LOG(INFO) << "Attempting to initialize QuicInMemoryCache from directory: "
125 << FLAGS_quic_in_memory_cache_dir;
127 FilePath directory(FLAGS_quic_in_memory_cache_dir);
128 base::FileEnumerator file_list(directory,
129 true,
130 base::FileEnumerator::FILES);
132 FilePath file = file_list.Next();
133 while (!file.empty()) {
134 // Need to skip files in .svn directories
135 if (file.value().find("/.svn/") != std::string::npos) {
136 file = file_list.Next();
137 continue;
140 BalsaHeaders request_headers, response_headers;
142 string file_contents;
143 file_util::ReadFileToString(file, &file_contents);
145 // Frame HTTP.
146 CachingBalsaVisitor caching_visitor;
147 BalsaFrame framer;
148 framer.set_balsa_headers(&response_headers);
149 framer.set_balsa_visitor(&caching_visitor);
150 size_t processed = 0;
151 while (processed < file_contents.length() &&
152 !caching_visitor.done_framing()) {
153 processed += framer.ProcessInput(file_contents.c_str() + processed,
154 file_contents.length() - processed);
157 string response_headers_str;
158 response_headers.DumpToString(&response_headers_str);
159 if (!caching_visitor.done_framing()) {
160 LOG(DFATAL) << "Did not frame entire message from file: " << file.value()
161 << " (" << processed << " of " << file_contents.length()
162 << " bytes).";
164 if (processed < file_contents.length()) {
165 // Didn't frame whole file. Assume remainder is body.
166 // This sometimes happens as a result of incompatibilities between
167 // BalsaFramer and wget's serialization of HTTP sans content-length.
168 caching_visitor.AppendToBody(file_contents.c_str() + processed,
169 file_contents.length() - processed);
170 processed += file_contents.length();
173 StringPiece base = file.value();
174 if (response_headers.HasHeader("X-Original-Url")) {
175 base = response_headers.GetHeader("X-Original-Url");
176 response_headers.RemoveAllOfHeader("X-Original-Url");
177 // Remove the protocol so that the string is of the form host + path,
178 // which is parsed properly below.
179 if (StringPieceUtils::StartsWithIgnoreCase(base, "https://")) {
180 base.remove_prefix(8);
181 } else if (StringPieceUtils::StartsWithIgnoreCase(base, "http://")) {
182 base.remove_prefix(7);
185 int path_start = base.find_first_of('/');
186 DCHECK_LT(0, path_start);
187 StringPiece host(base.substr(0, path_start));
188 StringPiece path(base.substr(path_start));
189 if (path[path.length() - 1] == ',') {
190 path.remove_suffix(1);
192 // Set up request headers. Assume method is GET and protocol is HTTP/1.1.
193 request_headers.SetRequestFirstlineFromStringPieces("GET",
194 path,
195 "HTTP/1.1");
196 request_headers.ReplaceOrAppendHeader("host", host);
198 LOG(INFO) << "Inserting 'http://" << GetKey(request_headers)
199 << "' into QuicInMemoryCache.";
201 AddResponse(request_headers, response_headers, caching_visitor.body());
203 file = file_list.Next();
207 QuicInMemoryCache::~QuicInMemoryCache() {
208 STLDeleteValues(&responses_);
211 string QuicInMemoryCache::GetKey(const BalsaHeaders& request_headers) const {
212 StringPiece uri = request_headers.request_uri();
213 StringPiece host;
214 if (uri[0] == '/') {
215 host = request_headers.GetHeader("host");
216 } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "https://")) {
217 uri.remove_prefix(8);
218 } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "http://")) {
219 uri.remove_prefix(7);
221 return host.as_string() + uri.as_string();
224 } // namespace tools
225 } // namespace net