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 27 void StoreBodyAndHeadersVisitor::ProcessBodyData(const char *input, 28 size_t size) { 29 body.append(input, size); 30 } 31 32 void StoreBodyAndHeadersVisitor::HandleHeaderError(BalsaFrame* framer) { 33 HandleError(); 34 } 35 36 void StoreBodyAndHeadersVisitor::HandleHeaderWarning(BalsaFrame* framer) { 37 HandleError(); 38 } 39 40 void StoreBodyAndHeadersVisitor::HandleChunkingError(BalsaFrame* framer) { 41 HandleError(); 42 } 43 44 void StoreBodyAndHeadersVisitor::HandleBodyError(BalsaFrame* framer) { 45 HandleError(); 46 } 47 48 FileData::FileData(BalsaHeaders* h, const std::string& b) 49 : headers(h), body(b) { 50 } 51 52 FileData::FileData() {} 53 54 FileData::~FileData() {} 55 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 64 MemoryCache::MemoryCache() {} 65 66 MemoryCache::~MemoryCache() {} 67 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 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 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 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 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 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