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

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: Small cleanup Created 6 years, 6 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());
wtc 2014/06/26 21:23:17 |url| doesn't have a |host| component. Is that OK?
dmziegler 2014/06/27 01:11:37 Yes, that would be included in the path 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 VLOG(1) << "Adding response for: " << GetKey(url);
wtc 2014/06/26 21:23:17 Nit: we should store the return value of GetKey(ur
dmziegler 2014/06/27 01:11:37 Done.
71 if (ContainsKey(responses_, GetKey(url))) {
103 LOG(DFATAL) << "Response for given request already exists!"; 72 LOG(DFATAL) << "Response for given request already exists!";
104 return; 73 return;
105 } 74 }
106 Response* new_response = new Response(); 75 Response* new_response = new Response();
107 new_response->set_headers(response_headers); 76 new_response->set_headers(response_headers);
108 new_response->set_body(response_body); 77 new_response->set_body(response_body);
109 responses_[GetKey(request_headers)] = new_response; 78 responses_[GetKey(url)] = new_response;
110 } 79 }
111 80
112 void QuicInMemoryCache::AddSpecialResponse(StringPiece method, 81 void QuicInMemoryCache::AddSpecialResponse(StringPiece path,
113 StringPiece path,
114 StringPiece version,
115 SpecialResponseType response_type) { 82 SpecialResponseType response_type) {
116 BalsaHeaders request_headers, response_headers; 83 GURL url("http://" + path.as_string());
117 request_headers.SetRequestFirstlineFromStringPieces(method, 84
118 path, 85 AddResponse(url, NULL, string());
wtc 2014/06/26 21:23:17 I seem to remember it is necessary to pass scoped_
dmziegler 2014/06/27 01:11:37 This implicitly calls the scoped_refptr<>() constr
wtc 2014/06/27 22:08:04 Ah, I finally realized what you meant by this. Sh
dmziegler 2014/07/01 18:23:39 A const pointer looks like the way to go. Will do
119 version); 86 responses_[GetKey(url)]->response_type_ = response_type;
120 AddResponse(request_headers, response_headers, "");
121 responses_[GetKey(request_headers)]->response_type_ = response_type;
122 } 87 }
123 88
124 QuicInMemoryCache::QuicInMemoryCache() { 89 QuicInMemoryCache::QuicInMemoryCache() {
125 Initialize(); 90 Initialize();
126 } 91 }
127 92
128 void QuicInMemoryCache::ResetForTests() { 93 void QuicInMemoryCache::ResetForTests() {
129 STLDeleteValues(&responses_); 94 STLDeleteValues(&responses_);
130 Initialize(); 95 Initialize();
131 } 96 }
132 97
133 void QuicInMemoryCache::Initialize() { 98 void QuicInMemoryCache::Initialize() {
134 // If there's no defined cache dir, we have no initialization to do. 99 // If there's no defined cache dir, we have no initialization to do.
135 if (g_quic_in_memory_cache_dir.size() == 0) { 100 if (g_quic_in_memory_cache_dir.size() == 0) {
136 VLOG(1) << "No cache directory found. Skipping initialization."; 101 VLOG(1) << "No cache directory found. Skipping initialization.";
137 return; 102 return;
138 } 103 }
139 VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: " 104 VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: "
140 << g_quic_in_memory_cache_dir; 105 << g_quic_in_memory_cache_dir;
141 106
142 FilePath directory(g_quic_in_memory_cache_dir); 107 FilePath directory(g_quic_in_memory_cache_dir);
143 base::FileEnumerator file_list(directory, 108 base::FileEnumerator file_list(directory,
144 true, 109 true,
145 base::FileEnumerator::FILES); 110 base::FileEnumerator::FILES);
146 111
147 FilePath file = file_list.Next(); 112 FilePath file = file_list.Next();
148 while (!file.empty()) { 113 for (; !file.empty(); file = file_list.Next()) {
149 // Need to skip files in .svn directories 114 // Need to skip files in .svn directories
150 if (file.value().find(FILE_PATH_LITERAL("/.svn/")) != std::string::npos) { 115 if (file.value().find(FILE_PATH_LITERAL("/.svn/")) != string::npos) {
151 file = file_list.Next(); 116 file = file_list.Next();
wtc 2014/06/26 21:23:16 IMPORTANT: delete this line (now done by the for l
dmziegler 2014/06/27 01:11:37 Done.
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 int headers_end = HttpUtil::LocateEndOfHeaders(file_contents.data(),
161 CachingBalsaVisitor caching_visitor; 124 file_contents.size());
162 BalsaFrame framer; 125 if (headers_end < 1) {
163 framer.set_balsa_headers(&response_headers); 126 LOG(DFATAL) << "Headers invalid or empty, ignoring: " << file.value();
164 framer.set_balsa_visitor(&caching_visitor); 127 continue;
165 size_t processed = 0;
166 while (processed < file_contents.length() &&
167 !caching_visitor.done_framing()) {
168 processed += framer.ProcessInput(file_contents.c_str() + processed,
169 file_contents.length() - processed);
170 } 128 }
171 129
172 string response_headers_str; 130 string raw_headers =
173 response_headers.DumpToString(&response_headers_str); 131 HttpUtil::AssembleRawHeaders(file_contents.data(), headers_end);
174 if (!caching_visitor.done_framing()) { 132
175 LOG(DFATAL) << "Did not frame entire message from file: " << file.value() 133 scoped_refptr<HttpResponseHeaders> response_headers =
176 << " (" << processed << " of " << file_contents.length() 134 new HttpResponseHeaders(raw_headers);
177 << " bytes)."; 135
136 string base;
137 if (response_headers->GetNormalizedHeader("X-Original-Url", &base)) {
138 response_headers->RemoveHeader("X-Original-Url");
139 // Remove the protocol so we can add it below.
140 if (StartsWithASCII(base, "https://", false)) {
141 base = base.substr(8);
142 } else if (StartsWithASCII(base, "http://", false)) {
143 base = base.substr(7);
144 }
145 } else {
146 base = file.AsUTF8Unsafe();
wtc 2014/06/26 21:23:17 If this generates a URL like "http://relative/path
dmziegler 2014/06/27 01:11:37 Actually, it's fine the way it is. "relative" just
178 } 147 }
179 if (processed < file_contents.length()) { 148 DCHECK(base.length() > 0 && base[0] != '/');
180 // Didn't frame whole file. Assume remainder is body. 149 if (base[base.length() - 1] == ',') {
wtc 2014/06/26 21:23:17 We should test !base.empty(), otherwise the releas
dmziegler 2014/06/27 01:11:37 Since that really shouldn't happen, I've just made
181 // This sometimes happens as a result of incompatibilities between 150 base = base.substr(0, base.length() - 1);
182 // BalsaFramer and wget's serialization of HTTP sans content-length.
183 caching_visitor.AppendToBody(file_contents.c_str() + processed,
184 file_contents.length() - processed);
185 processed += file_contents.length();
186 } 151 }
187 152
188 string utf8_file = file.AsUTF8Unsafe(); 153 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 154
214 VLOG(1) << "Inserting 'http://" << GetKey(request_headers) 155 VLOG(1) << "Inserting 'http://" << GetKey(url)
wtc 2014/06/26 21:23:17 Nit: we probably should omit "http://" in this log
dmziegler 2014/06/27 01:11:37 Done.
215 << "' into QuicInMemoryCache."; 156 << "' into QuicInMemoryCache.";
216 157
217 AddResponse(request_headers, response_headers, caching_visitor.body()); 158 StringPiece body(file_contents.data() + headers_end,
159 file_contents.size() - headers_end);
218 160
219 file = file_list.Next(); 161 AddResponse(url, response_headers, body);
220 } 162 }
221 } 163 }
222 164
223 QuicInMemoryCache::~QuicInMemoryCache() { 165 QuicInMemoryCache::~QuicInMemoryCache() {
224 STLDeleteValues(&responses_); 166 STLDeleteValues(&responses_);
225 } 167 }
226 168
227 string QuicInMemoryCache::GetKey(const BalsaHeaders& request_headers) const { 169 string QuicInMemoryCache::GetKey(const GURL& url) const {
228 StringPiece uri = request_headers.request_uri(); 170 // Take everything but the scheme portion of the URL.
229 if (uri.size() == 0) { 171 return url.host() + url.PathForRequest();
wtc 2014/06/26 21:23:17 Do we need a '/' in between? I seem to remember t
dmziegler 2014/06/27 01:11:37 No, PathForRequest() includes a slash. And I can't
wtc 2014/06/27 21:00:38 If url.PathForRequest() starts with a slash, then
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 } 172 }
242 173
243 } // namespace net 174 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698