Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(288)

Side by Side Diff: net/quic/quic_in_memory_cache.cc

Issue 337093003: QuicServer: Use Chrome's header parsing rather than Balsa (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@quic_server_3
Patch Set: Fix build on many platforms Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 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/quic/quic_in_memory_cache.h" 5 #include "net/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/stl_util.h" 8 #include "base/stl_util.h"
9 #include "base/strings/string_number_conversions.h" 9 #include "base/strings/string_number_conversions.h"
10 #include "net/tools/balsa/balsa_headers.h" 10 #include "base/strings/string_util.h"
11 #include "net/http/http_util.h"
12 #include "url/gurl.h"
11 13
12 using base::FilePath; 14 using base::FilePath;
13 using base::StringPiece; 15 using base::StringPiece;
14 using std::string; 16 using std::string;
15 17
16 // Specifies the directory used during QuicInMemoryCache 18 // Specifies the directory used during QuicInMemoryCache
17 // construction to seed the cache. Cache directory can be 19 // construction to seed the cache. Cache directory can be
18 // generated using `wget -p --save-headers <url> 20 // generated using `wget -p --save-headers <url>
19 21
20 namespace net { 22 namespace net {
21 23
22 FilePath::StringType g_quic_in_memory_cache_dir = FILE_PATH_LITERAL(""); 24 FilePath::StringType g_quic_in_memory_cache_dir = FILE_PATH_LITERAL("");
23 25
24 namespace { 26 QuicInMemoryCache::Response::Response() : response_type_(REGULAR_RESPONSE) {
27 }
25 28
26 // BalsaVisitor implementation (glue) which caches response bodies. 29 QuicInMemoryCache::Response::~Response() {
27 class CachingBalsaVisitor : public NoOpBalsaVisitor { 30 }
28 public:
29 CachingBalsaVisitor() : done_framing_(false) {}
30 virtual void ProcessBodyData(const char* input, size_t size) OVERRIDE {
31 AppendToBody(input, size);
32 }
33 virtual void MessageDone() OVERRIDE {
34 done_framing_ = true;
35 }
36 virtual void HandleHeaderError(BalsaFrame* framer) OVERRIDE {
37 UnhandledError();
38 }
39 virtual void HandleHeaderWarning(BalsaFrame* framer) OVERRIDE {
40 UnhandledError();
41 }
42 virtual void HandleChunkingError(BalsaFrame* framer) OVERRIDE {
43 UnhandledError();
44 }
45 virtual void HandleBodyError(BalsaFrame* framer) OVERRIDE {
46 UnhandledError();
47 }
48 void UnhandledError() {
49 LOG(DFATAL) << "Unhandled error framing HTTP.";
50 }
51 void AppendToBody(const char* input, size_t size) {
52 body_.append(input, size);
53 }
54 bool done_framing() const { return done_framing_; }
55 const string& body() const { return body_; }
56
57 private:
58 bool done_framing_;
59 string body_;
60 };
61
62 } // namespace
63 31
64 // static 32 // static
65 QuicInMemoryCache* QuicInMemoryCache::GetInstance() { 33 QuicInMemoryCache* QuicInMemoryCache::GetInstance() {
66 return Singleton<QuicInMemoryCache>::get(); 34 return Singleton<QuicInMemoryCache>::get();
67 } 35 }
68 36
69 const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse( 37 const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse(
70 const BalsaHeaders& request_headers) const { 38 const GURL& url) const {
71 ResponseMap::const_iterator it = responses_.find(GetKey(request_headers)); 39 ResponseMap::const_iterator it = responses_.find(GetKey(url));
72 if (it == responses_.end()) { 40 if (it == responses_.end()) {
73 return NULL; 41 return NULL;
74 } 42 }
75 return it->second; 43 return it->second;
76 } 44 }
77 45
78 void QuicInMemoryCache::AddSimpleResponse(StringPiece method, 46 void QuicInMemoryCache::AddSimpleResponse(StringPiece path,
79 StringPiece path,
80 StringPiece version, 47 StringPiece version,
81 StringPiece response_code, 48 StringPiece response_code,
82 StringPiece response_detail, 49 StringPiece response_detail,
83 StringPiece body) { 50 StringPiece body) {
84 BalsaHeaders request_headers, response_headers; 51 GURL url("http://" + path.as_string());
85 request_headers.SetRequestFirstlineFromStringPieces(method,
86 path,
87 version);
88 response_headers.SetRequestFirstlineFromStringPieces(version,
89 response_code,
90 response_detail);
91 response_headers.AppendHeader(
92 "content-length",
93 base::Uint64ToString(static_cast<uint64>(body.length())));
94 52
95 AddResponse(request_headers, response_headers, body); 53 string status_line = version.as_string() + " " +
54 response_code.as_string() + " " +
55 response_detail.as_string();
56
57 string header = "content-length: " +
58 base::Uint64ToString(static_cast<uint64>(body.length()));
59
60 scoped_refptr<HttpResponseHeaders> response_headers =
61 new HttpResponseHeaders(status_line + '\0' + header + '\0' + '\0');
62
63 AddResponse(url, response_headers, body);
96 } 64 }
97 65
98 void QuicInMemoryCache::AddResponse(const BalsaHeaders& request_headers, 66 void QuicInMemoryCache::AddResponse(
99 const BalsaHeaders& response_headers, 67 const GURL& url,
100 StringPiece response_body) { 68 scoped_refptr<HttpResponseHeaders> response_headers,
101 VLOG(1) << "Adding response for: " << GetKey(request_headers); 69 StringPiece response_body) {
102 if (ContainsKey(responses_, GetKey(request_headers))) { 70 string key = GetKey(url);
71 VLOG(1) << "Adding response for: " << key;
72 if (ContainsKey(responses_, key)) {
103 LOG(DFATAL) << "Response for given request already exists!"; 73 LOG(DFATAL) << "Response for given request already exists!";
104 return; 74 return;
105 } 75 }
106 Response* new_response = new Response(); 76 Response* new_response = new Response();
107 new_response->set_headers(response_headers); 77 new_response->set_headers(response_headers);
108 new_response->set_body(response_body); 78 new_response->set_body(response_body);
109 responses_[GetKey(request_headers)] = new_response; 79 responses_[key] = new_response;
110 } 80 }
111 81
112 void QuicInMemoryCache::AddSpecialResponse(StringPiece method, 82 void QuicInMemoryCache::AddSpecialResponse(StringPiece path,
113 StringPiece path,
114 StringPiece version,
115 SpecialResponseType response_type) { 83 SpecialResponseType response_type) {
116 BalsaHeaders request_headers, response_headers; 84 GURL url("http://" + path.as_string());
117 request_headers.SetRequestFirstlineFromStringPieces(method, 85
118 path, 86 AddResponse(url, NULL, string());
119 version); 87 responses_[GetKey(url)]->response_type_ = response_type;
120 AddResponse(request_headers, response_headers, "");
121 responses_[GetKey(request_headers)]->response_type_ = response_type;
122 } 88 }
123 89
124 QuicInMemoryCache::QuicInMemoryCache() { 90 QuicInMemoryCache::QuicInMemoryCache() {
125 Initialize(); 91 Initialize();
126 } 92 }
127 93
128 void QuicInMemoryCache::ResetForTests() { 94 void QuicInMemoryCache::ResetForTests() {
129 STLDeleteValues(&responses_); 95 STLDeleteValues(&responses_);
130 Initialize(); 96 Initialize();
131 } 97 }
132 98
133 void QuicInMemoryCache::Initialize() { 99 void QuicInMemoryCache::Initialize() {
134 // If there's no defined cache dir, we have no initialization to do. 100 // If there's no defined cache dir, we have no initialization to do.
135 if (g_quic_in_memory_cache_dir.size() == 0) { 101 if (g_quic_in_memory_cache_dir.size() == 0) {
136 VLOG(1) << "No cache directory found. Skipping initialization."; 102 VLOG(1) << "No cache directory found. Skipping initialization.";
137 return; 103 return;
138 } 104 }
139 VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: " 105 VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: "
140 << g_quic_in_memory_cache_dir; 106 << g_quic_in_memory_cache_dir;
141 107
142 FilePath directory(g_quic_in_memory_cache_dir); 108 FilePath directory(g_quic_in_memory_cache_dir);
143 base::FileEnumerator file_list(directory, 109 base::FileEnumerator file_list(directory,
144 true, 110 true,
145 base::FileEnumerator::FILES); 111 base::FileEnumerator::FILES);
146 112
147 FilePath file = file_list.Next(); 113 FilePath file = file_list.Next();
148 while (!file.empty()) { 114 for (; !file.empty(); file = file_list.Next()) {
149 // Need to skip files in .svn directories 115 // Need to skip files in .svn directories
150 if (file.value().find(FILE_PATH_LITERAL("/.svn/")) != std::string::npos) { 116 if (file.value().find(FILE_PATH_LITERAL("/.svn/")) != string::npos) {
151 file = file_list.Next();
152 continue; 117 continue;
153 } 118 }
154 119
155 BalsaHeaders request_headers, response_headers;
156
157 string file_contents; 120 string file_contents;
158 base::ReadFileToString(file, &file_contents); 121 base::ReadFileToString(file, &file_contents);
159 122
160 // Frame HTTP. 123 if (file_contents.length() > INT_MAX) {
161 CachingBalsaVisitor caching_visitor; 124 LOG(WARNING) << "File '" << file.value() << "' too large: "
162 BalsaFrame framer; 125 << file_contents.length();
163 framer.set_balsa_headers(&response_headers); 126 continue;
164 framer.set_balsa_visitor(&caching_visitor); 127 }
165 size_t processed = 0; 128 int file_len = static_cast<int>(file_contents.length());
166 while (processed < file_contents.length() && 129
167 !caching_visitor.done_framing()) { 130 int headers_end = HttpUtil::LocateEndOfHeaders(file_contents.data(),
168 processed += framer.ProcessInput(file_contents.c_str() + processed, 131 file_len);
wtc 2014/07/08 17:54:41 I think it would be better to change these HttpUti
dmz 2014/07/08 18:07:35 You're right. If you want, I can make a CL for thi
169 file_contents.length() - processed); 132 if (headers_end < 1) {
133 LOG(DFATAL) << "Headers invalid or empty, ignoring: " << file.value();
134 continue;
170 } 135 }
171 136
172 string response_headers_str; 137 string raw_headers =
173 response_headers.DumpToString(&response_headers_str); 138 HttpUtil::AssembleRawHeaders(file_contents.data(), headers_end);
174 if (!caching_visitor.done_framing()) { 139
175 LOG(DFATAL) << "Did not frame entire message from file: " << file.value() 140 scoped_refptr<HttpResponseHeaders> response_headers =
176 << " (" << processed << " of " << file_contents.length() 141 new HttpResponseHeaders(raw_headers);
177 << " bytes)."; 142
143 string base;
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);
151 }
152 } else {
153 base = file.AsUTF8Unsafe();
178 } 154 }
179 if (processed < file_contents.length()) { 155 if (base.length() == 0 || base[0] == '/') {
180 // Didn't frame whole file. Assume remainder is body. 156 LOG(DFATAL) << "Invalid path, ignoring: " << base;
181 // This sometimes happens as a result of incompatibilities between 157 continue;
182 // BalsaFramer and wget's serialization of HTTP sans content-length. 158 }
183 caching_visitor.AppendToBody(file_contents.c_str() + processed, 159 if (base[base.length() - 1] == ',') {
184 file_contents.length() - processed); 160 base = base.substr(0, base.length() - 1);
185 processed += file_contents.length();
186 } 161 }
187 162
188 string utf8_file = file.AsUTF8Unsafe(); 163 GURL url("http://" + base);
189 StringPiece base = utf8_file;
190 if (response_headers.HasHeader("X-Original-Url")) {
191 base = response_headers.GetHeader("X-Original-Url");
192 response_headers.RemoveAllOfHeader("X-Original-Url");
193 // Remove the protocol so that the string is of the form host + path,
194 // which is parsed properly below.
195 if (StringPieceUtils::StartsWithIgnoreCase(base, "https://")) {
196 base.remove_prefix(8);
197 } else if (StringPieceUtils::StartsWithIgnoreCase(base, "http://")) {
198 base.remove_prefix(7);
199 }
200 }
201 size_t path_start = base.find_first_of('/');
202 DCHECK_LT(0U, path_start);
203 StringPiece host(base.substr(0, path_start));
204 StringPiece path(base.substr(path_start));
205 if (path[path.length() - 1] == ',') {
206 path.remove_suffix(1);
207 }
208 // Set up request headers. Assume method is GET and protocol is HTTP/1.1.
209 request_headers.SetRequestFirstlineFromStringPieces("GET",
210 path,
211 "HTTP/1.1");
212 request_headers.ReplaceOrAppendHeader("host", host);
213 164
214 VLOG(1) << "Inserting 'http://" << GetKey(request_headers) 165 VLOG(1) << "Inserting '" << GetKey(url) << "' into QuicInMemoryCache.";
215 << "' into QuicInMemoryCache.";
216 166
217 AddResponse(request_headers, response_headers, caching_visitor.body()); 167 StringPiece body(file_contents.data() + headers_end,
168 file_contents.size() - headers_end);
218 169
219 file = file_list.Next(); 170 AddResponse(url, response_headers, body);
220 } 171 }
221 } 172 }
222 173
223 QuicInMemoryCache::~QuicInMemoryCache() { 174 QuicInMemoryCache::~QuicInMemoryCache() {
224 STLDeleteValues(&responses_); 175 STLDeleteValues(&responses_);
225 } 176 }
226 177
227 string QuicInMemoryCache::GetKey(const BalsaHeaders& request_headers) const { 178 string QuicInMemoryCache::GetKey(const GURL& url) const {
228 StringPiece uri = request_headers.request_uri(); 179 // Take everything but the scheme portion of the URL.
229 if (uri.size() == 0) { 180 return url.host() + url.PathForRequest();
230 return "";
231 }
232 StringPiece host;
233 if (uri[0] == '/') {
234 host = request_headers.GetHeader("host");
235 } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "https://")) {
236 uri.remove_prefix(8);
237 } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "http://")) {
238 uri.remove_prefix(7);
239 }
240 return host.as_string() + uri.as_string();
241 } 181 }
242 182
243 } // namespace net 183 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698