OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/tools/quic/quic_in_memory_cache.h" | 5 #include "net/tools/quic/quic_in_memory_cache.h" |
6 | 6 |
7 #include "base/files/file_enumerator.h" | 7 #include "base/files/file_enumerator.h" |
8 #include "base/files/file_util.h" | 8 #include "base/files/file_util.h" |
9 #include "base/stl_util.h" | 9 #include "base/stl_util.h" |
10 #include "base/strings/string_number_conversions.h" | 10 #include "base/strings/string_number_conversions.h" |
11 #include "net/tools/balsa/balsa_frame.h" | 11 #include "base/strings/string_util.h" |
12 #include "net/tools/balsa/balsa_headers.h" | 12 #include "net/http/http_response_headers.h" |
13 #include "net/tools/balsa/noop_balsa_visitor.h" | 13 #include "net/http/http_util.h" |
14 #include "net/tools/quic/spdy_utils.h" | 14 #include "net/spdy/spdy_http_utils.h" |
15 | 15 |
16 using base::FilePath; | 16 using base::FilePath; |
| 17 using base::IntToString; |
17 using base::StringPiece; | 18 using base::StringPiece; |
18 using std::string; | 19 using std::string; |
19 | 20 |
20 namespace net { | 21 namespace net { |
21 namespace tools { | 22 namespace tools { |
22 | 23 |
23 namespace { | |
24 | |
25 // BalsaVisitor implementation (glue) which caches response bodies. | |
26 class CachingBalsaVisitor : public NoOpBalsaVisitor { | |
27 public: | |
28 CachingBalsaVisitor() : done_framing_(false) {} | |
29 void ProcessBodyData(const char* input, size_t size) override { | |
30 AppendToBody(input, size); | |
31 } | |
32 void MessageDone() override { done_framing_ = true; } | |
33 void HandleHeaderError(BalsaFrame* framer) override { UnhandledError(); } | |
34 void HandleHeaderWarning(BalsaFrame* framer) override { UnhandledError(); } | |
35 void HandleChunkingError(BalsaFrame* framer) override { UnhandledError(); } | |
36 void HandleBodyError(BalsaFrame* framer) override { UnhandledError(); } | |
37 void UnhandledError() { | |
38 LOG(DFATAL) << "Unhandled error framing HTTP."; | |
39 } | |
40 void AppendToBody(const char* input, size_t size) { | |
41 body_.append(input, size); | |
42 } | |
43 bool done_framing() const { return done_framing_; } | |
44 const string& body() const { return body_; } | |
45 | |
46 private: | |
47 bool done_framing_; | |
48 string body_; | |
49 }; | |
50 | |
51 } // namespace | |
52 | |
53 QuicInMemoryCache::Response::Response() : response_type_(REGULAR_RESPONSE) {} | 24 QuicInMemoryCache::Response::Response() : response_type_(REGULAR_RESPONSE) {} |
54 | 25 |
55 QuicInMemoryCache::Response::~Response() {} | 26 QuicInMemoryCache::Response::~Response() {} |
56 | 27 |
57 // static | 28 // static |
58 QuicInMemoryCache* QuicInMemoryCache::GetInstance() { | 29 QuicInMemoryCache* QuicInMemoryCache::GetInstance() { |
59 return Singleton<QuicInMemoryCache>::get(); | 30 return Singleton<QuicInMemoryCache>::get(); |
60 } | 31 } |
61 | 32 |
62 const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse( | 33 const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse( |
63 StringPiece host, | 34 StringPiece host, |
64 StringPiece path) const { | 35 StringPiece path) const { |
65 ResponseMap::const_iterator it = responses_.find(GetKey(host, path)); | 36 ResponseMap::const_iterator it = responses_.find(GetKey(host, path)); |
66 if (it == responses_.end()) { | 37 if (it == responses_.end()) { |
67 return nullptr; | 38 return nullptr; |
68 } | 39 } |
69 return it->second; | 40 return it->second; |
70 } | 41 } |
71 | 42 |
72 void QuicInMemoryCache::AddSimpleResponse(StringPiece host, | 43 void QuicInMemoryCache::AddSimpleResponse(StringPiece host, |
73 StringPiece path, | 44 StringPiece path, |
74 int response_code, | 45 int response_code, |
75 StringPiece response_detail, | 46 StringPiece response_detail, |
76 StringPiece body) { | 47 StringPiece body) { |
77 SpdyHeaderBlock response_headers; | 48 SpdyHeaderBlock response_headers; |
78 response_headers[":version"] = "HTTP/1.1"; | 49 response_headers[":version"] = "HTTP/1.1"; |
79 string status = base::IntToString(response_code) + " "; | 50 string status = IntToString(response_code) + " "; |
80 response_detail.AppendToString(&status); | 51 response_detail.AppendToString(&status); |
81 response_headers[":status"] = status; | 52 response_headers[":status"] = status; |
82 response_headers["content-length"] = base::IntToString(body.length()); | 53 response_headers["content-length"] = |
| 54 IntToString(static_cast<int>(body.length())); |
83 AddResponse(host, path, response_headers, body); | 55 AddResponse(host, path, response_headers, body); |
84 } | 56 } |
85 | 57 |
86 void QuicInMemoryCache::AddResponse(StringPiece host, | 58 void QuicInMemoryCache::AddResponse(StringPiece host, |
87 StringPiece path, | 59 StringPiece path, |
88 const SpdyHeaderBlock& response_headers, | 60 const SpdyHeaderBlock& response_headers, |
89 StringPiece response_body) { | 61 StringPiece response_body) { |
90 AddResponseImpl(host, path, REGULAR_RESPONSE, response_headers, | 62 AddResponseImpl(host, path, REGULAR_RESPONSE, response_headers, |
91 response_body); | 63 response_body); |
92 } | 64 } |
(...skipping 10 matching lines...) Expand all Loading... |
103 STLDeleteValues(&responses_); | 75 STLDeleteValues(&responses_); |
104 } | 76 } |
105 | 77 |
106 void QuicInMemoryCache::InitializeFromDirectory(const string& cache_directory) { | 78 void QuicInMemoryCache::InitializeFromDirectory(const string& cache_directory) { |
107 if (cache_directory.empty()) { | 79 if (cache_directory.empty()) { |
108 LOG(DFATAL) << "cache_directory must not be empty."; | 80 LOG(DFATAL) << "cache_directory must not be empty."; |
109 return; | 81 return; |
110 } | 82 } |
111 VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: " | 83 VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: " |
112 << cache_directory; | 84 << cache_directory; |
113 FilePath directory(cache_directory); | 85 FilePath directory(FilePath::FromUTF8Unsafe(cache_directory)); |
114 base::FileEnumerator file_list(directory, | 86 base::FileEnumerator file_list(directory, |
115 true, | 87 true, |
116 base::FileEnumerator::FILES); | 88 base::FileEnumerator::FILES); |
117 | 89 |
118 for (FilePath file_iter = file_list.Next(); !file_iter.empty(); | 90 for (FilePath file_iter = file_list.Next(); !file_iter.empty(); |
119 file_iter = file_list.Next()) { | 91 file_iter = file_list.Next()) { |
120 BalsaHeaders request_headers, response_headers; | |
121 // Need to skip files in .svn directories | 92 // Need to skip files in .svn directories |
122 if (file_iter.value().find("/.svn/") != string::npos) { | 93 if (file_iter.value().find(FILE_PATH_LITERAL("/.svn/")) != string::npos) { |
123 continue; | 94 continue; |
124 } | 95 } |
125 | 96 |
126 // Tease apart filename into host and path. | 97 // Tease apart filename into host and path. |
127 StringPiece file(file_iter.value()); | 98 string file = file_iter.AsUTF8Unsafe(); |
128 file.remove_prefix(cache_directory.length()); | 99 file.erase(0, cache_directory.length()); |
129 if (file[0] == '/') { | 100 if (file[0] == '/') { |
130 file.remove_prefix(1); | 101 file.erase(0, 1); |
131 } | 102 } |
132 | 103 |
133 string file_contents; | 104 string file_contents; |
134 base::ReadFileToString(file_iter, &file_contents); | 105 base::ReadFileToString(file_iter, &file_contents); |
135 | 106 int file_len = static_cast<int>(file_contents.length()); |
136 // Frame HTTP. | 107 int headers_end = HttpUtil::LocateEndOfHeaders(file_contents.data(), |
137 CachingBalsaVisitor caching_visitor; | 108 file_len); |
138 BalsaFrame framer; | 109 if (headers_end < 1) { |
139 framer.set_balsa_headers(&response_headers); | 110 LOG(DFATAL) << "Headers invalid or empty, ignoring: " << file; |
140 framer.set_balsa_visitor(&caching_visitor); | 111 continue; |
141 size_t processed = 0; | |
142 while (processed < file_contents.length() && | |
143 !caching_visitor.done_framing()) { | |
144 processed += framer.ProcessInput(file_contents.c_str() + processed, | |
145 file_contents.length() - processed); | |
146 } | 112 } |
147 | 113 |
148 if (!caching_visitor.done_framing()) { | 114 string raw_headers = |
149 LOG(DFATAL) << "Did not frame entire message from file: " << file | 115 HttpUtil::AssembleRawHeaders(file_contents.data(), headers_end); |
150 << " (" << processed << " of " << file_contents.length() | 116 |
151 << " bytes)."; | 117 scoped_refptr<HttpResponseHeaders> response_headers = |
152 } | 118 new HttpResponseHeaders(raw_headers); |
153 if (processed < file_contents.length()) { | 119 |
154 // Didn't frame whole file. Assume remainder is body. | 120 string base; |
155 // This sometimes happens as a result of incompatibilities between | 121 if (response_headers->GetNormalizedHeader("X-Original-Url", &base)) { |
156 // BalsaFramer and wget's serialization of HTTP sans content-length. | 122 response_headers->RemoveHeader("X-Original-Url"); |
157 caching_visitor.AppendToBody(file_contents.c_str() + processed, | 123 // Remove the protocol so we can add it below. |
158 file_contents.length() - processed); | 124 if (StartsWithASCII(base, "https://", false)) { |
159 processed += file_contents.length(); | 125 base = base.substr(8); |
| 126 } else if (StartsWithASCII(base, "http://", false)) { |
| 127 base = base.substr(7); |
| 128 } |
| 129 } else { |
| 130 base = file; |
160 } | 131 } |
161 | 132 |
162 StringPiece base = file; | 133 size_t path_start = base.find_first_of('/'); |
163 if (response_headers.HasHeader("X-Original-Url")) { | 134 StringPiece host(StringPiece(base).substr(0, path_start)); |
164 base = response_headers.GetHeader("X-Original-Url"); | 135 StringPiece path(StringPiece(base).substr(path_start)); |
165 response_headers.RemoveAllOfHeader("X-Original-Url"); | |
166 // Remove the protocol so that the string is of the form host + path, | |
167 // which is parsed properly below. | |
168 if (StringPieceUtils::StartsWithIgnoreCase(base, "https://")) { | |
169 base.remove_prefix(8); | |
170 } else if (StringPieceUtils::StartsWithIgnoreCase(base, "http://")) { | |
171 base.remove_prefix(7); | |
172 } | |
173 } | |
174 int path_start = base.find_first_of('/'); | |
175 DCHECK_LT(0, path_start); | |
176 StringPiece host(base.substr(0, path_start)); | |
177 StringPiece path(base.substr(path_start)); | |
178 if (path[path.length() - 1] == ',') { | 136 if (path[path.length() - 1] == ',') { |
179 path.remove_suffix(1); | 137 path.remove_suffix(1); |
180 } | 138 } |
181 AddResponse(host, path, | 139 |
182 SpdyUtils::ResponseHeadersToSpdyHeaders(response_headers), | 140 StringPiece body(file_contents.data() + headers_end, |
183 caching_visitor.body()); | 141 file_contents.size() - headers_end); |
| 142 SpdyHeaderBlock header_block; |
| 143 CreateSpdyHeadersFromHttpResponse(*response_headers, SPDY3, &header_block); |
| 144 AddResponse(host, path, header_block, body); |
184 } | 145 } |
185 } | 146 } |
186 | 147 |
187 QuicInMemoryCache::~QuicInMemoryCache() { | 148 QuicInMemoryCache::~QuicInMemoryCache() { |
188 STLDeleteValues(&responses_); | 149 STLDeleteValues(&responses_); |
189 } | 150 } |
190 | 151 |
191 void QuicInMemoryCache::AddResponseImpl( | 152 void QuicInMemoryCache::AddResponseImpl( |
192 StringPiece host, | 153 StringPiece host, |
193 StringPiece path, | 154 StringPiece path, |
(...skipping 12 matching lines...) Expand all Loading... |
206 new_response->set_body(response_body); | 167 new_response->set_body(response_body); |
207 responses_[key] = new_response; | 168 responses_[key] = new_response; |
208 } | 169 } |
209 | 170 |
210 string QuicInMemoryCache::GetKey(StringPiece host, StringPiece path) const { | 171 string QuicInMemoryCache::GetKey(StringPiece host, StringPiece path) const { |
211 return host.as_string() + path.as_string(); | 172 return host.as_string() + path.as_string(); |
212 } | 173 } |
213 | 174 |
214 } // namespace tools | 175 } // namespace tools |
215 } // namespace net | 176 } // namespace net |
OLD | NEW |