// Copyright (c) 2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/tools/flip_server/mem_cache.h" #include <dirent.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <deque> #include <map> #include <string> #include "base/strings/string_util.h" #include "net/tools/balsa/balsa_frame.h" #include "net/tools/balsa/balsa_headers.h" #include "net/tools/dump_cache/url_to_filename_encoder.h" #include "net/tools/dump_cache/url_utilities.h" namespace { // The directory where cache locates); const char FLAGS_cache_base_dir[] = "."; } // namespace namespace net { void StoreBodyAndHeadersVisitor::ProcessBodyData(const char* input, size_t size) { body.append(input, size); } void StoreBodyAndHeadersVisitor::HandleHeaderError(BalsaFrame* framer) { HandleError(); } void StoreBodyAndHeadersVisitor::HandleHeaderWarning(BalsaFrame* framer) { HandleError(); } void StoreBodyAndHeadersVisitor::HandleChunkingError(BalsaFrame* framer) { HandleError(); } void StoreBodyAndHeadersVisitor::HandleBodyError(BalsaFrame* framer) { HandleError(); } FileData::FileData(const BalsaHeaders* headers, const std::string& filename, const std::string& body) : filename_(filename), body_(body) { if (headers) { headers_.reset(new BalsaHeaders); headers_->CopyFrom(*headers); } } FileData::FileData() {} FileData::~FileData() {} MemoryCache::MemoryCache() : cwd_(FLAGS_cache_base_dir) {} MemoryCache::~MemoryCache() { ClearFiles(); } void MemoryCache::CloneFrom(const MemoryCache& mc) { DCHECK_NE(this, &mc); ClearFiles(); files_ = mc.files_; cwd_ = mc.cwd_; } void MemoryCache::AddFiles() { std::deque<std::string> paths; paths.push_back(cwd_ + "/GET_"); DIR* current_dir = NULL; while (!paths.empty()) { while (current_dir == NULL && !paths.empty()) { std::string current_dir_name = paths.front(); VLOG(1) << "Attempting to open dir: \"" << current_dir_name << "\""; current_dir = opendir(current_dir_name.c_str()); paths.pop_front(); if (current_dir == NULL) { perror("Unable to open directory. "); current_dir_name.clear(); continue; } if (current_dir) { VLOG(1) << "Succeeded opening"; for (struct dirent* dir_data = readdir(current_dir); dir_data != NULL; dir_data = readdir(current_dir)) { std::string current_entry_name = current_dir_name + "/" + dir_data->d_name; if (dir_data->d_type == DT_REG) { VLOG(1) << "Found file: " << current_entry_name; ReadAndStoreFileContents(current_entry_name.c_str()); } else if (dir_data->d_type == DT_DIR) { VLOG(1) << "Found subdir: " << current_entry_name; if (std::string(dir_data->d_name) != "." && std::string(dir_data->d_name) != "..") { VLOG(1) << "Adding to search path: " << current_entry_name; paths.push_front(current_entry_name); } } } VLOG(1) << "Oops, no data left. Closing dir."; closedir(current_dir); current_dir = NULL; } } } } void MemoryCache::ReadToString(const char* filename, std::string* output) { output->clear(); int fd = open(filename, 0, "r"); if (fd == -1) return; char buffer[4096]; ssize_t read_status = read(fd, buffer, sizeof(buffer)); while (read_status > 0) { output->append(buffer, static_cast<size_t>(read_status)); do { read_status = read(fd, buffer, sizeof(buffer)); } while (read_status <= 0 && errno == EINTR); } close(fd); } void MemoryCache::ReadAndStoreFileContents(const char* filename) { StoreBodyAndHeadersVisitor visitor; BalsaFrame framer; framer.set_balsa_visitor(&visitor); framer.set_balsa_headers(&(visitor.headers)); std::string filename_contents; ReadToString(filename, &filename_contents); // Ugly hack to make everything look like 1.1. if (filename_contents.find("HTTP/1.0") == 0) filename_contents[7] = '1'; size_t pos = 0; size_t old_pos = 0; while (true) { old_pos = pos; pos += framer.ProcessInput(filename_contents.data() + pos, filename_contents.size() - pos); if (framer.Error() || pos == old_pos) { LOG(ERROR) << "Unable to make forward progress, or error" " framing file: " << filename; if (framer.Error()) { LOG(INFO) << "********************************************ERROR!"; return; } return; } if (framer.MessageFullyRead()) { // If no Content-Length or Transfer-Encoding was captured in the // file, then the rest of the data is the body. Many of the captures // from within Chrome don't have content-lengths. if (!visitor.body.length()) visitor.body = filename_contents.substr(pos); break; } } visitor.headers.RemoveAllOfHeader("content-length"); visitor.headers.RemoveAllOfHeader("transfer-encoding"); visitor.headers.RemoveAllOfHeader("connection"); visitor.headers.AppendHeader("transfer-encoding", "chunked"); visitor.headers.AppendHeader("connection", "keep-alive"); // Experiment with changing headers for forcing use of cached // versions of content. // TODO(mbelshe) REMOVE ME #if 0 // TODO(mbelshe) append current date. visitor.headers.RemoveAllOfHeader("date"); if (visitor.headers.HasHeader("expires")) { visitor.headers.RemoveAllOfHeader("expires"); visitor.headers.AppendHeader("expires", "Fri, 30 Aug, 2019 12:00:00 GMT"); } #endif DCHECK_GE(std::string(filename).size(), cwd_.size() + 1); DCHECK_EQ(std::string(filename).substr(0, cwd_.size()), cwd_); DCHECK_EQ(filename[cwd_.size()], '/'); std::string filename_stripped = std::string(filename).substr(cwd_.size() + 1); LOG(INFO) << "Adding file (" << visitor.body.length() << " bytes): " << filename_stripped; size_t slash_pos = filename_stripped.find('/'); if (slash_pos == std::string::npos) { slash_pos = filename_stripped.size(); } InsertFile( &visitor.headers, filename_stripped.substr(0, slash_pos), visitor.body); } FileData* MemoryCache::GetFileData(const std::string& filename) { Files::iterator fi = files_.end(); if (EndsWith(filename, ".html", true)) { fi = files_.find(filename.substr(0, filename.size() - 5) + ".http"); } if (fi == files_.end()) fi = files_.find(filename); if (fi == files_.end()) { return NULL; } return fi->second; } bool MemoryCache::AssignFileData(const std::string& filename, MemCacheIter* mci) { mci->file_data = GetFileData(filename); if (mci->file_data == NULL) { LOG(ERROR) << "Could not find file data for " << filename; return false; } return true; } void MemoryCache::InsertFile(const BalsaHeaders* headers, const std::string& filename, const std::string& body) { InsertFile(new FileData(headers, filename, body)); } void MemoryCache::InsertFile(FileData* file_data) { Files::iterator it = files_.find(file_data->filename()); if (it != files_.end()) { delete it->second; it->second = file_data; } else { files_.insert(std::make_pair(file_data->filename(), file_data)); } } void MemoryCache::ClearFiles() { for (Files::const_iterator i = files_.begin(); i != files_.end(); ++i) { delete i->second; } files_.clear(); } } // namespace net