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/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
)
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
) {
36 static_cast<disk_cache::EntryImpl
*>(entry
)->SetTimes(last_used
,
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.
49 std::wstring::size_type
pos(0);
50 std::wstring
backslash(L
"\\");
52 // If the path starts with the long file header, skip over that
53 const std::wstring
kLargeFilenamePrefix(L
"\\\\?\\");
54 std::wstring
header(kLargeFilenamePrefix
);
55 if (path
.value().find(header
) == 0)
58 // Create the subdirectories individually
59 while ((pos
= path
.value().find(backslash
, pos
)) != std::wstring::npos
) {
60 base::FilePath::StringType subdir
= path
.value().substr(0, pos
);
61 CreateDirectoryW(subdir
.c_str(), NULL
);
62 // we keep going even if directory creation failed.
65 // Now create the full path
66 return CreateDirectoryW(path
.value().c_str(), NULL
) == TRUE
;
68 return base::CreateDirectory(path
);
72 DiskDumper::DiskDumper(const base::FilePath
& path
)
73 : path_(path
), entry_(NULL
) {
74 base::CreateDirectory(path
);
77 int DiskDumper::CreateEntry(const std::string
& key
,
78 disk_cache::Entry
** entry
,
79 const net::CompletionCallback
& callback
) {
80 // The URL may not start with a valid protocol; search for it.
81 int urlpos
= key
.find("http");
82 std::string url
= urlpos
> 0 ? key
.substr(urlpos
) : key
;
83 std::string base_path
= path_
.MaybeAsASCII();
84 std::string new_path
=
85 net::UrlToFilenameEncoder::Encode(url
, base_path
, false);
86 entry_path_
= base::FilePath::FromUTF8Unsafe(new_path
);
88 #ifdef WIN32_LARGE_FILENAME_SUPPORT
89 // In order for long filenames to work, we'll need to prepend
90 // the windows magic token.
91 const std::wstring
kLongFilenamePrefix(L
"\\\\?\\");
92 // There is no way to prepend to a filename. We simply *have*
93 // to convert to a wstring to do this.
94 std::wstring name
= kLongFilenamePrefix
;
95 name
.append(entry_path_
.value());
96 entry_path_
= base::FilePath(name
);
101 SafeCreateDirectory(entry_path_
.DirName());
103 base::FilePath::StringType file
= entry_path_
.value();
104 #ifdef WIN32_LARGE_FILENAME_SUPPORT
105 entry_
= CreateFileW(file
.c_str(), GENERIC_WRITE
|GENERIC_READ
, 0, 0,
106 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
107 if (entry_
== INVALID_HANDLE_VALUE
)
108 wprintf(L
"CreateFileW (%s) failed: %d\n", file
.c_str(), GetLastError());
109 return (entry_
!= INVALID_HANDLE_VALUE
) ? net::OK
: net::ERR_FAILED
;
111 entry_
= base::OpenFile(entry_path_
, "w+");
112 return (entry_
!= NULL
) ? net::OK
: net::ERR_FAILED
;
116 // Utility Function to create a normalized header string from a
117 // HttpResponseInfo. The output will be formatted exactly
119 // HTTP/<version> <status_code> <status_text>\n
120 // [<header-name>: <header-values>\n]*
121 // meaning, each line is \n-terminated, and there is no extra whitespace
122 // beyond the single space separators shown (of course, values can contain
123 // whitespace within them). If a given header-name appears more than once
124 // in the set of headers, they are combined into a single line like so:
125 // <header-name>: <header-value1>, <header-value2>, ...<header-valueN>\n
127 // DANGER: For some headers (e.g., "Set-Cookie"), the normalized form can be
128 // a lossy format. This is due to the fact that some servers generate
129 // Set-Cookie headers that contain unquoted commas (usually as part of the
130 // value of an "expires" attribute). So, use this function with caution. Do
131 // not expect to be able to re-parse Set-Cookie headers from this output.
133 // NOTE: Do not make any assumptions about the encoding of this output
134 // string. It may be non-ASCII, and the encoding used by the server is not
135 // necessarily known to us. Do not assume that this output is UTF-8!
136 void GetNormalizedHeaders(const net::HttpResponseInfo
& info
,
137 std::string
* output
) {
138 // Start with the status line
139 output
->assign(info
.headers
->GetStatusLine());
140 output
->append("\r\n");
142 // Enumerate the headers
144 std::string name
, value
;
145 while (info
.headers
->EnumerateHeaderLines(&iter
, &name
, &value
)) {
146 output
->append(name
);
147 output
->append(": ");
148 output
->append(value
);
149 output
->append("\r\n");
152 // Mark the end of headers
153 output
->append("\r\n");
156 int DiskDumper::WriteEntry(disk_cache::Entry
* entry
, int index
, int offset
,
157 net::IOBuffer
* buf
, int buf_len
,
158 const net::CompletionCallback
& callback
) {
165 if (index
== 0) { // Stream 0 is the headers.
166 net::HttpResponseInfo response_info
;
168 if (!net::HttpCache::ParseResponseInfo(buf
->data(), buf_len
,
169 &response_info
, &truncated
))
172 // Skip this entry if it was truncated (results in an empty file).
176 // Remove the size headers.
177 response_info
.headers
->RemoveHeader("transfer-encoding");
178 response_info
.headers
->RemoveHeader("content-length");
179 response_info
.headers
->RemoveHeader("x-original-url");
181 // Convert the headers into a string ending with LF.
182 GetNormalizedHeaders(response_info
, &headers
);
184 // Append a header for the original URL.
185 std::string url
= entry_url_
;
186 // strip off the "XXGET" which may be in the key.
187 std::string::size_type
pos(0);
188 if ((pos
= url
.find("http")) != 0) {
189 if (pos
!= std::string::npos
)
190 url
= url
.substr(pos
);
192 std::string x_original_url
= "X-Original-Url: " + url
+ "\r\n";
193 // we know that the last two bytes are CRLF.
194 headers
.replace(headers
.length() - 2, 0, x_original_url
);
196 data
= headers
.c_str();
197 len
= headers
.size();
198 } else if (index
== 1) {
205 #ifdef WIN32_LARGE_FILENAME_SUPPORT
207 if (!WriteFile(entry_
, data
, len
, &bytes
, 0))
212 return fwrite(data
, 1, len
, entry_
);
216 void DiskDumper::CloseEntry(disk_cache::Entry
* entry
, base::Time last_used
,
217 base::Time last_modified
) {
218 #ifdef WIN32_LARGE_FILENAME_SUPPORT
221 base::CloseFile(entry_
);