summaryrefslogtreecommitdiffstats
path: root/chromium/net/quic/quic_in_memory_cache.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/quic/quic_in_memory_cache.cc')
-rw-r--r--chromium/net/quic/quic_in_memory_cache.cc242
1 files changed, 242 insertions, 0 deletions
diff --git a/chromium/net/quic/quic_in_memory_cache.cc b/chromium/net/quic/quic_in_memory_cache.cc
new file mode 100644
index 00000000000..45d25b24e40
--- /dev/null
+++ b/chromium/net/quic/quic_in_memory_cache.cc
@@ -0,0 +1,242 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_in_memory_cache.h"
+
+#include "base/file_util.h"
+#include "base/files/file_enumerator.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/tools/balsa/balsa_headers.h"
+
+using base::FilePath;
+using base::StringPiece;
+using std::string;
+
+// Specifies the directory used during QuicInMemoryCache
+// construction to seed the cache. Cache directory can be
+// generated using `wget -p --save-headers <url>
+
+namespace net {
+
+namespace {
+
+const FilePath::CharType* g_quic_in_memory_cache_dir = FILE_PATH_LITERAL("");
+
+// BalsaVisitor implementation (glue) which caches response bodies.
+class CachingBalsaVisitor : public NoOpBalsaVisitor {
+ public:
+ CachingBalsaVisitor() : done_framing_(false) {}
+ virtual void ProcessBodyData(const char* input, size_t size) OVERRIDE {
+ AppendToBody(input, size);
+ }
+ virtual void MessageDone() OVERRIDE {
+ done_framing_ = true;
+ }
+ virtual void HandleHeaderError(BalsaFrame* framer) OVERRIDE {
+ UnhandledError();
+ }
+ virtual void HandleHeaderWarning(BalsaFrame* framer) OVERRIDE {
+ UnhandledError();
+ }
+ virtual void HandleChunkingError(BalsaFrame* framer) OVERRIDE {
+ UnhandledError();
+ }
+ virtual void HandleBodyError(BalsaFrame* framer) OVERRIDE {
+ UnhandledError();
+ }
+ void UnhandledError() {
+ LOG(DFATAL) << "Unhandled error framing HTTP.";
+ }
+ void AppendToBody(const char* input, size_t size) {
+ body_.append(input, size);
+ }
+ bool done_framing() const { return done_framing_; }
+ const string& body() const { return body_; }
+
+ private:
+ bool done_framing_;
+ string body_;
+};
+
+} // namespace
+
+// static
+QuicInMemoryCache* QuicInMemoryCache::GetInstance() {
+ return Singleton<QuicInMemoryCache>::get();
+}
+
+const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse(
+ const BalsaHeaders& request_headers) const {
+ ResponseMap::const_iterator it = responses_.find(GetKey(request_headers));
+ if (it == responses_.end()) {
+ return NULL;
+ }
+ return it->second;
+}
+
+void QuicInMemoryCache::AddSimpleResponse(StringPiece method,
+ StringPiece path,
+ StringPiece version,
+ StringPiece response_code,
+ StringPiece response_detail,
+ StringPiece body) {
+ BalsaHeaders request_headers, response_headers;
+ request_headers.SetRequestFirstlineFromStringPieces(method,
+ path,
+ version);
+ response_headers.SetRequestFirstlineFromStringPieces(version,
+ response_code,
+ response_detail);
+ response_headers.AppendHeader("content-length",
+ base::IntToString(body.length()));
+
+ AddResponse(request_headers, response_headers, body);
+}
+
+void QuicInMemoryCache::AddResponse(const BalsaHeaders& request_headers,
+ const BalsaHeaders& response_headers,
+ StringPiece response_body) {
+ VLOG(1) << "Adding response for: " << GetKey(request_headers);
+ if (ContainsKey(responses_, GetKey(request_headers))) {
+ LOG(DFATAL) << "Response for given request already exists!";
+ return;
+ }
+ Response* new_response = new Response();
+ new_response->set_headers(response_headers);
+ new_response->set_body(response_body);
+ responses_[GetKey(request_headers)] = new_response;
+}
+
+void QuicInMemoryCache::AddSpecialResponse(StringPiece method,
+ StringPiece path,
+ StringPiece version,
+ SpecialResponseType response_type) {
+ BalsaHeaders request_headers, response_headers;
+ request_headers.SetRequestFirstlineFromStringPieces(method,
+ path,
+ version);
+ AddResponse(request_headers, response_headers, "");
+ responses_[GetKey(request_headers)]->response_type_ = response_type;
+}
+
+QuicInMemoryCache::QuicInMemoryCache() {
+ Initialize();
+}
+
+void QuicInMemoryCache::ResetForTests() {
+ STLDeleteValues(&responses_);
+ Initialize();
+}
+
+void QuicInMemoryCache::Initialize() {
+ // If there's no defined cache dir, we have no initialization to do.
+ if (g_quic_in_memory_cache_dir[0] == '\0') {
+ VLOG(1) << "No cache directory found. Skipping initialization.";
+ return;
+ }
+ VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: "
+ << g_quic_in_memory_cache_dir;
+
+ FilePath directory(g_quic_in_memory_cache_dir);
+ base::FileEnumerator file_list(directory,
+ true,
+ base::FileEnumerator::FILES);
+
+ FilePath file = file_list.Next();
+ while (!file.empty()) {
+ // Need to skip files in .svn directories
+ if (file.value().find(FILE_PATH_LITERAL("/.svn/")) != std::string::npos) {
+ file = file_list.Next();
+ continue;
+ }
+
+ BalsaHeaders request_headers, response_headers;
+
+ string file_contents;
+ base::ReadFileToString(file, &file_contents);
+
+ // Frame HTTP.
+ CachingBalsaVisitor caching_visitor;
+ BalsaFrame framer;
+ framer.set_balsa_headers(&response_headers);
+ framer.set_balsa_visitor(&caching_visitor);
+ size_t processed = 0;
+ while (processed < file_contents.length() &&
+ !caching_visitor.done_framing()) {
+ processed += framer.ProcessInput(file_contents.c_str() + processed,
+ file_contents.length() - processed);
+ }
+
+ string response_headers_str;
+ response_headers.DumpToString(&response_headers_str);
+ if (!caching_visitor.done_framing()) {
+ LOG(DFATAL) << "Did not frame entire message from file: " << file.value()
+ << " (" << processed << " of " << file_contents.length()
+ << " bytes).";
+ }
+ if (processed < file_contents.length()) {
+ // Didn't frame whole file. Assume remainder is body.
+ // This sometimes happens as a result of incompatibilities between
+ // BalsaFramer and wget's serialization of HTTP sans content-length.
+ caching_visitor.AppendToBody(file_contents.c_str() + processed,
+ file_contents.length() - processed);
+ processed += file_contents.length();
+ }
+
+ StringPiece base = file.AsUTF8Unsafe();
+ if (response_headers.HasHeader("X-Original-Url")) {
+ base = response_headers.GetHeader("X-Original-Url");
+ response_headers.RemoveAllOfHeader("X-Original-Url");
+ // Remove the protocol so that the string is of the form host + path,
+ // which is parsed properly below.
+ if (StringPieceUtils::StartsWithIgnoreCase(base, "https://")) {
+ base.remove_prefix(8);
+ } else if (StringPieceUtils::StartsWithIgnoreCase(base, "http://")) {
+ base.remove_prefix(7);
+ }
+ }
+ int path_start = base.find_first_of('/');
+ DCHECK_LT(0, path_start);
+ StringPiece host(base.substr(0, path_start));
+ StringPiece path(base.substr(path_start));
+ if (path[path.length() - 1] == ',') {
+ path.remove_suffix(1);
+ }
+ // Set up request headers. Assume method is GET and protocol is HTTP/1.1.
+ request_headers.SetRequestFirstlineFromStringPieces("GET",
+ path,
+ "HTTP/1.1");
+ request_headers.ReplaceOrAppendHeader("host", host);
+
+ VLOG(1) << "Inserting 'http://" << GetKey(request_headers)
+ << "' into QuicInMemoryCache.";
+
+ AddResponse(request_headers, response_headers, caching_visitor.body());
+
+ file = file_list.Next();
+ }
+}
+
+QuicInMemoryCache::~QuicInMemoryCache() {
+ STLDeleteValues(&responses_);
+}
+
+string QuicInMemoryCache::GetKey(const BalsaHeaders& request_headers) const {
+ StringPiece uri = request_headers.request_uri();
+ if (uri.size() == 0) {
+ return "";
+ }
+ StringPiece host;
+ if (uri[0] == '/') {
+ host = request_headers.GetHeader("host");
+ } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "https://")) {
+ uri.remove_prefix(8);
+ } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "http://")) {
+ uri.remove_prefix(7);
+ }
+ return host.as_string() + uri.as_string();
+}
+
+} // namespace net