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