summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormbelshe@google.com <mbelshe@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-02 22:05:50 +0000
committermbelshe@google.com <mbelshe@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-02 22:05:50 +0000
commitc44344e2c2ff0adc7b44a5ba6a0ede1905d3a142 (patch)
treed75f73283d8cecda8542b8273a64d9c8a3865ea0
parent596e2e3e43d284bcbc3e4e4b7f444d541954c631 (diff)
downloadchromium_src-c44344e2c2ff0adc7b44a5ba6a0ede1905d3a142.zip
chromium_src-c44344e2c2ff0adc7b44a5ba6a0ede1905d3a142.tar.gz
chromium_src-c44344e2c2ff0adc7b44a5ba6a0ede1905d3a142.tar.bz2
Create an option to dump the cache to disk files. Because URLs are frequently large, this requires
implementing large filename support (for windows), and of course the filenames need to be encoded with filesystem-safe characters. BUG=none TEST=none Review URL: http://codereview.chromium.org/174391 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@25248 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--net/net.gyp3
-rw-r--r--net/tools/dump_cache/cache_dumper.cc190
-rw-r--r--net/tools/dump_cache/cache_dumper.h82
-rw-r--r--net/tools/dump_cache/dump_cache.cc35
-rw-r--r--net/tools/dump_cache/upgrade.cc92
-rw-r--r--net/tools/dump_cache/url_to_filename_encoder.h119
6 files changed, 476 insertions, 45 deletions
diff --git a/net/net.gyp b/net/net.gyp
index da8f4b5..509f9ec 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -732,9 +732,12 @@
'../base/base.gyp:base',
],
'sources': [
+ 'tools/dump_cache/cache_dumper.cc',
+ 'tools/dump_cache/cache_dumper.h',
'tools/dump_cache/dump_cache.cc',
'tools/dump_cache/dump_files.cc',
'tools/dump_cache/upgrade.cc',
+ 'tools/dump_cache/url_to_filename_encoder.h',
],
},
],
diff --git a/net/tools/dump_cache/cache_dumper.cc b/net/tools/dump_cache/cache_dumper.cc
new file mode 100644
index 0000000..29f7be4
--- /dev/null
+++ b/net/tools/dump_cache/cache_dumper.cc
@@ -0,0 +1,190 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/dump_cache/cache_dumper.h"
+
+#include "net/base/io_buffer.h"
+#include "net/disk_cache/entry_impl.h"
+#include "net/http/http_cache.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_response_info.h"
+#include "net/tools/dump_cache/url_to_filename_encoder.h"
+
+bool CacheDumper::CreateEntry(const std::string& key,
+ disk_cache::Entry** entry) {
+ return cache_->CreateEntry(key, entry);
+}
+
+bool CacheDumper::WriteEntry(disk_cache::Entry* entry, int index, int offset,
+ net::IOBuffer* buf, int buf_len) {
+ int written = entry->WriteData(index, offset, buf, buf_len, NULL, false);
+ return written == buf_len;
+}
+
+void CacheDumper::CloseEntry(disk_cache::Entry* entry, base::Time last_used,
+ base::Time last_modified) {
+ if (entry) {
+ static_cast<disk_cache::EntryImpl*>(entry)->SetTimes(last_used,
+ last_modified);
+ entry->Close();
+ }
+}
+
+// A version of CreateDirectory which supports lengthy filenames.
+// Returns true on success, false on failure.
+bool SafeCreateDirectory(const std::wstring& path) {
+#ifdef WIN32_LARGE_FILENAME_SUPPORT
+ // Due to large paths on windows, it can't simply do a
+ // CreateDirectory("a/b/c"). Instead, create each subdirectory manually.
+ bool rv = false;
+ std::wstring::size_type pos(0);
+ std::wstring backslash(L"\\");
+
+ // If the path starts with the long file header, skip over that
+ const std::wstring kLargeFilenamePrefix(L"\\\\?\\");
+ std::wstring header(kLargeFilenamePrefix);
+ if (path.find(header) == 0)
+ pos = 4;
+
+ // Create the subdirectories individually
+ while((pos = path.find(backslash, pos)) != std::wstring::npos) {
+ std::wstring subdir = path.substr(0, pos);
+ CreateDirectoryW(subdir.c_str(), NULL);
+ // we keep going even if directory creation failed.
+ pos++;
+ }
+ // Now create the full path
+ return CreateDirectoryW(path.c_str(), NULL) == TRUE;
+#else
+ return file_util::CreateDirectory(path);
+#endif
+}
+
+bool DiskDumper::CreateEntry(const std::string& key,
+ disk_cache::Entry** entry) {
+ FilePath path(path_);
+ entry_path_ = net::UrlToFilenameEncoder::Encode(key, path);
+
+#ifdef WIN32_LARGE_FILENAME_SUPPORT
+ // In order for long filenames to work, we'll need to prepend
+ // the windows magic token.
+ const std::wstring kLongFilenamePrefix(L"\\\\?\\");
+ entry_path_ = FilePath(kLongFilenamePrefix).Append(entry_path_);
+#endif
+
+ entry_url_ = key;
+
+ FilePath directory = entry_path_.DirName();
+ SafeCreateDirectory(directory.value());
+
+ std::wstring file = entry_path_.value();
+#ifdef WIN32_LARGE_FILENAME_SUPPORT
+ entry_ = CreateFileW(file.c_str(), GENERIC_WRITE|GENERIC_READ, 0, 0,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
+ return entry_ != INVALID_HANDLE_VALUE;
+#else
+ entry_ = file_util::OpenFile(entry_path_, "w+");
+ return entry_ != NULL;
+#endif
+}
+
+// Utility Function to create a normalized header string from a
+// HttpResponseInfo. The output will be formatted exactly
+// like so:
+// HTTP/<version> <status_code> <status_text>\n
+// [<header-name>: <header-values>\n]*
+// meaning, each line is \n-terminated, and there is no extra whitespace
+// beyond the single space separators shown (of course, values can contain
+// whitespace within them). If a given header-name appears more than once
+// in the set of headers, they are combined into a single line like so:
+// <header-name>: <header-value1>, <header-value2>, ...<header-valueN>\n
+//
+// DANGER: For some headers (e.g., "Set-Cookie"), the normalized form can be
+// a lossy format. This is due to the fact that some servers generate
+// Set-Cookie headers that contain unquoted commas (usually as part of the
+// value of an "expires" attribute). So, use this function with caution. Do
+// not expect to be able to re-parse Set-Cookie headers from this output.
+//
+// NOTE: Do not make any assumptions about the encoding of this output
+// string. It may be non-ASCII, and the encoding used by the server is not
+// necessarily known to us. Do not assume that this output is UTF-8!
+void GetNormalizedHeaders(const net::HttpResponseInfo& info,
+ std::string* output) {
+ // Start with the status line
+ output->assign(info.headers->GetStatusLine());
+ output->append("\r\n");
+
+ // Enumerate the headers
+ void* iter = 0;
+ std::string name, value;
+ while (info.headers->EnumerateHeaderLines(&iter, &name, &value)) {
+ output->append(name);
+ output->append(": ");
+ output->append(value);
+ output->append("\r\n");
+ }
+
+ // Mark the end of headers
+ output->append("\r\n");
+}
+
+bool DiskDumper::WriteEntry(disk_cache::Entry* entry, int index, int offset,
+ net::IOBuffer* buf, int buf_len) {
+ if (!entry_)
+ return false;
+
+ std::string headers;
+ const char *data;
+ int len;
+ if (index == 0) { // Stream 0 is the headers.
+ net::HttpResponseInfo response_info;
+ if (!net::HttpCache::ParseResponseInfo(buf->data(), buf_len,
+ &response_info))
+ return false;
+
+ // Remove the size headers.
+ response_info.headers->RemoveHeader("transfer-encoding");
+ response_info.headers->RemoveHeader("content-length");
+ response_info.headers->RemoveHeader("x-original-url");
+
+ // Convert the headers into a string ending with LF.
+ GetNormalizedHeaders(response_info, &headers);
+
+ // Append a header for the original URL.
+ std::string url = entry_url_;
+ // strip off the "XXGET" which may be in the key.
+ std::string::size_type pos(0);
+ if ((pos = url.find("http")) != 0) {
+ if (pos != std::string::npos)
+ url = url.substr(pos);
+ }
+ std::string x_original_url = "X-Original-Url: " + url + "\r\n";
+ // we know that the last two bytes are CRLF.
+ headers.replace(headers.length() - 2, 0, x_original_url);
+
+ data = headers.c_str();
+ len = headers.size();
+ } else if (index == 1) { // Stream 1 is the data.
+ data = buf->data();
+ len = buf_len;
+ }
+#ifdef WIN32_LARGE_FILENAME_SUPPORT
+ DWORD bytes;
+ DWORD rv = WriteFile(entry_, data, len, &bytes, 0);
+ return rv == TRUE && bytes == len;
+#else
+ int bytes = fwrite(data, 1, len, entry_);
+ return bytes == len;
+#endif
+}
+
+void DiskDumper::CloseEntry(disk_cache::Entry* entry, base::Time last_used,
+ base::Time last_modified) {
+#ifdef WIN32_LARGE_FILENAME_SUPPORT
+ CloseHandle(entry_);
+#else
+ file_util::CloseFile(entry_);
+#endif
+}
+
diff --git a/net/tools/dump_cache/cache_dumper.h b/net/tools/dump_cache/cache_dumper.h
new file mode 100644
index 0000000..8a11269
--- /dev/null
+++ b/net/tools/dump_cache/cache_dumper.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_DUMP_CACHE_DUMPER_H_
+#define NET_TOOLS_DUMP_CACHE_DUMPER_H_
+
+#include <string>
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "net/disk_cache/backend_impl.h"
+
+#ifdef WIN32
+// Dumping the cache often creates very large filenames, which are tricky
+// on windows. Most API calls don't support large filenames, including
+// most of the base library functions. Unfortunately, adding "\\?\" into
+// the filename support is tricky. Instead, if WIN32_LARGE_FILENAME_SUPPORT
+// is set, we use direct WIN32 APIs for manipulating the files.
+#define WIN32_LARGE_FILENAME_SUPPORT
+#endif
+
+// An abstract class for writing cache dump data.
+class CacheDumpWriter {
+ public:
+ // Creates an entry to be written.
+ // On success, populates the |entry|.
+ // Returns true on success, false otherwise.
+ virtual bool CreateEntry(const std::string& key,
+ disk_cache::Entry** entry) = 0;
+
+ // Write to the current entry.
+ // Returns true on success, false otherwise.
+ virtual bool WriteEntry(disk_cache::Entry* entry, int stream, int offset,
+ net::IOBuffer* buf, int buf_len) = 0;
+
+ // Close the current entry.
+ virtual void CloseEntry(disk_cache::Entry* entry, base::Time last_used,
+ base::Time last_modified) = 0;
+};
+
+// Writes data to a cache.
+class CacheDumper : public CacheDumpWriter {
+ public:
+ CacheDumper(disk_cache::BackendImpl* cache) : cache_(cache) {};
+
+ virtual bool CreateEntry(const std::string& key, disk_cache::Entry** entry);
+ virtual bool WriteEntry(disk_cache::Entry* entry, int stream, int offset,
+ net::IOBuffer* buf, int buf_len);
+ virtual void CloseEntry(disk_cache::Entry* entry, base::Time last_used,
+ base::Time last_modified);
+
+ private:
+ disk_cache::BackendImpl* cache_;
+};
+
+// Writes data to a disk.
+class DiskDumper : public CacheDumpWriter {
+ public:
+ DiskDumper(const std::wstring& path) : path_(path), entry_(NULL) {
+ file_util::CreateDirectory(path);
+ };
+ virtual bool CreateEntry(const std::string& key, disk_cache::Entry** entry);
+ virtual bool WriteEntry(disk_cache::Entry* entry, int stream, int offset,
+ net::IOBuffer* buf, int buf_len);
+ virtual void CloseEntry(disk_cache::Entry* entry, base::Time last_used,
+ base::Time last_modified);
+
+ private:
+ std::wstring path_;
+ // This is a bit of a hack. As we get a CreateEntry, we coin the current
+ // entry_path_ where we write that entry to disk. Subsequent calls to
+ // WriteEntry() utilize this path for writing to disk.
+ FilePath entry_path_;
+ std::string entry_url_;
+#ifdef WIN32_LARGE_FILENAME_SUPPORT
+ HANDLE entry_;
+#else
+ FILE* entry_;
+#endif
+};
+
+#endif // NET_TOOLS_DUMP_CACHE_DUMPER_H_
diff --git a/net/tools/dump_cache/dump_cache.cc b/net/tools/dump_cache/dump_cache.cc
index f14c7a9..748bf0f 100644
--- a/net/tools/dump_cache/dump_cache.cc
+++ b/net/tools/dump_cache/dump_cache.cc
@@ -29,7 +29,7 @@ int GetMajorVersion(const std::wstring& input_path);
int DumpContents(const std::wstring& input_path);
int DumpHeaders(const std::wstring& input_path);
int RunSlave(const std::wstring& input_path, const std::wstring& pipe_number);
-int Upgrade(const std::wstring& output_path, HANDLE pipe);
+int CopyCache(const std::wstring& output_path, HANDLE pipe, bool copy_to_text);
HANDLE CreateServer(std::wstring* pipe_number);
const char kUpgradeHelp[] =
@@ -50,6 +50,9 @@ const wchar_t kDumpHeaders[] = L"dump-headers";
// Dumps all entries to stdout.
const wchar_t kDumpContents[] = L"dump-contents";
+// Convert the cache to files.
+const wchar_t kDumpToFiles[] = L"dump-to-files";
+
// Upgrade an old version to the current one.
const wchar_t kUpgrade[] = L"upgrade";
@@ -63,6 +66,7 @@ int Help() {
printf("--dump-headers: display file headers\n");
printf("--dump-contents: display all entries\n");
printf("--upgrade: copy contents to the output path\n");
+ printf("--dump-to-files: write the contents of the cache to files\n");
return INVALID_ARGUMENT;
}
@@ -75,14 +79,21 @@ int LaunchSlave(const CommandLine& command_line,
size_t to_remove = hacked_command_line.find(old_exe);
hacked_command_line.erase(to_remove, old_exe.size());
- std::wstring new_program = StringPrintf(L"%ls%d.exe", L"dump_cache_",
- version);
+ bool do_upgrade = command_line.HasSwitch(kUpgrade);
+ bool do_convert_to_text = command_line.HasSwitch(kDumpToFiles);
+
+ std::wstring new_program;
+ if (do_upgrade)
+ new_program = StringPrintf(L"%ls%d.exe", L"dump_cache_", version);
+ else
+ new_program = StringPrintf(L"dump_cache.exe");
+
hacked_command_line.insert(to_remove, new_program);
CommandLine new_command_line(L"");
new_command_line.ParseFromString(hacked_command_line);
- if (command_line.HasSwitch(kUpgrade))
+ if (do_upgrade || do_convert_to_text)
new_command_line.AppendSwitch(kSlave);
new_command_line.AppendSwitchWithValue(kPipe, pipe_number);
@@ -110,13 +121,17 @@ int main(int argc, const char* argv[]) {
bool upgrade = false;
bool slave_required = false;
- std::wstring output_path;
- if (command_line.HasSwitch(kUpgrade)) {
- output_path = command_line.GetSwitchValue(kOutputPath);
+ bool copy_to_text = false;
+ std::wstring output_path = command_line.GetSwitchValue(kOutputPath);
+ if (command_line.HasSwitch(kUpgrade))
+ upgrade = true;
+ if (command_line.HasSwitch(kDumpToFiles))
+ copy_to_text = true;
+
+ if (upgrade || copy_to_text) {
if (output_path.empty())
return Help();
slave_required = true;
- upgrade = true;
}
int version = GetMajorVersion(input_path);
@@ -148,8 +163,8 @@ int main(int argc, const char* argv[]) {
return ret;
}
- if (upgrade)
- return Upgrade(output_path, server);
+ if (upgrade || copy_to_text)
+ return CopyCache(output_path, server, copy_to_text);
if (slave_required) {
// Wait until the slave starts dumping data before we quit. Lazy "fix" for a
diff --git a/net/tools/dump_cache/upgrade.cc b/net/tools/dump_cache/upgrade.cc
index bf27c9d..de0f878 100644
--- a/net/tools/dump_cache/upgrade.cc
+++ b/net/tools/dump_cache/upgrade.cc
@@ -4,10 +4,16 @@
#include "base/logging.h"
#include "base/message_loop.h"
+#include "base/scoped_ptr.h"
#include "base/string_util.h"
+#include "googleurl/src/gurl.h"
#include "net/base/io_buffer.h"
#include "net/disk_cache/backend_impl.h"
#include "net/disk_cache/entry_impl.h"
+#include "net/http/http_cache.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_response_info.h"
+#include "net/tools/dump_cache/cache_dumper.h"
namespace {
@@ -102,7 +108,7 @@ enum {
class BaseSM : public MessageLoopForIO::IOHandler {
public:
- BaseSM(disk_cache::BackendImpl* cache, HANDLE channel);
+ BaseSM(HANDLE channel);
virtual ~BaseSM();
protected:
@@ -113,7 +119,6 @@ class BaseSM : public MessageLoopForIO::IOHandler {
MessageLoopForIO::IOContext in_context_;
MessageLoopForIO::IOContext out_context_;
- disk_cache::BackendImpl* cache_;
disk_cache::EntryImpl* entry_;
HANDLE channel_;
int state_;
@@ -125,9 +130,8 @@ class BaseSM : public MessageLoopForIO::IOHandler {
DISALLOW_COPY_AND_ASSIGN(BaseSM);
};
-BaseSM::BaseSM(disk_cache::BackendImpl* cache, HANDLE channel)
- : cache_(cache), entry_(NULL), channel_(channel), state_(0),
- pending_count_(0) {
+BaseSM::BaseSM(HANDLE channel)
+ : entry_(NULL), channel_(channel), state_(0), pending_count_(0) {
in_buffer_.reset(new char[kChannelSize]);
out_buffer_.reset(new char[kChannelSize]);
input_ = reinterpret_cast<IoBuffer*>(in_buffer_.get());
@@ -195,9 +199,12 @@ bool BaseSM::IsPending() {
class MasterSM : public BaseSM {
public:
- MasterSM(disk_cache::BackendImpl* cache, HANDLE channel)
- : BaseSM(cache, channel) {}
- virtual ~MasterSM() {}
+ MasterSM(const std::wstring& path, HANDLE channel, bool dump_to_disk)
+ : BaseSM(channel), path_(path), dump_to_disk_(dump_to_disk) {
+ }
+ virtual ~MasterSM() {
+ delete writer_;
+ }
bool DoInit();
virtual void OnIOCompleted(MessageLoopForIO::IOContext* context,
@@ -236,6 +243,10 @@ class MasterSM : public BaseSM {
int bytes_remaining_;
int offset_;
int copied_entries_;
+ scoped_ptr<disk_cache::BackendImpl> cache_;
+ CacheDumpWriter* writer_;
+ const std::wstring& path_;
+ bool dump_to_disk_;
};
void MasterSM::OnIOCompleted(MessageLoopForIO::IOContext* context,
@@ -287,6 +298,19 @@ bool MasterSM::DoInit() {
DEBUGMSG("Master DoInit\n");
DCHECK(state_ == MASTER_INITIAL);
+ if (dump_to_disk_) {
+ writer_ = new DiskDumper(path_);
+ } else {
+ cache_.reset(new disk_cache::BackendImpl(path_));
+ if (!cache_->Init()) {
+ printf("Unable to initialize new files\n");
+ return false;
+ }
+ writer_ = new CacheDumper(cache_.get());
+ }
+ if (!writer_)
+ return false;
+
copied_entries_ = 0;
remote_entry_ = 0;
@@ -343,17 +367,20 @@ void MasterSM::DoGetKey(int bytes_read) {
std::string key(input_->buffer);
DCHECK(key.size() == input_->msg.buffer_bytes - 1);
- if (!cache_->CreateEntry(key,
- reinterpret_cast<disk_cache::Entry**>(&entry_))) {
+
+ if (!writer_->CreateEntry(key,
+ reinterpret_cast<disk_cache::Entry**>(&entry_))) {
printf("Skipping entry \"%s\" (name conflict!)\n", key.c_str());
return SendGetPrevEntry();
}
- if (key.size() < 60) {
- DEBUGMSG("Entry \"%s\" created\n", key.c_str());
- } else {
- DEBUGMSG("Entry (long name) created\n", key.c_str());
+ if (key.size() >= 64) {
+ key[60] = '.';
+ key[61] = '.';
+ key[62] = '.';
+ key[63] = '\0';
}
+ DEBUGMSG("Entry \"%s\" created\n", key.c_str());
state_ = MASTER_GET_USE_TIMES;
Message msg;
msg.command = GET_USE_TIMES;
@@ -403,8 +430,7 @@ void MasterSM::DoGetDataSize() {
void MasterSM::CloseEntry() {
DEBUGMSG("Master CloseEntry\n");
printf("%c\r", copied_entries_ % 2 ? 'x' : '+');
- entry_->SetTimes(last_used_, last_modified_);
- entry_->Close();
+ writer_->CloseEntry(entry_, last_used_, last_modified_);
entry_ = NULL;
copied_entries_++;
SendGetPrevEntry();
@@ -447,8 +473,7 @@ void MasterSM::DoReadData(int bytes_read) {
scoped_refptr<net::WrappedIOBuffer> buf =
new net::WrappedIOBuffer(input_->buffer);
- if (read_size != entry_->WriteData(stream_, offset_, buf, read_size, NULL,
- false))
+ if (!writer_->WriteEntry(entry_, stream_, offset_, buf, read_size))
return Fail();
offset_ += read_size;
@@ -482,8 +507,15 @@ void MasterSM::Fail() {
class SlaveSM : public BaseSM {
public:
- SlaveSM(disk_cache::BackendImpl* cache, HANDLE channel)
- : BaseSM(cache, channel), iterator_(NULL) {}
+ SlaveSM(const std::wstring& path, HANDLE channel)
+ : BaseSM(channel), iterator_(NULL) {
+ cache_.reset(new disk_cache::BackendImpl(path));
+ if (!cache_->Init()) {
+ printf("Unable to open cache files\n");
+ return;
+ }
+ cache_->SetUpgradeMode();
+ }
virtual ~SlaveSM();
bool DoInit();
@@ -509,6 +541,8 @@ class SlaveSM : public BaseSM {
void Fail();
void* iterator_;
+
+ scoped_ptr<disk_cache::BackendImpl> cache_;
};
SlaveSM::~SlaveSM() {
@@ -760,15 +794,10 @@ HANDLE CreateServer(std::wstring* pipe_number) {
}
// This is the controller process for an upgrade operation.
-int Upgrade(const std::wstring& output_path, HANDLE pipe) {
+int CopyCache(const std::wstring& output_path, HANDLE pipe, bool copy_to_text) {
MessageLoop loop(MessageLoop::TYPE_IO);
- disk_cache::BackendImpl cache(output_path);
- if (!cache.Init()) {
- printf("Unable to initialize new files\n");
- return -1;
- }
- MasterSM master(&cache, pipe);
+ MasterSM master(output_path, pipe, copy_to_text);
if (!master.DoInit()) {
printf("Unable to talk with the helper\n");
return -1;
@@ -788,14 +817,7 @@ int RunSlave(const std::wstring& input_path, const std::wstring& pipe_number) {
return -1;
}
- disk_cache::BackendImpl cache(input_path);
- if (!cache.Init()) {
- printf("Unable to open cache files\n");
- return -1;
- }
- cache.SetUpgradeMode();
-
- SlaveSM slave(&cache, pipe);
+ SlaveSM slave(input_path, pipe);
if (!slave.DoInit()) {
printf("Unable to talk with the main process\n");
return -1;
diff --git a/net/tools/dump_cache/url_to_filename_encoder.h b/net/tools/dump_cache/url_to_filename_encoder.h
new file mode 100644
index 0000000..e71d7a0
--- /dev/null
+++ b/net/tools/dump_cache/url_to_filename_encoder.h
@@ -0,0 +1,119 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_TOOLS_DUMP_CACHE_URL_TO_FILE_ENCODER_H_
+#define NET_TOOLS_DUMP_CACHE_URL_TO_FILE_ENCODER_H_
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/string_util.h"
+#include "googleurl/src/gurl.h"
+
+namespace net {
+
+// Helper class for converting a URL into a filename.
+class UrlToFilenameEncoder {
+ public:
+ // Given a |url| and a |base_path|, returns a FilePath which represents this
+ // |url|.
+ static FilePath Encode(const std::string& url, FilePath base_path) {
+ std::string clean_url(url);
+ if (clean_url.length() && clean_url[clean_url.length()-1] == '/')
+ clean_url.append("index.html");
+
+ GURL gurl(clean_url);
+ FilePath filename(base_path);
+ filename = filename.Append(ASCIIToWide(gurl.host()));
+
+ std::wstring url_filename = ASCIIToWide(gurl.PathForRequest());
+ // Strip the leading '/'
+ if (url_filename[0] == L'/')
+ url_filename = url_filename.substr(1);
+
+ // replace '/' with '\'
+ ConvertToSlashes(url_filename);
+
+ // strip double slashes ("\\")
+ StripDoubleSlashes(url_filename);
+
+ // Save path as filesystem-safe characters
+ url_filename = Escape(url_filename);
+ filename = filename.Append(url_filename);
+
+ return filename;
+ }
+
+ private:
+ // This is the length at which we chop individual subdirectories.
+ // Technically, we shouldn't need to do this, but I found that
+ // even with long-filename support, windows had trouble creating
+ // long subdirectories, and making them shorter helps.
+ static const int kMaximumSubdirectoryLength = 128;
+
+ // Escape the given input |path| and chop any individual components
+ // of the path which are greater than kMaximumSubdirectoryLength characters
+ // into two chunks.
+ static std::wstring Escape(const std::wstring& path) {
+ std::wstring output;
+ int last_slash = 0;
+ for (size_t index = 0; index < path.length(); index++) {
+ wchar_t wide_char = path[index];
+ DCHECK((wide_char & 0xff) == wide_char);
+ char ch = static_cast<char>(wide_char & 0xff);
+ if (ch == 0x5C)
+ last_slash = index;
+ if ((ch == 0x2D) || // hyphen
+ (ch == 0x5C) || (ch == 0x5F) || // backslash, underscore
+ ((0x30 <= ch) && (ch <= 0x39)) || // Digits [0-9]
+ ((0x41 <= ch) && (ch <= 0x5A)) || // Uppercase [A-Z]
+ ((0x61 <= ch) && (ch <= 0x7A))) { // Lowercase [a-z]
+ output.append(&path[index],1);
+ } else {
+ wchar_t encoded[3];
+ encoded[0] = L'x';
+ encoded[1] = ch / 16;
+ encoded[1] += (encoded[1] >= 10) ? 'A' - 10 : '0';
+ encoded[2] = ch % 16;
+ encoded[2] += (encoded[2] >= 10) ? 'A' - 10 : '0';
+ output.append(encoded, 3);
+ }
+ if (index - last_slash > kMaximumSubdirectoryLength) {
+ wchar_t backslash = L'\\';
+ output.append(&backslash, 1);
+ last_slash = index;
+ }
+ }
+ return output;
+ }
+
+ // Replace all instances of |from| within |str| as |to|.
+ static void ReplaceAll(std::wstring& str, const std::wstring& from,
+ const std::wstring& to) {
+ std::wstring::size_type pos(0);
+ while((pos = str.find(from, pos)) != std::wstring::npos) {
+ str.replace(pos, from.size(), to);
+ pos += from.size();
+ }
+ }
+
+ // Replace all instances of "/" with "\" in |path|.
+ static void ConvertToSlashes(std::wstring& path) {
+ std::wstring slash(L"/");
+ std::wstring backslash(L"\\");
+ ReplaceAll(path, slash, backslash);
+ }
+
+ // Replace all instances of "\\" with "%5C%5C" in |path|.
+ static void StripDoubleSlashes(std::wstring& path) {
+ std::wstring::size_type pos(0);
+ std::wstring doubleslash(L"\\\\");
+ std::wstring escaped_doubleslash(L"%5C%5C");
+ ReplaceAll(path, doubleslash, escaped_doubleslash);
+ }
+};
+
+} // namespace net
+
+#endif // NET_TOOLS_DUMP_CACHE_URL_TO_FILE_ENCODER_H__
+