Home | History | Annotate | Download | only in flip_server
      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