Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / net / tools / flip_server / mem_cache.cc
blob950a1a6056ed06dd6a0fd22b031e96ac3c6c0704
1 // Copyright (c) 2009 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.
5 #include "net/tools/flip_server/mem_cache.h"
7 #include <dirent.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <stdio.h>
11 #include <sys/stat.h>
12 #include <sys/types.h>
13 #include <unistd.h>
15 #include <deque>
16 #include <map>
17 #include <string>
19 #include "base/strings/string_util.h"
20 #include "net/tools/balsa/balsa_frame.h"
21 #include "net/tools/balsa/balsa_headers.h"
22 #include "net/tools/flip_server/url_to_filename_encoder.h"
23 #include "net/tools/flip_server/url_utilities.h"
25 namespace {
26 // The directory where cache locates);
27 const char FLAGS_cache_base_dir[] = ".";
28 } // namespace
30 namespace net {
32 void StoreBodyAndHeadersVisitor::ProcessBodyData(const char* input,
33 size_t size) {
34 body.append(input, size);
37 void StoreBodyAndHeadersVisitor::HandleHeaderError(BalsaFrame* framer) {
38 HandleError();
41 void StoreBodyAndHeadersVisitor::HandleHeaderWarning(BalsaFrame* framer) {
42 HandleError();
45 void StoreBodyAndHeadersVisitor::HandleChunkingError(BalsaFrame* framer) {
46 HandleError();
49 void StoreBodyAndHeadersVisitor::HandleBodyError(BalsaFrame* framer) {
50 HandleError();
53 FileData::FileData(const BalsaHeaders* headers,
54 const std::string& filename,
55 const std::string& body)
56 : filename_(filename), body_(body) {
57 if (headers) {
58 headers_.reset(new BalsaHeaders);
59 headers_->CopyFrom(*headers);
63 FileData::FileData() {}
65 FileData::~FileData() {}
67 MemoryCache::MemoryCache() : cwd_(FLAGS_cache_base_dir) {}
69 MemoryCache::~MemoryCache() { ClearFiles(); }
71 void MemoryCache::CloneFrom(const MemoryCache& mc) {
72 DCHECK_NE(this, &mc);
73 ClearFiles();
74 files_ = mc.files_;
75 cwd_ = mc.cwd_;
78 void MemoryCache::AddFiles() {
79 std::deque<std::string> paths;
80 paths.push_back(cwd_ + "/GET_");
81 DIR* current_dir = NULL;
82 while (!paths.empty()) {
83 while (current_dir == NULL && !paths.empty()) {
84 std::string current_dir_name = paths.front();
85 VLOG(1) << "Attempting to open dir: \"" << current_dir_name << "\"";
86 current_dir = opendir(current_dir_name.c_str());
87 paths.pop_front();
89 if (current_dir == NULL) {
90 perror("Unable to open directory. ");
91 current_dir_name.clear();
92 continue;
95 if (current_dir) {
96 VLOG(1) << "Succeeded opening";
97 for (struct dirent* dir_data = readdir(current_dir); dir_data != NULL;
98 dir_data = readdir(current_dir)) {
99 std::string current_entry_name =
100 current_dir_name + "/" + dir_data->d_name;
101 if (dir_data->d_type == DT_REG) {
102 VLOG(1) << "Found file: " << current_entry_name;
103 ReadAndStoreFileContents(current_entry_name.c_str());
104 } else if (dir_data->d_type == DT_DIR) {
105 VLOG(1) << "Found subdir: " << current_entry_name;
106 if (std::string(dir_data->d_name) != "." &&
107 std::string(dir_data->d_name) != "..") {
108 VLOG(1) << "Adding to search path: " << current_entry_name;
109 paths.push_front(current_entry_name);
113 VLOG(1) << "Oops, no data left. Closing dir.";
114 closedir(current_dir);
115 current_dir = NULL;
121 void MemoryCache::ReadToString(const char* filename, std::string* output) {
122 output->clear();
123 int fd = open(filename, 0, "r");
124 if (fd == -1)
125 return;
126 char buffer[4096];
127 ssize_t read_status = read(fd, buffer, sizeof(buffer));
128 while (read_status > 0) {
129 output->append(buffer, static_cast<size_t>(read_status));
130 do {
131 read_status = read(fd, buffer, sizeof(buffer));
132 } while (read_status <= 0 && errno == EINTR);
134 close(fd);
137 void MemoryCache::ReadAndStoreFileContents(const char* filename) {
138 StoreBodyAndHeadersVisitor visitor;
139 BalsaFrame framer;
140 framer.set_balsa_visitor(&visitor);
141 framer.set_balsa_headers(&(visitor.headers));
142 std::string filename_contents;
143 ReadToString(filename, &filename_contents);
145 // Ugly hack to make everything look like 1.1.
146 if (filename_contents.find("HTTP/1.0") == 0)
147 filename_contents[7] = '1';
149 size_t pos = 0;
150 size_t old_pos = 0;
151 while (true) {
152 old_pos = pos;
153 pos += framer.ProcessInput(filename_contents.data() + pos,
154 filename_contents.size() - pos);
155 if (framer.Error() || pos == old_pos) {
156 LOG(ERROR) << "Unable to make forward progress, or error"
157 " framing file: " << filename;
158 if (framer.Error()) {
159 LOG(INFO) << "********************************************ERROR!";
160 return;
162 return;
164 if (framer.MessageFullyRead()) {
165 // If no Content-Length or Transfer-Encoding was captured in the
166 // file, then the rest of the data is the body. Many of the captures
167 // from within Chrome don't have content-lengths.
168 if (!visitor.body.length())
169 visitor.body = filename_contents.substr(pos);
170 break;
173 visitor.headers.RemoveAllOfHeader("content-length");
174 visitor.headers.RemoveAllOfHeader("transfer-encoding");
175 visitor.headers.RemoveAllOfHeader("connection");
176 visitor.headers.AppendHeader("transfer-encoding", "chunked");
177 visitor.headers.AppendHeader("connection", "keep-alive");
179 // Experiment with changing headers for forcing use of cached
180 // versions of content.
181 // TODO(mbelshe) REMOVE ME
182 #if 0
183 // TODO(mbelshe) append current date.
184 visitor.headers.RemoveAllOfHeader("date");
185 if (visitor.headers.HasHeader("expires")) {
186 visitor.headers.RemoveAllOfHeader("expires");
187 visitor.headers.AppendHeader("expires",
188 "Fri, 30 Aug, 2019 12:00:00 GMT");
190 #endif
191 DCHECK_GE(std::string(filename).size(), cwd_.size() + 1);
192 DCHECK_EQ(std::string(filename).substr(0, cwd_.size()), cwd_);
193 DCHECK_EQ(filename[cwd_.size()], '/');
194 std::string filename_stripped = std::string(filename).substr(cwd_.size() + 1);
195 LOG(INFO) << "Adding file (" << visitor.body.length()
196 << " bytes): " << filename_stripped;
197 size_t slash_pos = filename_stripped.find('/');
198 if (slash_pos == std::string::npos) {
199 slash_pos = filename_stripped.size();
201 InsertFile(
202 &visitor.headers, filename_stripped.substr(0, slash_pos), visitor.body);
205 FileData* MemoryCache::GetFileData(const std::string& filename) {
206 Files::iterator fi = files_.end();
207 if (base::EndsWith(filename, ".html", base::CompareCase::SENSITIVE)) {
208 fi = files_.find(filename.substr(0, filename.size() - 5) + ".http");
210 if (fi == files_.end())
211 fi = files_.find(filename);
213 if (fi == files_.end()) {
214 return NULL;
216 return fi->second;
219 bool MemoryCache::AssignFileData(const std::string& filename,
220 MemCacheIter* mci) {
221 mci->file_data = GetFileData(filename);
222 if (mci->file_data == NULL) {
223 LOG(ERROR) << "Could not find file data for " << filename;
224 return false;
226 return true;
229 void MemoryCache::InsertFile(const BalsaHeaders* headers,
230 const std::string& filename,
231 const std::string& body) {
232 InsertFile(new FileData(headers, filename, body));
235 void MemoryCache::InsertFile(FileData* file_data) {
236 Files::iterator it = files_.find(file_data->filename());
237 if (it != files_.end()) {
238 delete it->second;
239 it->second = file_data;
240 } else {
241 files_.insert(std::make_pair(file_data->filename(), file_data));
245 void MemoryCache::ClearFiles() {
246 for (Files::const_iterator i = files_.begin(); i != files_.end(); ++i) {
247 delete i->second;
249 files_.clear();
252 } // namespace net