Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / net / tools / quic / quic_in_memory_cache.cc
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
3 // found in the LICENSE file.
4
5 #include "net/tools/quic/quic_in_memory_cache.h"
6
7 #include "base/files/file_enumerator.h"
8 #include "base/files/file_util.h"
9 #include "base/stl_util.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "net/tools/balsa/balsa_headers.h"
12
13 using base::FilePath;
14 using base::StringPiece;
15 using std::string;
16
17 // Specifies the directory used during QuicInMemoryCache
18 // construction to seed the cache. Cache directory can be
19 // generated using `wget -p --save-headers <url>
20
21 namespace net {
22 namespace tools {
23
24 std::string FLAGS_quic_in_memory_cache_dir = "";
25
26 namespace {
27
28 // BalsaVisitor implementation (glue) which caches response bodies.
29 class CachingBalsaVisitor : public NoOpBalsaVisitor {
30  public:
31   CachingBalsaVisitor() : done_framing_(false) {}
32   void ProcessBodyData(const char* input, size_t size) override {
33     AppendToBody(input, size);
34   }
35   void MessageDone() override { done_framing_ = true; }
36   void HandleHeaderError(BalsaFrame* framer) override { UnhandledError(); }
37   void HandleHeaderWarning(BalsaFrame* framer) override { UnhandledError(); }
38   void HandleChunkingError(BalsaFrame* framer) override { UnhandledError(); }
39   void HandleBodyError(BalsaFrame* framer) override { UnhandledError(); }
40   void UnhandledError() {
41     LOG(DFATAL) << "Unhandled error framing HTTP.";
42   }
43   void AppendToBody(const char* input, size_t size) {
44     body_.append(input, size);
45   }
46   bool done_framing() const { return done_framing_; }
47   const string& body() const { return body_; }
48
49  private:
50   bool done_framing_;
51   string body_;
52 };
53
54 }  // namespace
55
56 // static
57 QuicInMemoryCache* QuicInMemoryCache::GetInstance() {
58   return Singleton<QuicInMemoryCache>::get();
59 }
60
61 const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse(
62     const BalsaHeaders& request_headers) const {
63   ResponseMap::const_iterator it = responses_.find(GetKey(request_headers));
64   if (it == responses_.end()) {
65     return nullptr;
66   }
67   return it->second;
68 }
69
70 void QuicInMemoryCache::AddSimpleResponse(StringPiece method,
71                                           StringPiece path,
72                                           StringPiece version,
73                                           StringPiece response_code,
74                                           StringPiece response_detail,
75                                           StringPiece body) {
76   BalsaHeaders request_headers, response_headers;
77   request_headers.SetRequestFirstlineFromStringPieces(method,
78                                                       path,
79                                                       version);
80   response_headers.SetRequestFirstlineFromStringPieces(version,
81                                                        response_code,
82                                                        response_detail);
83   response_headers.AppendHeader("content-length",
84                                 base::IntToString(body.length()));
85
86   AddResponse(request_headers, response_headers, body);
87 }
88
89 void QuicInMemoryCache::AddResponse(const BalsaHeaders& request_headers,
90                                     const BalsaHeaders& response_headers,
91                                     StringPiece response_body) {
92   VLOG(1) << "Adding response for: " << GetKey(request_headers);
93   if (ContainsKey(responses_, GetKey(request_headers))) {
94     LOG(DFATAL) << "Response for given request already exists!";
95     return;
96   }
97   Response* new_response = new Response();
98   new_response->set_headers(response_headers);
99   new_response->set_body(response_body);
100   responses_[GetKey(request_headers)] = new_response;
101 }
102
103 void QuicInMemoryCache::AddSpecialResponse(StringPiece method,
104                                            StringPiece path,
105                                            StringPiece version,
106                                            SpecialResponseType response_type) {
107   BalsaHeaders request_headers, response_headers;
108   request_headers.SetRequestFirstlineFromStringPieces(method,
109                                                       path,
110                                                       version);
111   AddResponse(request_headers, response_headers, "");
112   responses_[GetKey(request_headers)]->response_type_ = response_type;
113 }
114
115 QuicInMemoryCache::QuicInMemoryCache() {
116   Initialize();
117 }
118
119 void QuicInMemoryCache::ResetForTests() {
120   STLDeleteValues(&responses_);
121   Initialize();
122 }
123
124 void QuicInMemoryCache::Initialize() {
125   // If there's no defined cache dir, we have no initialization to do.
126   if (FLAGS_quic_in_memory_cache_dir.empty()) {
127     VLOG(1) << "No cache directory found. Skipping initialization.";
128     return;
129   }
130   VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: "
131           << FLAGS_quic_in_memory_cache_dir;
132
133   FilePath directory(FLAGS_quic_in_memory_cache_dir);
134   base::FileEnumerator file_list(directory,
135                                  true,
136                                  base::FileEnumerator::FILES);
137
138   FilePath file = file_list.Next();
139   while (!file.empty()) {
140     // Need to skip files in .svn directories
141     if (file.value().find("/.svn/") != std::string::npos) {
142       file = file_list.Next();
143       continue;
144     }
145
146     BalsaHeaders request_headers, response_headers;
147
148     string file_contents;
149     base::ReadFileToString(file, &file_contents);
150
151     // Frame HTTP.
152     CachingBalsaVisitor caching_visitor;
153     BalsaFrame framer;
154     framer.set_balsa_headers(&response_headers);
155     framer.set_balsa_visitor(&caching_visitor);
156     size_t processed = 0;
157     while (processed < file_contents.length() &&
158            !caching_visitor.done_framing()) {
159       processed += framer.ProcessInput(file_contents.c_str() + processed,
160                                        file_contents.length() - processed);
161     }
162
163     if (!caching_visitor.done_framing()) {
164       LOG(DFATAL) << "Did not frame entire message from file: " << file.value()
165                   << " (" << processed << " of " << file_contents.length()
166                   << " bytes).";
167     }
168     if (processed < file_contents.length()) {
169       // Didn't frame whole file. Assume remainder is body.
170       // This sometimes happens as a result of incompatibilities between
171       // BalsaFramer and wget's serialization of HTTP sans content-length.
172       caching_visitor.AppendToBody(file_contents.c_str() + processed,
173                                    file_contents.length() - processed);
174       processed += file_contents.length();
175     }
176
177     StringPiece base = file.value();
178     if (response_headers.HasHeader("X-Original-Url")) {
179       base = response_headers.GetHeader("X-Original-Url");
180       response_headers.RemoveAllOfHeader("X-Original-Url");
181       // Remove the protocol so that the string is of the form host + path,
182       // which is parsed properly below.
183       if (StringPieceUtils::StartsWithIgnoreCase(base, "https://")) {
184         base.remove_prefix(8);
185       } else if (StringPieceUtils::StartsWithIgnoreCase(base, "http://")) {
186         base.remove_prefix(7);
187       }
188     }
189     int path_start = base.find_first_of('/');
190     DCHECK_LT(0, path_start);
191     StringPiece host(base.substr(0, path_start));
192     StringPiece path(base.substr(path_start));
193     if (path[path.length() - 1] == ',') {
194       path.remove_suffix(1);
195     }
196     // Set up request headers. Assume method is GET and protocol is HTTP/1.1.
197     request_headers.SetRequestFirstlineFromStringPieces("GET",
198                                                         path,
199                                                         "HTTP/1.1");
200     request_headers.ReplaceOrAppendHeader("host", host);
201
202     VLOG(1) << "Inserting 'http://" << GetKey(request_headers)
203             << "' into QuicInMemoryCache.";
204
205     AddResponse(request_headers, response_headers, caching_visitor.body());
206
207     file = file_list.Next();
208   }
209 }
210
211 QuicInMemoryCache::~QuicInMemoryCache() {
212   STLDeleteValues(&responses_);
213 }
214
215 string QuicInMemoryCache::GetKey(const BalsaHeaders& request_headers) const {
216   StringPiece uri = request_headers.request_uri();
217   if (uri.size() == 0) {
218     return "";
219   }
220   StringPiece host;
221   if (uri[0] == '/') {
222     host = request_headers.GetHeader("host");
223   } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "https://")) {
224     uri.remove_prefix(8);
225   } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "http://")) {
226     uri.remove_prefix(7);
227   }
228   return host.as_string() + uri.as_string();
229 }
230
231 }  // namespace tools
232 }  // namespace net