| 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 |