Home | History | Annotate | Download | only in dump_cache
      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/dump_cache/cache_dumper.h"
      6 
      7 #include "net/base/io_buffer.h"
      8 #include "net/disk_cache/entry_impl.h"
      9 #include "net/http/http_cache.h"
     10 #include "net/http/http_response_headers.h"
     11 #include "net/http/http_response_info.h"
     12 #include "net/tools/dump_cache/url_to_filename_encoder.h"
     13 
     14 bool CacheDumper::CreateEntry(const std::string& key,
     15                               disk_cache::Entry** entry) {
     16   return cache_->CreateEntry(key, entry);
     17 }
     18 
     19 bool CacheDumper::WriteEntry(disk_cache::Entry* entry, int index, int offset,
     20                              net::IOBuffer* buf, int buf_len) {
     21   int written = entry->WriteData(index, offset, buf, buf_len, NULL, false);
     22   return written == buf_len;
     23 }
     24 
     25 void CacheDumper::CloseEntry(disk_cache::Entry* entry, base::Time last_used,
     26                              base::Time last_modified) {
     27   if (entry) {
     28     static_cast<disk_cache::EntryImpl*>(entry)->SetTimes(last_used,
     29                                                          last_modified);
     30     entry->Close();
     31   }
     32 }
     33 
     34 // A version of CreateDirectory which supports lengthy filenames.
     35 // Returns true on success, false on failure.
     36 bool SafeCreateDirectory(const std::wstring& path) {
     37 #ifdef WIN32_LARGE_FILENAME_SUPPORT
     38   // Due to large paths on windows, it can't simply do a
     39   // CreateDirectory("a/b/c").  Instead, create each subdirectory manually.
     40   bool rv = false;
     41   std::wstring::size_type pos(0);
     42   std::wstring backslash(L"\\");
     43 
     44   // If the path starts with the long file header, skip over that
     45   const std::wstring kLargeFilenamePrefix(L"\\\\?\\");
     46   std::wstring header(kLargeFilenamePrefix);
     47   if (path.find(header) == 0)
     48     pos = 4;
     49 
     50   // Create the subdirectories individually
     51   while((pos = path.find(backslash, pos)) != std::wstring::npos) {
     52     std::wstring subdir = path.substr(0, pos);
     53     CreateDirectoryW(subdir.c_str(), NULL);
     54     // we keep going even if directory creation failed.
     55     pos++;
     56   }
     57   // Now create the full path
     58   return CreateDirectoryW(path.c_str(), NULL) == TRUE;
     59 #else
     60   return file_util::CreateDirectory(path);
     61 #endif
     62 }
     63 
     64 bool DiskDumper::CreateEntry(const std::string& key,
     65                              disk_cache::Entry** entry) {
     66   FilePath path(path_);
     67   entry_path_ = net::UrlToFilenameEncoder::Encode(key, path);
     68 
     69 #ifdef WIN32_LARGE_FILENAME_SUPPORT
     70   // In order for long filenames to work, we'll need to prepend
     71   // the windows magic token.
     72   const std::wstring kLongFilenamePrefix(L"\\\\?\\");
     73   // There is no way to prepend to a filename.  We simply *have*
     74   // to convert to a wstring to do this.
     75   std::wstring name = kLongFilenamePrefix;
     76   name.append(entry_path_.ToWStringHack());
     77   entry_path_ = FilePath(name);
     78 #endif
     79 
     80   entry_url_ = key;
     81 
     82   FilePath directory = entry_path_.DirName();
     83   SafeCreateDirectory(directory.value());
     84 
     85   std::wstring file = entry_path_.value();
     86 #ifdef WIN32_LARGE_FILENAME_SUPPORT
     87   entry_ = CreateFileW(file.c_str(), GENERIC_WRITE|GENERIC_READ, 0, 0,
     88                        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
     89   return entry_ != INVALID_HANDLE_VALUE;
     90 #else
     91   entry_ = file_util::OpenFile(entry_path_, "w+");
     92   return entry_ != NULL;
     93 #endif
     94 }
     95 
     96 // Utility Function to create a normalized header string from a
     97 // HttpResponseInfo.  The output will be formatted exactly
     98 // like so:
     99 //     HTTP/<version> <status_code> <status_text>\n
    100 //     [<header-name>: <header-values>\n]*
    101 // meaning, each line is \n-terminated, and there is no extra whitespace
    102 // beyond the single space separators shown (of course, values can contain
    103 // whitespace within them).  If a given header-name appears more than once
    104 // in the set of headers, they are combined into a single line like so:
    105 //     <header-name>: <header-value1>, <header-value2>, ...<header-valueN>\n
    106 //
    107 // DANGER: For some headers (e.g., "Set-Cookie"), the normalized form can be
    108 // a lossy format.  This is due to the fact that some servers generate
    109 // Set-Cookie headers that contain unquoted commas (usually as part of the
    110 // value of an "expires" attribute).  So, use this function with caution.  Do
    111 // not expect to be able to re-parse Set-Cookie headers from this output.
    112 //
    113 // NOTE: Do not make any assumptions about the encoding of this output
    114 // string.  It may be non-ASCII, and the encoding used by the server is not
    115 // necessarily known to us.  Do not assume that this output is UTF-8!
    116 void GetNormalizedHeaders(const net::HttpResponseInfo& info,
    117                           std::string* output) {
    118   // Start with the status line
    119   output->assign(info.headers->GetStatusLine());
    120   output->append("\r\n");
    121 
    122   // Enumerate the headers
    123   void* iter = 0;
    124   std::string name, value;
    125   while (info.headers->EnumerateHeaderLines(&iter, &name, &value)) {
    126     output->append(name);
    127     output->append(": ");
    128     output->append(value);
    129     output->append("\r\n");
    130   }
    131 
    132   // Mark the end of headers
    133   output->append("\r\n");
    134 }
    135 
    136 bool DiskDumper::WriteEntry(disk_cache::Entry* entry, int index, int offset,
    137                             net::IOBuffer* buf, int buf_len) {
    138   if (!entry_)
    139     return false;
    140 
    141   std::string headers;
    142   const char *data;
    143   int len;
    144   if (index == 0) {  // Stream 0 is the headers.
    145     net::HttpResponseInfo response_info;
    146     bool truncated;
    147     if (!net::HttpCache::ParseResponseInfo(buf->data(), buf_len,
    148                                            &response_info, &truncated))
    149       return false;
    150 
    151     // Skip this entry if it was truncated (results in an empty file).
    152     if (truncated)
    153       return true;
    154 
    155     // Remove the size headers.
    156     response_info.headers->RemoveHeader("transfer-encoding");
    157     response_info.headers->RemoveHeader("content-length");
    158     response_info.headers->RemoveHeader("x-original-url");
    159 
    160     // Convert the headers into a string ending with LF.
    161     GetNormalizedHeaders(response_info, &headers);
    162 
    163     // Append a header for the original URL.
    164     std::string url = entry_url_;
    165     // strip off the "XXGET" which may be in the key.
    166     std::string::size_type pos(0);
    167     if ((pos = url.find("http")) != 0) {
    168       if (pos != std::string::npos)
    169         url = url.substr(pos);
    170     }
    171     std::string x_original_url = "X-Original-Url: " + url + "\r\n";
    172     // we know that the last two bytes are CRLF.
    173     headers.replace(headers.length() - 2, 0, x_original_url);
    174 
    175     data = headers.c_str();
    176     len = headers.size();
    177   } else if (index == 1) {  // Stream 1 is the data.
    178     data = buf->data();
    179     len = buf_len;
    180   }
    181 #ifdef WIN32_LARGE_FILENAME_SUPPORT
    182   DWORD bytes;
    183   DWORD rv = WriteFile(entry_, data, len, &bytes, 0);
    184   return rv == TRUE && bytes == len;
    185 #else
    186   int bytes = fwrite(data, 1, len, entry_);
    187   return bytes == len;
    188 #endif
    189 }
    190 
    191 void DiskDumper::CloseEntry(disk_cache::Entry* entry, base::Time last_used,
    192                           base::Time last_modified) {
    193 #ifdef WIN32_LARGE_FILENAME_SUPPORT
    194   CloseHandle(entry_);
    195 #else
    196   file_util::CloseFile(entry_);
    197 #endif
    198 }
    199 
    200