1 // Copyright 2014 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/quic/quic_in_memory_cache.h"
7 #include "base/files/file_enumerator.h"
8 #include "base/stl_util.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h"
11 #include "net/http/http_util.h"
15 using base::StringPiece
;
18 // Specifies the directory used during QuicInMemoryCache
19 // construction to seed the cache. Cache directory can be
20 // generated using `wget -p --save-headers <url>
24 FilePath::StringType g_quic_in_memory_cache_dir
= FILE_PATH_LITERAL("");
26 QuicInMemoryCache::Response::Response() : response_type_(REGULAR_RESPONSE
) {
29 QuicInMemoryCache::Response::~Response() {
33 QuicInMemoryCache
* QuicInMemoryCache::GetInstance() {
34 return Singleton
<QuicInMemoryCache
>::get();
37 const QuicInMemoryCache::Response
* QuicInMemoryCache::GetResponse(
38 const GURL
& url
) const {
39 ResponseMap::const_iterator it
= responses_
.find(GetKey(url
));
40 if (it
== responses_
.end()) {
46 void QuicInMemoryCache::AddSimpleResponse(StringPiece path
,
48 StringPiece response_code
,
49 StringPiece response_detail
,
51 GURL
url("http://" + path
.as_string());
53 string status_line
= version
.as_string() + " " +
54 response_code
.as_string() + " " +
55 response_detail
.as_string();
57 string header
= "content-length: " +
58 base::Uint64ToString(static_cast<uint64
>(body
.length()));
60 scoped_refptr
<HttpResponseHeaders
> response_headers
=
61 new HttpResponseHeaders(status_line
+ '\0' + header
+ '\0' + '\0');
63 AddResponse(url
, response_headers
, body
);
66 void QuicInMemoryCache::AddResponse(
68 scoped_refptr
<HttpResponseHeaders
> response_headers
,
69 StringPiece response_body
) {
70 string key
= GetKey(url
);
71 VLOG(1) << "Adding response for: " << key
;
72 if (ContainsKey(responses_
, key
)) {
73 LOG(DFATAL
) << "Response for given request already exists!";
76 Response
* new_response
= new Response();
77 new_response
->set_headers(response_headers
);
78 new_response
->set_body(response_body
);
79 responses_
[key
] = new_response
;
82 void QuicInMemoryCache::AddSpecialResponse(StringPiece path
,
83 SpecialResponseType response_type
) {
84 GURL
url("http://" + path
.as_string());
86 AddResponse(url
, nullptr, string());
87 responses_
[GetKey(url
)]->response_type_
= response_type
;
90 QuicInMemoryCache::QuicInMemoryCache() {
94 void QuicInMemoryCache::ResetForTests() {
95 STLDeleteValues(&responses_
);
99 void QuicInMemoryCache::Initialize() {
100 // If there's no defined cache dir, we have no initialization to do.
101 if (g_quic_in_memory_cache_dir
.size() == 0) {
102 VLOG(1) << "No cache directory found. Skipping initialization.";
105 VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: "
106 << g_quic_in_memory_cache_dir
;
108 FilePath
directory(g_quic_in_memory_cache_dir
);
109 base::FileEnumerator
file_list(directory
,
111 base::FileEnumerator::FILES
);
113 FilePath file
= file_list
.Next();
114 for (; !file
.empty(); file
= file_list
.Next()) {
115 // Need to skip files in .svn directories
116 if (file
.value().find(FILE_PATH_LITERAL("/.svn/")) != string::npos
) {
120 string file_contents
;
121 base::ReadFileToString(file
, &file_contents
);
123 if (file_contents
.length() > INT_MAX
) {
124 LOG(WARNING
) << "File '" << file
.value() << "' too large: "
125 << file_contents
.length();
128 int file_len
= static_cast<int>(file_contents
.length());
130 int headers_end
= HttpUtil::LocateEndOfHeaders(file_contents
.data(),
132 if (headers_end
< 1) {
133 LOG(DFATAL
) << "Headers invalid or empty, ignoring: " << file
.value();
138 HttpUtil::AssembleRawHeaders(file_contents
.data(), headers_end
);
140 scoped_refptr
<HttpResponseHeaders
> response_headers
=
141 new HttpResponseHeaders(raw_headers
);
144 if (response_headers
->GetNormalizedHeader("X-Original-Url", &base
)) {
145 response_headers
->RemoveHeader("X-Original-Url");
146 // Remove the protocol so we can add it below.
147 if (StartsWithASCII(base
, "https://", false)) {
148 base
= base
.substr(8);
149 } else if (StartsWithASCII(base
, "http://", false)) {
150 base
= base
.substr(7);
153 base
= file
.AsUTF8Unsafe();
155 if (base
.length() == 0 || base
[0] == '/') {
156 LOG(DFATAL
) << "Invalid path, ignoring: " << base
;
159 if (base
[base
.length() - 1] == ',') {
160 base
= base
.substr(0, base
.length() - 1);
163 GURL
url("http://" + base
);
165 VLOG(1) << "Inserting '" << GetKey(url
) << "' into QuicInMemoryCache.";
167 StringPiece
body(file_contents
.data() + headers_end
,
168 file_contents
.size() - headers_end
);
170 AddResponse(url
, response_headers
, body
);
174 QuicInMemoryCache::~QuicInMemoryCache() {
175 STLDeleteValues(&responses_
);
178 string
QuicInMemoryCache::GetKey(const GURL
& url
) const {
179 // Take everything but the scheme portion of the URL.
180 return url
.host() + url
.PathForRequest();