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
)
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.
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)
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.
64 // Now create the full path
65 return CreateDirectoryW(path
.value().c_str(), NULL
) == TRUE
;
67 return base::CreateDirectory(path
);
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
);
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
;
110 entry_
= base::OpenFile(entry_path_
, "w+");
111 return (entry_
!= NULL
) ? net::OK
: net::ERR_FAILED
;
115 // Utility Function to create a normalized header string from a
116 // HttpResponseInfo. The output will be formatted exactly
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
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
) {
164 if (index
== 0) { // Stream 0 is the headers.
165 net::HttpResponseInfo response_info
;
167 if (!net::HttpCache::ParseResponseInfo(buf
->data(), buf_len
,
168 &response_info
, &truncated
))
171 // Skip this entry if it was truncated (results in an empty file).
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) {
204 #ifdef WIN32_LARGE_FILENAME_SUPPORT
206 if (!WriteFile(entry_
, data
, len
, &bytes
, 0))
211 return fwrite(data
, 1, len
, entry_
);
215 void DiskDumper::CloseEntry(disk_cache::Entry
* entry
, base::Time last_used
,
216 base::Time last_modified
) {
217 #ifdef WIN32_LARGE_FILENAME_SUPPORT
220 base::CloseFile(entry_
);