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