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

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: Address review comments 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 "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());
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());
85 request_headers.SetRequestFirstlineFromStringPieces(method,
86 path,
87 version);
88 response_headers.SetRequestFirstlineFromStringPieces(version,
89 response_code,
90 response_detail);
91 response_headers.AppendHeader("content-length",
92 base::IntToString(body.length()));
93 50
94 AddResponse(request_headers, response_headers, body); 51 scoped_refptr<HttpResponseHeaders> response_headers =
52 new HttpResponseHeaders("");
53 response_headers->ReplaceStatusLine(response_code.as_string() + " " +
54 response_detail.as_string() + " " +
55 body.as_string());
56 response_headers->AddHeader("content-length: " +
57 base::IntToString(body.length()));
58
59 AddResponse(url, response_headers, body);
95 } 60 }
96 61
97 void QuicInMemoryCache::AddResponse(const BalsaHeaders& request_headers, 62 void QuicInMemoryCache::AddResponse(
98 const BalsaHeaders& response_headers, 63 const GURL& url,
99 StringPiece response_body) { 64 scoped_refptr<HttpResponseHeaders> response_headers,
100 VLOG(1) << "Adding response for: " << GetKey(request_headers); 65 StringPiece response_body) {
101 if (ContainsKey(responses_, GetKey(request_headers))) { 66 VLOG(1) << "Adding response for: " << url.spec();
67 if (ContainsKey(responses_, url.spec())) {
102 LOG(DFATAL) << "Response for given request already exists!"; 68 LOG(DFATAL) << "Response for given request already exists!";
103 return; 69 return;
104 } 70 }
105 Response* new_response = new Response(); 71 Response* new_response = new Response();
106 new_response->set_headers(response_headers); 72 new_response->set_headers(response_headers);
107 new_response->set_body(response_body); 73 new_response->set_body(response_body);
108 responses_[GetKey(request_headers)] = new_response; 74 responses_[url.spec()] = new_response;
109 } 75 }
110 76
111 void QuicInMemoryCache::AddSpecialResponse(StringPiece method, 77 void QuicInMemoryCache::AddSpecialResponse(StringPiece path,
112 StringPiece path,
113 StringPiece version,
114 SpecialResponseType response_type) { 78 SpecialResponseType response_type) {
115 BalsaHeaders request_headers, response_headers; 79 GURL url(path.as_string());
116 request_headers.SetRequestFirstlineFromStringPieces(method, 80
117 path, 81 AddResponse(url, new HttpResponseHeaders(""), "");
118 version); 82 responses_[url.spec()]->response_type_ = response_type;
119 AddResponse(request_headers, response_headers, "");
120 responses_[GetKey(request_headers)]->response_type_ = response_type;
121 } 83 }
122 84
123 QuicInMemoryCache::QuicInMemoryCache() { 85 QuicInMemoryCache::QuicInMemoryCache() {
124 Initialize(); 86 Initialize();
125 } 87 }
126 88
127 void QuicInMemoryCache::ResetForTests() { 89 void QuicInMemoryCache::ResetForTests() {
128 STLDeleteValues(&responses_); 90 STLDeleteValues(&responses_);
129 Initialize(); 91 Initialize();
130 } 92 }
131 93
132 void QuicInMemoryCache::Initialize() { 94 void QuicInMemoryCache::Initialize() {
133 // If there's no defined cache dir, we have no initialization to do. 95 // If there's no defined cache dir, we have no initialization to do.
134 if (g_quic_in_memory_cache_dir.size() == 0) { 96 if (g_quic_in_memory_cache_dir.size() == 0) {
135 VLOG(1) << "No cache directory found. Skipping initialization."; 97 VLOG(1) << "No cache directory found. Skipping initialization.";
136 return; 98 return;
137 } 99 }
138 VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: " 100 VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: "
139 << g_quic_in_memory_cache_dir; 101 << g_quic_in_memory_cache_dir;
140 102
141 FilePath directory(g_quic_in_memory_cache_dir); 103 FilePath directory(g_quic_in_memory_cache_dir);
142 base::FileEnumerator file_list(directory, 104 base::FileEnumerator file_list(directory,
143 true, 105 true,
144 base::FileEnumerator::FILES); 106 base::FileEnumerator::FILES);
145 107
146 FilePath file = file_list.Next(); 108 FilePath file = file_list.Next();
147 while (!file.empty()) { 109 while (!file.empty()) {
148 // Need to skip files in .svn directories 110 // Need to skip files in .svn directories
149 if (file.value().find(FILE_PATH_LITERAL("/.svn/")) != std::string::npos) { 111 if (file.value().find(FILE_PATH_LITERAL("/.svn/")) != string::npos) {
150 file = file_list.Next(); 112 file = file_list.Next();
151 continue; 113 continue;
152 } 114 }
153 115
154 BalsaHeaders request_headers, response_headers;
155
156 string file_contents; 116 string file_contents;
157 base::ReadFileToString(file, &file_contents); 117 base::ReadFileToString(file, &file_contents);
158 118
159 // Frame HTTP. 119 int headers_end = HttpUtil::LocateEndOfHeaders(file_contents.c_str(),
160 CachingBalsaVisitor caching_visitor; 120 file_contents.size());
161 BalsaFrame framer; 121 if (headers_end < 1) {
162 framer.set_balsa_headers(&response_headers); 122 LOG(DFATAL) << "Could not find end of headers in file: " << file.value();
163 framer.set_balsa_visitor(&caching_visitor);
164 size_t processed = 0;
165 while (processed < file_contents.length() &&
166 !caching_visitor.done_framing()) {
167 processed += framer.ProcessInput(file_contents.c_str() + processed,
168 file_contents.length() - processed);
169 } 123 }
170 124
171 string response_headers_str; 125 string raw_headers =
172 response_headers.DumpToString(&response_headers_str); 126 HttpUtil::AssembleRawHeaders(file_contents.c_str(), headers_end);
173 if (!caching_visitor.done_framing()) { 127
174 LOG(DFATAL) << "Did not frame entire message from file: " << file.value() 128 scoped_refptr<HttpResponseHeaders> response_headers =
175 << " (" << processed << " of " << file_contents.length() 129 new HttpResponseHeaders(raw_headers);
176 << " bytes)."; 130
177 } 131 string base;
178 if (processed < file_contents.length()) { 132 if (response_headers->HasHeader("X-Original-Url")) {
179 // Didn't frame whole file. Assume remainder is body. 133 if (!response_headers->GetNormalizedHeader("X-Original-Url", &base)) {
180 // This sometimes happens as a result of incompatibilities between 134 LOG(DFATAL) << "Failed to read X-Original-Url in file: "
181 // BalsaFramer and wget's serialization of HTTP sans content-length. 135 << file.value();
182 caching_visitor.AppendToBody(file_contents.c_str() + processed, 136 }
183 file_contents.length() - processed); 137 response_headers->RemoveHeader("X-Original-Url");
184 processed += file_contents.length(); 138 // Remove the protocol so we can add it below.
139 if (StringPieceUtils::StartsWithIgnoreCase(base, "https://")) {
140 base = base.substr(8);
141 } else if (StringPieceUtils::StartsWithIgnoreCase(base, "http://")) {
142 base = base.substr(7);
143 }
144 } else {
145 base = file.AsUTF8Unsafe();
185 } 146 }
186 147
187 string utf8_file = file.AsUTF8Unsafe(); 148 GURL url("http://" + base);
188 StringPiece base = utf8_file;
189 if (response_headers.HasHeader("X-Original-Url")) {
190 base = response_headers.GetHeader("X-Original-Url");
191 response_headers.RemoveAllOfHeader("X-Original-Url");
192 // Remove the protocol so that the string is of the form host + path,
193 // which is parsed properly below.
194 if (StringPieceUtils::StartsWithIgnoreCase(base, "https://")) {
195 base.remove_prefix(8);
196 } else if (StringPieceUtils::StartsWithIgnoreCase(base, "http://")) {
197 base.remove_prefix(7);
198 }
199 }
200 int path_start = base.find_first_of('/');
201 DCHECK_LT(0, path_start);
202 StringPiece host(base.substr(0, path_start));
203 StringPiece path(base.substr(path_start));
204 if (path[path.length() - 1] == ',') {
205 path.remove_suffix(1);
206 }
207 // Set up request headers. Assume method is GET and protocol is HTTP/1.1.
208 request_headers.SetRequestFirstlineFromStringPieces("GET",
209 path,
210 "HTTP/1.1");
211 request_headers.ReplaceOrAppendHeader("host", host);
212 149
213 VLOG(1) << "Inserting 'http://" << GetKey(request_headers) 150 VLOG(1) << "Inserting '" << url.spec() << "' into QuicInMemoryCache.";
214 << "' into QuicInMemoryCache.";
215 151
216 AddResponse(request_headers, response_headers, caching_visitor.body()); 152 StringPiece body(file_contents.c_str() + headers_end,
153 file_contents.size() - headers_end);
154
155 AddResponse(url, response_headers, body);
217 156
218 file = file_list.Next(); 157 file = file_list.Next();
219 } 158 }
220 } 159 }
221 160
222 QuicInMemoryCache::~QuicInMemoryCache() { 161 QuicInMemoryCache::~QuicInMemoryCache() {
223 STLDeleteValues(&responses_); 162 STLDeleteValues(&responses_);
224 } 163 }
225 164
226 string QuicInMemoryCache::GetKey(const BalsaHeaders& request_headers) const {
227 StringPiece uri = request_headers.request_uri();
228 if (uri.size() == 0) {
229 return "";
230 }
231 StringPiece host;
232 if (uri[0] == '/') {
233 host = request_headers.GetHeader("host");
234 } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "https://")) {
235 uri.remove_prefix(8);
236 } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "http://")) {
237 uri.remove_prefix(7);
238 }
239 return host.as_string() + uri.as_string();
240 }
241
242 } // namespace net 165 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698