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.
4
5 #include "net/tools/flip_server/mem_cache.h"
6
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
14 #include <deque>
15
16 #include "base/string_piece.h"
17 #include "net/tools/dump_cache/url_to_filename_encoder.h"
18 #include "net/tools/dump_cache/url_utilities.h"
19 #include "net/tools/flip_server/balsa_frame.h"
20 #include "net/tools/flip_server/balsa_headers.h"
21
22 // The directory where cache locates);
23 std::string FLAGS_cache_base_dir = ".";
24
25 namespace net {
26
ProcessBodyData(const char * input,size_t size)27 void StoreBodyAndHeadersVisitor::ProcessBodyData(const char *input,
28 size_t size) {
29 body.append(input, size);
30 }
31
HandleHeaderError(BalsaFrame * framer)32 void StoreBodyAndHeadersVisitor::HandleHeaderError(BalsaFrame* framer) {
33 HandleError();
34 }
35
HandleHeaderWarning(BalsaFrame * framer)36 void StoreBodyAndHeadersVisitor::HandleHeaderWarning(BalsaFrame* framer) {
37 HandleError();
38 }
39
HandleChunkingError(BalsaFrame * framer)40 void StoreBodyAndHeadersVisitor::HandleChunkingError(BalsaFrame* framer) {
41 HandleError();
42 }
43
HandleBodyError(BalsaFrame * framer)44 void StoreBodyAndHeadersVisitor::HandleBodyError(BalsaFrame* framer) {
45 HandleError();
46 }
47
FileData(BalsaHeaders * h,const std::string & b)48 FileData::FileData(BalsaHeaders* h, const std::string& b)
49 : headers(h), body(b) {
50 }
51
FileData()52 FileData::FileData() {}
53
~FileData()54 FileData::~FileData() {}
55
CopyFrom(const FileData & file_data)56 void FileData::CopyFrom(const FileData& file_data) {
57 headers = new BalsaHeaders;
58 headers->CopyFrom(*(file_data.headers));
59 filename = file_data.filename;
60 related_files = file_data.related_files;
61 body = file_data.body;
62 }
63
MemoryCache()64 MemoryCache::MemoryCache() {}
65
~MemoryCache()66 MemoryCache::~MemoryCache() {}
67
CloneFrom(const MemoryCache & mc)68 void MemoryCache::CloneFrom(const MemoryCache& mc) {
69 for (Files::const_iterator i = mc.files_.begin();
70 i != mc.files_.end();
71 ++i) {
72 Files::iterator out_i =
73 files_.insert(make_pair(i->first, FileData())).first;
74 out_i->second.CopyFrom(i->second);
75 cwd_ = mc.cwd_;
76 }
77 }
78
AddFiles()79 void MemoryCache::AddFiles() {
80 std::deque<std::string> paths;
81 cwd_ = FLAGS_cache_base_dir;
82 paths.push_back(cwd_ + "/GET_");
83 DIR* current_dir = NULL;
84 while (!paths.empty()) {
85 while (current_dir == NULL && !paths.empty()) {
86 std::string current_dir_name = paths.front();
87 VLOG(1) << "Attempting to open dir: \"" << current_dir_name << "\"";
88 current_dir = opendir(current_dir_name.c_str());
89 paths.pop_front();
90
91 if (current_dir == NULL) {
92 perror("Unable to open directory. ");
93 current_dir_name.clear();
94 continue;
95 }
96
97 if (current_dir) {
98 VLOG(1) << "Succeeded opening";
99 for (struct dirent* dir_data = readdir(current_dir);
100 dir_data != NULL;
101 dir_data = readdir(current_dir)) {
102 std::string current_entry_name =
103 current_dir_name + "/" + dir_data->d_name;
104 if (dir_data->d_type == DT_REG) {
105 VLOG(1) << "Found file: " << current_entry_name;
106 ReadAndStoreFileContents(current_entry_name.c_str());
107 } else if (dir_data->d_type == DT_DIR) {
108 VLOG(1) << "Found subdir: " << current_entry_name;
109 if (std::string(dir_data->d_name) != "." &&
110 std::string(dir_data->d_name) != "..") {
111 VLOG(1) << "Adding to search path: " << current_entry_name;
112 paths.push_front(current_entry_name);
113 }
114 }
115 }
116 VLOG(1) << "Oops, no data left. Closing dir.";
117 closedir(current_dir);
118 current_dir = NULL;
119 }
120 }
121 }
122 }
123
ReadToString(const char * filename,std::string * output)124 void MemoryCache::ReadToString(const char* filename, std::string* output) {
125 output->clear();
126 int fd = open(filename, 0, "r");
127 if (fd == -1)
128 return;
129 char buffer[4096];
130 ssize_t read_status = read(fd, buffer, sizeof(buffer));
131 while (read_status > 0) {
132 output->append(buffer, static_cast<size_t>(read_status));
133 do {
134 read_status = read(fd, buffer, sizeof(buffer));
135 } while (read_status <= 0 && errno == EINTR);
136 }
137 close(fd);
138 }
139
ReadAndStoreFileContents(const char * filename)140 void MemoryCache::ReadAndStoreFileContents(const char* filename) {
141 StoreBodyAndHeadersVisitor visitor;
142 BalsaFrame framer;
143 framer.set_balsa_visitor(&visitor);
144 framer.set_balsa_headers(&(visitor.headers));
145 std::string filename_contents;
146 ReadToString(filename, &filename_contents);
147
148 // Ugly hack to make everything look like 1.1.
149 if (filename_contents.find("HTTP/1.0") == 0)
150 filename_contents[7] = '1';
151
152 size_t pos = 0;
153 size_t old_pos = 0;
154 while (true) {
155 old_pos = pos;
156 pos += framer.ProcessInput(filename_contents.data() + pos,
157 filename_contents.size() - pos);
158 if (framer.Error() || pos == old_pos) {
159 LOG(ERROR) << "Unable to make forward progress, or error"
160 " framing file: " << filename;
161 if (framer.Error()) {
162 LOG(INFO) << "********************************************ERROR!";
163 return;
164 }
165 return;
166 }
167 if (framer.MessageFullyRead()) {
168 // If no Content-Length or Transfer-Encoding was captured in the
169 // file, then the rest of the data is the body. Many of the captures
170 // from within Chrome don't have content-lengths.
171 if (!visitor.body.length())
172 visitor.body = filename_contents.substr(pos);
173 break;
174 }
175 }
176 visitor.headers.RemoveAllOfHeader("content-length");
177 visitor.headers.RemoveAllOfHeader("transfer-encoding");
178 visitor.headers.RemoveAllOfHeader("connection");
179 visitor.headers.AppendHeader("transfer-encoding", "chunked");
180 visitor.headers.AppendHeader("connection", "keep-alive");
181
182 // Experiment with changing headers for forcing use of cached
183 // versions of content.
184 // TODO(mbelshe) REMOVE ME
185 #if 0
186 // TODO(mbelshe) append current date.
187 visitor.headers.RemoveAllOfHeader("date");
188 if (visitor.headers.HasHeader("expires")) {
189 visitor.headers.RemoveAllOfHeader("expires");
190 visitor.headers.AppendHeader("expires",
191 "Fri, 30 Aug, 2019 12:00:00 GMT");
192 }
193 #endif
194 BalsaHeaders* headers = new BalsaHeaders;
195 headers->CopyFrom(visitor.headers);
196 std::string filename_stripped = std::string(filename).substr(cwd_.size() + 1);
197 LOG(INFO) << "Adding file (" << visitor.body.length() << " bytes): "
198 << filename_stripped;
199 files_[filename_stripped] = FileData();
200 FileData& fd = files_[filename_stripped];
201 fd = FileData(headers, visitor.body);
202 fd.filename = std::string(filename_stripped,
203 filename_stripped.find_first_of('/'));
204 }
205
GetFileData(const std::string & filename)206 FileData* MemoryCache::GetFileData(const std::string& filename) {
207 Files::iterator fi = files_.end();
208 if (filename.compare(filename.length() - 5, 5, ".html", 5) == 0) {
209 std::string new_filename(filename.data(), filename.size() - 5);
210 new_filename += ".http";
211 fi = files_.find(new_filename);
212 }
213 if (fi == files_.end())
214 fi = files_.find(filename);
215
216 if (fi == files_.end()) {
217 return NULL;
218 }
219 return &(fi->second);
220 }
221
AssignFileData(const std::string & filename,MemCacheIter * mci)222 bool MemoryCache::AssignFileData(const std::string& filename,
223 MemCacheIter* mci) {
224 mci->file_data = GetFileData(filename);
225 if (mci->file_data == NULL) {
226 LOG(ERROR) << "Could not find file data for " << filename;
227 return false;
228 }
229 return true;
230 }
231
232 } // namespace net
233
234