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