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 #include <unistd.h> 14 15 #include <deque> 16 #include <map> 17 #include <string> 18 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/dump_cache/url_to_filename_encoder.h" 23 #include "net/tools/dump_cache/url_utilities.h" 24 25 namespace { 26 // The directory where cache locates); 27 const char FLAGS_cache_base_dir[] = "."; 28 } // namespace 29 30 namespace net { 31 32 void StoreBodyAndHeadersVisitor::ProcessBodyData(const char* input, 33 size_t size) { 34 body.append(input, size); 35 } 36 37 void StoreBodyAndHeadersVisitor::HandleHeaderError(BalsaFrame* framer) { 38 HandleError(); 39 } 40 41 void StoreBodyAndHeadersVisitor::HandleHeaderWarning(BalsaFrame* framer) { 42 HandleError(); 43 } 44 45 void StoreBodyAndHeadersVisitor::HandleChunkingError(BalsaFrame* framer) { 46 HandleError(); 47 } 48 49 void StoreBodyAndHeadersVisitor::HandleBodyError(BalsaFrame* framer) { 50 HandleError(); 51 } 52 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); 60 } 61 } 62 63 FileData::FileData() {} 64 65 FileData::~FileData() {} 66 67 MemoryCache::MemoryCache() : cwd_(FLAGS_cache_base_dir) {} 68 69 MemoryCache::~MemoryCache() { ClearFiles(); } 70 71 void MemoryCache::CloneFrom(const MemoryCache& mc) { 72 DCHECK_NE(this, &mc); 73 ClearFiles(); 74 files_ = mc.files_; 75 cwd_ = mc.cwd_; 76 } 77 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(); 88 89 if (current_dir == NULL) { 90 perror("Unable to open directory. "); 91 current_dir_name.clear(); 92 continue; 93 } 94 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); 110 } 111 } 112 } 113 VLOG(1) << "Oops, no data left. Closing dir."; 114 closedir(current_dir); 115 current_dir = NULL; 116 } 117 } 118 } 119 } 120 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); 133 } 134 close(fd); 135 } 136 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); 144 145 // Ugly hack to make everything look like 1.1. 146 if (filename_contents.find("HTTP/1.0") == 0) 147 filename_contents[7] = '1'; 148 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; 161 } 162 return; 163 } 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; 171 } 172 } 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"); 178 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"); 189 } 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(); 200 } 201 InsertFile( 202 &visitor.headers, filename_stripped.substr(0, slash_pos), visitor.body); 203 } 204 205 FileData* MemoryCache::GetFileData(const std::string& filename) { 206 Files::iterator fi = files_.end(); 207 if (EndsWith(filename, ".html", true)) { 208 fi = files_.find(filename.substr(0, filename.size() - 5) + ".http"); 209 } 210 if (fi == files_.end()) 211 fi = files_.find(filename); 212 213 if (fi == files_.end()) { 214 return NULL; 215 } 216 return fi->second; 217 } 218 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; 225 } 226 return true; 227 } 228 229 void MemoryCache::InsertFile(const BalsaHeaders* headers, 230 const std::string& filename, 231 const std::string& body) { 232 InsertFile(new FileData(headers, filename, body)); 233 } 234 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)); 242 } 243 } 244 245 void MemoryCache::ClearFiles() { 246 for (Files::const_iterator i = files_.begin(); i != files_.end(); ++i) { 247 delete i->second; 248 } 249 files_.clear(); 250 } 251 252 } // namespace net 253