We started redesigning GpuMemoryBuffer interface to handle multiple buffers [0].
[chromium-blink-merge.git] / net / tools / dump_cache / cache_dumper.cc
blob8820798153f82a0eb6edd2481b32a3d0d3c411b7
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.
5 #include "net/tools/dump_cache/cache_dumper.h"
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"
17 CacheDumper::CacheDumper(disk_cache::Backend* cache)
18 : cache_(cache) {
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);
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);
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();
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"\\");
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;
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++;
64 // Now create the full path
65 return CreateDirectoryW(path.value().c_str(), NULL) == TRUE;
66 #else
67 return base::CreateDirectory(path);
68 #endif
71 DiskDumper::DiskDumper(const base::FilePath& path)
72 : path_(path.AsEndingWithSeparator()), entry_(NULL) {
73 base::CreateDirectory(path);
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);
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
98 entry_url_ = key;
100 SafeCreateDirectory(entry_path_.DirName());
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
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
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.
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");
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");
151 // Mark the end of headers
152 output->append("\r\n");
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;
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;
171 // Skip this entry if it was truncated (results in an empty file).
172 if (truncated)
173 return buf_len;
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");
180 // Convert the headers into a string ending with LF.
181 GetNormalizedHeaders(response_info, &headers);
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);
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);
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;
204 #ifdef WIN32_LARGE_FILENAME_SUPPORT
205 DWORD bytes;
206 if (!WriteFile(entry_, data, len, &bytes, 0))
207 return 0;
209 return bytes;
210 #else
211 return fwrite(data, 1, len, entry_);
212 #endif
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