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