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