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

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: Lowercase SPDY header 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 "net/http/http_util.h"
11 #include "net/quic/string_piece_utils.h"
11 12
12 using base::FilePath; 13 using base::FilePath;
13 using base::StringPiece; 14 using base::StringPiece;
14 using std::string; 15 using std::string;
15 16
16 // Specifies the directory used during QuicInMemoryCache 17 // Specifies the directory used during QuicInMemoryCache
17 // construction to seed the cache. Cache directory can be 18 // construction to seed the cache. Cache directory can be
18 // generated using `wget -p --save-headers <url> 19 // generated using `wget -p --save-headers <url>
19 20
20 namespace net { 21 namespace net {
21 22
22 FilePath::StringType g_quic_in_memory_cache_dir = FILE_PATH_LITERAL(""); 23 FilePath::StringType g_quic_in_memory_cache_dir = FILE_PATH_LITERAL("");
23 24
24 namespace { 25 QuicInMemoryCache::Response::Response() : response_type_(REGULAR_RESPONSE) {
26 }
25 27
26 // BalsaVisitor implementation (glue) which caches response bodies. 28 QuicInMemoryCache::Response::~Response() {
27 class CachingBalsaVisitor : public NoOpBalsaVisitor { 29 }
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 30
64 // static 31 // static
65 QuicInMemoryCache* QuicInMemoryCache::GetInstance() { 32 QuicInMemoryCache* QuicInMemoryCache::GetInstance() {
66 return Singleton<QuicInMemoryCache>::get(); 33 return Singleton<QuicInMemoryCache>::get();
67 } 34 }
68 35
69 const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse( 36 const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse(
70 const BalsaHeaders& request_headers) const { 37 const GURL& url) const {
71 ResponseMap::const_iterator it = responses_.find(GetKey(request_headers)); 38 ResponseMap::const_iterator it = responses_.find(url.spec());
wtc 2014/06/25 22:22:23 The GetKey method in the original code ignores the
dmziegler 2014/06/26 01:23:28 Done.
72 if (it == responses_.end()) { 39 if (it == responses_.end()) {
73 return NULL; 40 return NULL;
74 } 41 }
75 return it->second; 42 return it->second;
76 } 43 }
77 44
78 void QuicInMemoryCache::AddSimpleResponse(StringPiece method, 45 void QuicInMemoryCache::AddSimpleResponse(StringPiece path,
79 StringPiece path,
80 StringPiece version,
81 StringPiece response_code, 46 StringPiece response_code,
82 StringPiece response_detail, 47 StringPiece response_detail,
83 StringPiece body) { 48 StringPiece body) {
84 BalsaHeaders request_headers, response_headers; 49 GURL url(path.as_string());
wtc 2014/06/25 22:22:23 1. IMPORTANT: |path| is missing the scheme ("http"
dmziegler 2014/06/26 01:23:28 Done.
85 request_headers.SetRequestFirstlineFromStringPieces(method, 50
86 path, 51 scoped_refptr<HttpResponseHeaders> response_headers =
87 version); 52 new HttpResponseHeaders("");
wtc 2014/06/25 22:22:23 1. Please add a TODO comment to add a default Http
dmziegler 2014/06/26 01:23:28 1. I think this is not necessary anymore. 2. I th
88 response_headers.SetRequestFirstlineFromStringPieces(version, 53 response_headers->ReplaceStatusLine(response_code.as_string() + " " +
89 response_code, 54 response_detail.as_string() + " " +
90 response_detail); 55 body.as_string());
wtc 2014/06/25 22:22:24 1. BUG: I think this should use |version|, and NOT
dmziegler 2014/06/26 01:23:28 1. Done. 2. Yes, in fact I've passed in the conte
91 response_headers.AppendHeader( 56 response_headers->AddHeader(
92 "content-length", 57 "content-length: " +
93 base::Uint64ToString(static_cast<uint64>(body.length()))); 58 base::Uint64ToString(static_cast<uint64>(body.length())));
94 59
95 AddResponse(request_headers, response_headers, body); 60 AddResponse(url, response_headers, body);
96 } 61 }
97 62
98 void QuicInMemoryCache::AddResponse(const BalsaHeaders& request_headers, 63 void QuicInMemoryCache::AddResponse(
99 const BalsaHeaders& response_headers, 64 const GURL& url,
100 StringPiece response_body) { 65 scoped_refptr<HttpResponseHeaders> response_headers,
101 VLOG(1) << "Adding response for: " << GetKey(request_headers); 66 StringPiece response_body) {
102 if (ContainsKey(responses_, GetKey(request_headers))) { 67 VLOG(1) << "Adding response for: " << url.spec();
68 if (ContainsKey(responses_, url.spec())) {
103 LOG(DFATAL) << "Response for given request already exists!"; 69 LOG(DFATAL) << "Response for given request already exists!";
104 return; 70 return;
105 } 71 }
106 Response* new_response = new Response(); 72 Response* new_response = new Response();
107 new_response->set_headers(response_headers); 73 new_response->set_headers(response_headers);
108 new_response->set_body(response_body); 74 new_response->set_body(response_body);
109 responses_[GetKey(request_headers)] = new_response; 75 responses_[url.spec()] = new_response;
110 } 76 }
111 77
112 void QuicInMemoryCache::AddSpecialResponse(StringPiece method, 78 void QuicInMemoryCache::AddSpecialResponse(StringPiece path,
113 StringPiece path,
114 StringPiece version,
115 SpecialResponseType response_type) { 79 SpecialResponseType response_type) {
116 BalsaHeaders request_headers, response_headers; 80 GURL url(path.as_string());
117 request_headers.SetRequestFirstlineFromStringPieces(method, 81
118 path, 82 AddResponse(url, new HttpResponseHeaders(""), "");
wtc 2014/06/25 22:22:23 1. IMPORTANT: do we need to put new HttpResponseHe
dmziegler 2014/06/26 01:23:28 1. This should call the scoped_refptr constructor
119 version); 83 responses_[url.spec()]->response_type_ = response_type;
120 AddResponse(request_headers, response_headers, "");
121 responses_[GetKey(request_headers)]->response_type_ = response_type;
122 } 84 }
123 85
124 QuicInMemoryCache::QuicInMemoryCache() { 86 QuicInMemoryCache::QuicInMemoryCache() {
125 Initialize(); 87 Initialize();
126 } 88 }
127 89
128 void QuicInMemoryCache::ResetForTests() { 90 void QuicInMemoryCache::ResetForTests() {
129 STLDeleteValues(&responses_); 91 STLDeleteValues(&responses_);
130 Initialize(); 92 Initialize();
131 } 93 }
132 94
133 void QuicInMemoryCache::Initialize() { 95 void QuicInMemoryCache::Initialize() {
134 // If there's no defined cache dir, we have no initialization to do. 96 // If there's no defined cache dir, we have no initialization to do.
135 if (g_quic_in_memory_cache_dir.size() == 0) { 97 if (g_quic_in_memory_cache_dir.size() == 0) {
136 VLOG(1) << "No cache directory found. Skipping initialization."; 98 VLOG(1) << "No cache directory found. Skipping initialization.";
137 return; 99 return;
138 } 100 }
139 VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: " 101 VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: "
140 << g_quic_in_memory_cache_dir; 102 << g_quic_in_memory_cache_dir;
141 103
142 FilePath directory(g_quic_in_memory_cache_dir); 104 FilePath directory(g_quic_in_memory_cache_dir);
143 base::FileEnumerator file_list(directory, 105 base::FileEnumerator file_list(directory,
144 true, 106 true,
145 base::FileEnumerator::FILES); 107 base::FileEnumerator::FILES);
146 108
147 FilePath file = file_list.Next(); 109 FilePath file = file_list.Next();
148 while (!file.empty()) { 110 while (!file.empty()) {
wtc 2014/06/25 22:22:23 I think this should be written as a for loop: f
dmziegler 2014/06/26 01:23:28 Done.
149 // Need to skip files in .svn directories 111 // Need to skip files in .svn directories
150 if (file.value().find(FILE_PATH_LITERAL("/.svn/")) != std::string::npos) { 112 if (file.value().find(FILE_PATH_LITERAL("/.svn/")) != string::npos) {
151 file = file_list.Next(); 113 file = file_list.Next();
152 continue; 114 continue;
153 } 115 }
154 116
155 BalsaHeaders request_headers, response_headers;
156
157 string file_contents; 117 string file_contents;
158 base::ReadFileToString(file, &file_contents); 118 base::ReadFileToString(file, &file_contents);
159 119
160 // Frame HTTP. 120 int headers_end = HttpUtil::LocateEndOfHeaders(file_contents.c_str(),
wtc 2014/06/25 22:22:24 Nit: use data() instead of c_str() if you also sup
dmziegler 2014/06/26 01:23:28 Done.
161 CachingBalsaVisitor caching_visitor; 121 file_contents.size());
wtc 2014/06/25 23:17:32 Nit: use length() instead of size().
162 BalsaFrame framer; 122 if (headers_end < 1) {
163 framer.set_balsa_headers(&response_headers); 123 LOG(DFATAL) << "Could not find end of headers in file: " << file.value();
wtc 2014/06/25 22:22:23 1. I think we should have a "continue" here. 2. W
dmziegler 2014/06/26 01:23:28 1. Ah, I see, LOG(DFATAL) only FATALs in debug mod
164 framer.set_balsa_visitor(&caching_visitor);
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 } 124 }
171 125
172 string response_headers_str; 126 string raw_headers =
173 response_headers.DumpToString(&response_headers_str); 127 HttpUtil::AssembleRawHeaders(file_contents.c_str(), headers_end);
174 if (!caching_visitor.done_framing()) { 128
175 LOG(DFATAL) << "Did not frame entire message from file: " << file.value() 129 scoped_refptr<HttpResponseHeaders> response_headers =
176 << " (" << processed << " of " << file_contents.length() 130 new HttpResponseHeaders(raw_headers);
177 << " bytes)."; 131
178 } 132 string base;
179 if (processed < file_contents.length()) { 133 if (response_headers->HasHeader("X-Original-Url")) {
180 // Didn't frame whole file. Assume remainder is body. 134 if (!response_headers->GetNormalizedHeader("X-Original-Url", &base)) {
wtc 2014/06/25 22:22:23 I think you can replace the nested if statements w
dmziegler 2014/06/26 01:23:28 Done.
181 // This sometimes happens as a result of incompatibilities between 135 LOG(DFATAL) << "Failed to read X-Original-Url in file: "
182 // BalsaFramer and wget's serialization of HTTP sans content-length. 136 << file.value();
183 caching_visitor.AppendToBody(file_contents.c_str() + processed, 137 }
184 file_contents.length() - processed); 138 response_headers->RemoveHeader("X-Original-Url");
185 processed += file_contents.length(); 139 // Remove the protocol so we can add it below.
140 if (StringPieceUtils::StartsWithIgnoreCase(base, "https://")) {
141 base = base.substr(8);
142 } else if (StringPieceUtils::StartsWithIgnoreCase(base, "http://")) {
143 base = base.substr(7);
144 }
145 } else {
146 base = file.AsUTF8Unsafe();
wtc 2014/06/25 23:17:32 1. We should make sure this is an absolute pathnam
dmziegler 2014/06/26 01:23:28 I don't think it's supposed to be absolute. The DC
186 } 147 }
187 148
188 string utf8_file = file.AsUTF8Unsafe(); 149 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);
wtc 2014/06/25 23:17:32 Should we also remove the trailing ',' in the file
dmziegler 2014/06/26 01:23:28 Oh yes.
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 150
214 VLOG(1) << "Inserting 'http://" << GetKey(request_headers) 151 VLOG(1) << "Inserting '" << url.spec() << "' into QuicInMemoryCache.";
215 << "' into QuicInMemoryCache.";
216 152
217 AddResponse(request_headers, response_headers, caching_visitor.body()); 153 StringPiece body(file_contents.c_str() + headers_end,
154 file_contents.size() - headers_end);
155
156 AddResponse(url, response_headers, body);
218 157
219 file = file_list.Next(); 158 file = file_list.Next();
220 } 159 }
221 } 160 }
222 161
223 QuicInMemoryCache::~QuicInMemoryCache() { 162 QuicInMemoryCache::~QuicInMemoryCache() {
224 STLDeleteValues(&responses_); 163 STLDeleteValues(&responses_);
225 } 164 }
226 165
227 string QuicInMemoryCache::GetKey(const BalsaHeaders& request_headers) const {
228 StringPiece uri = request_headers.request_uri();
229 if (uri.size() == 0) {
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 }
242
243 } // namespace net 166 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698