Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / net / tools / quic / quic_in_memory_cache.cc
blob2ae5c49d9e86378b81d5bfcf1f73c59d6eed9169
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 "base/strings/string_util.h"
12 #include "net/http/http_response_headers.h"
13 #include "net/http/http_util.h"
14 #include "net/spdy/spdy_http_utils.h"
16 using base::FilePath;
17 using base::IntToString;
18 using base::StringPiece;
19 using std::string;
21 namespace net {
22 namespace tools {
24 QuicInMemoryCache::Response::Response() : response_type_(REGULAR_RESPONSE) {}
26 QuicInMemoryCache::Response::~Response() {}
28 // static
29 QuicInMemoryCache* QuicInMemoryCache::GetInstance() {
30 return base::Singleton<QuicInMemoryCache>::get();
33 const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse(
34 StringPiece host,
35 StringPiece path) const {
36 ResponseMap::const_iterator it = responses_.find(GetKey(host, path));
37 if (it == responses_.end()) {
38 if (default_response_.get()) {
39 return default_response_.get();
41 return nullptr;
43 return it->second;
46 void QuicInMemoryCache::AddSimpleResponse(StringPiece host,
47 StringPiece path,
48 int response_code,
49 StringPiece response_detail,
50 StringPiece body) {
51 SpdyHeaderBlock response_headers;
52 response_headers[":version"] = "HTTP/1.1";
53 string status = IntToString(response_code) + " ";
54 response_detail.AppendToString(&status);
55 response_headers[":status"] = status;
56 response_headers["content-length"] =
57 IntToString(static_cast<int>(body.length()));
58 AddResponse(host, path, response_headers, body);
61 void QuicInMemoryCache::AddDefaultResponse(Response* response) {
62 default_response_.reset(response);
65 void QuicInMemoryCache::AddResponse(StringPiece host,
66 StringPiece path,
67 const SpdyHeaderBlock& response_headers,
68 StringPiece response_body) {
69 AddResponseImpl(host, path, REGULAR_RESPONSE, response_headers,
70 response_body);
73 void QuicInMemoryCache::AddSpecialResponse(StringPiece host,
74 StringPiece path,
75 SpecialResponseType response_type) {
76 AddResponseImpl(host, path, response_type, SpdyHeaderBlock(), "");
79 QuicInMemoryCache::QuicInMemoryCache() {}
81 void QuicInMemoryCache::ResetForTests() {
82 STLDeleteValues(&responses_);
85 void QuicInMemoryCache::InitializeFromDirectory(const string& cache_directory) {
86 if (cache_directory.empty()) {
87 LOG(DFATAL) << "cache_directory must not be empty.";
88 return;
90 VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: "
91 << cache_directory;
92 FilePath directory(FilePath::FromUTF8Unsafe(cache_directory));
93 base::FileEnumerator file_list(directory,
94 true,
95 base::FileEnumerator::FILES);
97 for (FilePath file_iter = file_list.Next(); !file_iter.empty();
98 file_iter = file_list.Next()) {
99 // Need to skip files in .svn directories
100 if (file_iter.value().find(FILE_PATH_LITERAL("/.svn/")) != string::npos) {
101 continue;
104 // Tease apart filename into host and path.
105 string file = file_iter.AsUTF8Unsafe();
106 file.erase(0, cache_directory.length());
107 if (file[0] == '/') {
108 file.erase(0, 1);
111 string file_contents;
112 base::ReadFileToString(file_iter, &file_contents);
113 int file_len = static_cast<int>(file_contents.length());
114 int headers_end = HttpUtil::LocateEndOfHeaders(file_contents.data(),
115 file_len);
116 if (headers_end < 1) {
117 LOG(DFATAL) << "Headers invalid or empty, ignoring: " << file;
118 continue;
121 string raw_headers =
122 HttpUtil::AssembleRawHeaders(file_contents.data(), headers_end);
124 scoped_refptr<HttpResponseHeaders> response_headers =
125 new HttpResponseHeaders(raw_headers);
127 string base;
128 if (response_headers->GetNormalizedHeader("X-Original-Url", &base)) {
129 response_headers->RemoveHeader("X-Original-Url");
130 // Remove the protocol so we can add it below.
131 if (base::StartsWith(base, "https://",
132 base::CompareCase::INSENSITIVE_ASCII)) {
133 base = base.substr(8);
134 } else if (base::StartsWith(base, "http://",
135 base::CompareCase::INSENSITIVE_ASCII)) {
136 base = base.substr(7);
138 } else {
139 base = file;
142 size_t path_start = base.find_first_of('/');
143 StringPiece host(StringPiece(base).substr(0, path_start));
144 StringPiece path(StringPiece(base).substr(path_start));
145 if (path[path.length() - 1] == ',') {
146 path.remove_suffix(1);
149 StringPiece body(file_contents.data() + headers_end,
150 file_contents.size() - headers_end);
151 // QUIC_VERSION_24 and below use SPDY/3 headers, which includes the
152 // status reason in :status, and "HTTP/1.1" in :version. Since this
153 // format is a strict superset of SPDY/4 headers, the cache uses this
154 // format to store response. Once SPDY/3 responses are no longer
155 // required, the cache can store SPDY/4 headers.
156 SpdyHeaderBlock header_block;
157 CreateSpdyHeadersFromHttpResponse(*response_headers, HTTP2, &header_block);
158 AddResponse(host, path, header_block, body);
162 QuicInMemoryCache::~QuicInMemoryCache() {
163 STLDeleteValues(&responses_);
166 void QuicInMemoryCache::AddResponseImpl(
167 StringPiece host,
168 StringPiece path,
169 SpecialResponseType response_type,
170 const SpdyHeaderBlock& response_headers,
171 StringPiece response_body) {
172 string key = GetKey(host, path);
173 VLOG(1) << "Adding response for: " << key;
174 if (ContainsKey(responses_, key)) {
175 LOG(DFATAL) << "Response for '" << key << "' already exists!";
176 return;
178 Response* new_response = new Response();
179 new_response->set_response_type(response_type);
180 new_response->set_headers(response_headers);
181 new_response->set_body(response_body);
182 responses_[key] = new_response;
185 string QuicInMemoryCache::GetKey(StringPiece host, StringPiece path) const {
186 return host.as_string() + path.as_string();
189 } // namespace tools
190 } // namespace net