summaryrefslogtreecommitdiffstats
path: root/chrome/browser/net
diff options
context:
space:
mode:
authoreroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-04 06:45:43 +0000
committereroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-04 06:45:43 +0000
commit4e600d6a8369367459477f9845f86d4fce16f69e (patch)
tree5e01487537a1fbf7c3cae46c23d201d3e58332cf /chrome/browser/net
parentb0544c44da65ac1a2b0654f97ec473c76176aea2 (diff)
downloadchromium_src-4e600d6a8369367459477f9845f86d4fce16f69e.zip
chromium_src-4e600d6a8369367459477f9845f86d4fce16f69e.tar.gz
chromium_src-4e600d6a8369367459477f9845f86d4fce16f69e.tar.bz2
Refactor: Move the code that handles "chrome://net-internals/*" from the net module to the chrome module.
This is in preparation for making a javascript frontend to it, and merging in the functionality from about:network. There is no behavior change here, just moving code around. Review URL: http://codereview.chromium.org/668039 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40608 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/net')
-rw-r--r--chrome/browser/net/view_net_internals_job_factory.cc822
-rw-r--r--chrome/browser/net/view_net_internals_job_factory.h18
2 files changed, 840 insertions, 0 deletions
diff --git a/chrome/browser/net/view_net_internals_job_factory.cc b/chrome/browser/net/view_net_internals_job_factory.cc
new file mode 100644
index 0000000..ab58dfcf
--- /dev/null
+++ b/chrome/browser/net/view_net_internals_job_factory.cc
@@ -0,0 +1,822 @@
+// Copyright (c) 2010 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 "chrome/browser/net/view_net_internals_job_factory.h"
+
+#include <sstream>
+
+#include "base/format_macros.h"
+#include "base/stl_util-inl.h"
+#include "base/string_util.h"
+#include "chrome/common/url_constants.h"
+#include "net/base/escape.h"
+#include "net/base/host_resolver_impl.h"
+#include "net/base/load_log_util.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+#include "net/base/sys_addrinfo.h"
+#include "net/proxy/proxy_service.h"
+#include "net/socket_stream/socket_stream.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_simple_job.h"
+#include "net/url_request/view_cache_helper.h"
+
+namespace {
+
+const char kViewHttpCacheSubPath[] = "view-cache";
+
+std::string GetDetails(const GURL& url) {
+ DCHECK(ViewNetInternalsJobFactory::IsSupportedURL(url));
+ size_t start = strlen(chrome::kNetworkViewInternalsURL);
+ if (start >= url.spec().size())
+ return std::string();
+ return url.spec().substr(start);
+}
+
+GURL MakeURL(const std::string& details) {
+ return GURL(std::string(chrome::kNetworkViewInternalsURL) + details);
+}
+
+// A job subclass that implements a protocol to inspect the internal
+// state of the network stack.
+class ViewNetInternalsJob : public URLRequestSimpleJob {
+ public:
+
+ explicit ViewNetInternalsJob(URLRequest* request)
+ : URLRequestSimpleJob(request) {}
+
+ // URLRequestSimpleJob methods:
+ virtual bool GetData(std::string* mime_type,
+ std::string* charset,
+ std::string* data) const;
+
+ // Overridden methods from URLRequestJob:
+ virtual bool IsRedirectResponse(GURL* location, int* http_status_code);
+
+ private:
+ ~ViewNetInternalsJob() {}
+
+ // Returns true if the current request is for a "view-cache" URL.
+ // If it is, then |key| is assigned the particular cache URL of the request.
+ bool GetViewCacheKeyForRequest(std::string* key) const;
+};
+
+//------------------------------------------------------------------------------
+// Format helpers.
+//------------------------------------------------------------------------------
+
+void OutputTextInPre(const std::string& text, std::string* out) {
+ out->append("<pre>");
+ out->append(EscapeForHTML(text));
+ out->append("</pre>");
+}
+
+// Appends an input button to |data| with text |title| that sends the command
+// string |command| back to the browser, and then refreshes the page.
+void DrawCommandButton(const std::string& title,
+ const std::string& command,
+ std::string* data) {
+ StringAppendF(data, "<input type=\"button\" value=\"%s\" "
+ "onclick=\"DoCommand('%s')\" />",
+ title.c_str(),
+ command.c_str());
+}
+
+//------------------------------------------------------------------------------
+// URLRequestContext helpers.
+//------------------------------------------------------------------------------
+
+net::HostResolverImpl* GetHostResolverImpl(URLRequestContext* context) {
+ return context->host_resolver()->GetAsHostResolverImpl();
+}
+
+net::HostCache* GetHostCache(URLRequestContext* context) {
+ if (GetHostResolverImpl(context))
+ return GetHostResolverImpl(context)->cache();
+ return NULL;
+}
+
+//------------------------------------------------------------------------------
+// Subsection definitions.
+//------------------------------------------------------------------------------
+
+class SubSection {
+ public:
+ // |name| is the URL path component for this subsection.
+ // |title| is the textual description.
+ SubSection(SubSection* parent, const char* name, const char* title)
+ : parent_(parent),
+ name_(name),
+ title_(title) {
+ }
+
+ virtual ~SubSection() {
+ STLDeleteContainerPointers(children_.begin(), children_.end());
+ }
+
+ // Outputs the subsection's contents to |out|.
+ virtual void OutputBody(URLRequestContext* context, std::string* out) {}
+
+ // Outputs this subsection, and all of its children.
+ void OutputRecursive(URLRequestContext* context, std::string* out) {
+ if (!is_root()) {
+ // Canonicalizing the URL escapes characters which cause problems in HTML.
+ std::string section_url = MakeURL(GetFullyQualifiedName()).spec();
+
+ // Print the heading.
+ StringAppendF(
+ out,
+ "<div>"
+ "<span class=subsection_title>%s</span> "
+ "<span class=subsection_name>(<a href='%s'>%s</a>)<span>"
+ "</div>",
+ EscapeForHTML(title_).c_str(),
+ section_url.c_str(),
+ EscapeForHTML(section_url).c_str());
+
+ out->append("<div class=subsection_body>");
+ }
+
+ OutputBody(context, out);
+
+ for (size_t i = 0; i < children_.size(); ++i)
+ children_[i]->OutputRecursive(context, out);
+
+ if (!is_root())
+ out->append("</div>");
+ }
+
+ // Returns the SubSection contained by |this| with fully qualified name
+ // |dotted_name|, or NULL if none was found.
+ SubSection* FindSubSectionByName(const std::string& dotted_name) {
+ if (dotted_name == "")
+ return this;
+
+ std::string child_name;
+ std::string child_sub_name;
+
+ size_t split_pos = dotted_name.find('.');
+ if (split_pos == std::string::npos) {
+ child_name = dotted_name;
+ child_sub_name = std::string();
+ } else {
+ child_name = dotted_name.substr(0, split_pos);
+ child_sub_name = dotted_name.substr(split_pos + 1);
+ }
+
+ for (size_t i = 0; i < children_.size(); ++i) {
+ if (children_[i]->name_ == child_name)
+ return children_[i]->FindSubSectionByName(child_sub_name);
+ }
+
+ return NULL; // Not found.
+ }
+
+ std::string GetFullyQualifiedName() {
+ if (!parent_)
+ return name_;
+
+ std::string parent_name = parent_->GetFullyQualifiedName();
+ if (parent_name.empty())
+ return name_;
+
+ return parent_name + "." + name_;
+ }
+
+ bool is_root() const {
+ return parent_ == NULL;
+ }
+
+ protected:
+ typedef std::vector<SubSection*> SubSectionList;
+
+ void AddSubSection(SubSection* subsection) {
+ children_.push_back(subsection);
+ }
+
+ SubSection* parent_;
+ std::string name_;
+ std::string title_;
+
+ SubSectionList children_;
+};
+
+class ProxyServiceCurrentConfigSubSection : public SubSection {
+ public:
+ explicit ProxyServiceCurrentConfigSubSection(SubSection* parent)
+ : SubSection(parent, "config", "Current configuration") {
+ }
+
+ virtual void OutputBody(URLRequestContext* context, std::string* out) {
+ DrawCommandButton("Force reload", "reload-proxy-config", out);
+
+ net::ProxyService* proxy_service = context->proxy_service();
+ if (proxy_service->config_has_been_initialized()) {
+ // net::ProxyConfig defines an operator<<.
+ std::ostringstream stream;
+ stream << proxy_service->config();
+ OutputTextInPre(stream.str(), out);
+ } else {
+ out->append("<i>Not yet initialized</i>");
+ }
+ }
+};
+
+class ProxyServiceLastInitLogSubSection : public SubSection {
+ public:
+ explicit ProxyServiceLastInitLogSubSection(SubSection* parent)
+ : SubSection(parent, "init_log", "Last initialized load log") {
+ }
+
+ virtual void OutputBody(URLRequestContext* context, std::string* out) {
+ net::ProxyService* proxy_service = context->proxy_service();
+ net::LoadLog* log = proxy_service->init_proxy_resolver_log();
+ if (log) {
+ OutputTextInPre(net::LoadLogUtil::PrettyPrintAsEventTree(log), out);
+ } else {
+ out->append("<i>None.</i>");
+ }
+ }
+};
+
+class ProxyServiceBadProxiesSubSection : public SubSection {
+ public:
+ explicit ProxyServiceBadProxiesSubSection(SubSection* parent)
+ : SubSection(parent, "bad_proxies", "Bad Proxies") {
+ }
+
+ virtual void OutputBody(URLRequestContext* context, std::string* out) {
+ net::ProxyService* proxy_service = context->proxy_service();
+ const net::ProxyRetryInfoMap& bad_proxies_map =
+ proxy_service->proxy_retry_info();
+
+ DrawCommandButton("Clear", "clear-badproxies", out);
+
+ out->append("<table border=1>");
+ out->append("<tr><th>Bad proxy server</th>"
+ "<th>Remaining time until retry (ms)</th></tr>");
+
+ for (net::ProxyRetryInfoMap::const_iterator it = bad_proxies_map.begin();
+ it != bad_proxies_map.end(); ++it) {
+ const std::string& proxy_uri = it->first;
+ const net::ProxyRetryInfo& retry_info = it->second;
+
+ // Note that ttl_ms may be negative, for the cases where entries have
+ // expired but not been garbage collected yet.
+ int ttl_ms = static_cast<int>(
+ (retry_info.bad_until - base::TimeTicks::Now()).InMilliseconds());
+
+ // Color expired entries blue.
+ if (ttl_ms > 0)
+ out->append("<tr>");
+ else
+ out->append("<tr style='color:blue'>");
+
+ StringAppendF(out, "<td>%s</td><td>%d</td>",
+ EscapeForHTML(proxy_uri).c_str(),
+ ttl_ms);
+
+ out->append("</tr>");
+ }
+ out->append("</table>");
+ }
+};
+
+class ProxyServiceSubSection : public SubSection {
+ public:
+ explicit ProxyServiceSubSection(SubSection* parent)
+ : SubSection(parent, "proxyservice", "ProxyService") {
+ AddSubSection(new ProxyServiceCurrentConfigSubSection(this));
+ AddSubSection(new ProxyServiceLastInitLogSubSection(this));
+ AddSubSection(new ProxyServiceBadProxiesSubSection(this));
+ }
+};
+
+class HostResolverCacheSubSection : public SubSection {
+ public:
+ explicit HostResolverCacheSubSection(SubSection* parent)
+ : SubSection(parent, "hostcache", "HostCache") {
+ }
+
+ virtual void OutputBody(URLRequestContext* context, std::string* out) {
+ const net::HostCache* host_cache = GetHostCache(context);
+
+ if (!host_cache || host_cache->caching_is_disabled()) {
+ out->append("<i>Caching is disabled.</i>");
+ return;
+ }
+
+ DrawCommandButton("Clear", "clear-hostcache", out);
+
+ StringAppendF(
+ out,
+ "<ul><li>Size: %" PRIuS "</li>"
+ "<li>Capacity: %" PRIuS "</li>"
+ "<li>Time to live (ms) for success entries: %" PRId64"</li>"
+ "<li>Time to live (ms) for failure entries: %" PRId64"</li></ul>",
+ host_cache->size(),
+ host_cache->max_entries(),
+ host_cache->success_entry_ttl().InMilliseconds(),
+ host_cache->failure_entry_ttl().InMilliseconds());
+
+ out->append("<table border=1>"
+ "<tr>"
+ "<th>Host</th>"
+ "<th>Address family</th>"
+ "<th>Address list</th>"
+ "<th>Time to live (ms)</th>"
+ "</tr>");
+
+ for (net::HostCache::EntryMap::const_iterator it =
+ host_cache->entries().begin();
+ it != host_cache->entries().end();
+ ++it) {
+ const net::HostCache::Key& key = it->first;
+ const net::HostCache::Entry* entry = it->second.get();
+
+ std::string address_family_str =
+ AddressFamilyToString(key.address_family);
+
+ // Note that ttl_ms may be negative, for the cases where entries have
+ // expired but not been garbage collected yet.
+ int ttl_ms = static_cast<int>(
+ (entry->expiration - base::TimeTicks::Now()).InMilliseconds());
+
+ // Color expired entries blue.
+ if (ttl_ms > 0) {
+ out->append("<tr>");
+ } else {
+ out->append("<tr style='color:blue'>");
+ }
+
+ // Stringify all of the addresses in the address list, separated
+ // by newlines (br).
+ std::string address_list_html;
+
+ if (entry->error != net::OK) {
+ address_list_html = "<span style='font-weight: bold; color:red'>" +
+ EscapeForHTML(net::ErrorToString(entry->error)) +
+ "</span>";
+ } else {
+ const struct addrinfo* current_address = entry->addrlist.head();
+ while (current_address) {
+ if (!address_list_html.empty())
+ address_list_html += "<br>";
+ address_list_html += EscapeForHTML(
+ net::NetAddressToString(current_address));
+ current_address = current_address->ai_next;
+ }
+ }
+
+ StringAppendF(out,
+ "<td>%s</td><td>%s</td><td>%s</td>"
+ "<td>%d</td></tr>",
+ EscapeForHTML(key.hostname).c_str(),
+ EscapeForHTML(address_family_str).c_str(),
+ address_list_html.c_str(),
+ ttl_ms);
+ }
+
+ out->append("</table>");
+ }
+
+ static std::string AddressFamilyToString(net::AddressFamily address_family) {
+ switch (address_family) {
+ case net::ADDRESS_FAMILY_IPV4:
+ return "IPV4";
+ case net::ADDRESS_FAMILY_IPV6:
+ return "IPV6";
+ case net::ADDRESS_FAMILY_UNSPECIFIED:
+ return "UNSPECIFIED";
+ default:
+ NOTREACHED();
+ return "???";
+ }
+ }
+};
+
+class HostResolverTraceSubSection : public SubSection {
+ public:
+ explicit HostResolverTraceSubSection(SubSection* parent)
+ : SubSection(parent, "trace", "Trace of requests") {
+ }
+
+ virtual void OutputBody(URLRequestContext* context, std::string* out) {
+ net::HostResolverImpl* resolver = GetHostResolverImpl(context);
+ if (!resolver) {
+ out->append("<i>Tracing is not supported by this resolver.</i>");
+ return;
+ }
+
+ DrawCommandButton("Clear", "clear-hostresolver-trace", out);
+
+ if (resolver->IsRequestsTracingEnabled()) {
+ DrawCommandButton("Disable tracing", "hostresolver-trace-disable", out);
+ } else {
+ DrawCommandButton("Enable tracing", "hostresolver-trace-enable", out);
+ }
+
+ scoped_refptr<net::LoadLog> log = resolver->GetRequestsTrace();
+
+ if (log) {
+ out->append(
+ "<p>To make sense of this trace, process it with the Python script "
+ "formatter.py at "
+ "<a href='http://src.chromium.org/viewvc/chrome/trunk/src/net/tools/"
+ "dns_trace_formatter/'>net/tools/dns_trace_formatter</a></p>");
+ OutputTextInPre(net::LoadLogUtil::PrettyPrintAsEventTree(log), out);
+ } else {
+ out->append("<p><i>No trace information, must enable tracing.</i></p>");
+ }
+ }
+};
+
+class HostResolverSubSection : public SubSection {
+ public:
+ explicit HostResolverSubSection(SubSection* parent)
+ : SubSection(parent, "hostresolver", "HostResolver") {
+ AddSubSection(new HostResolverCacheSubSection(this));
+ AddSubSection(new HostResolverTraceSubSection(this));
+ }
+};
+
+// Helper for the URLRequest "outstanding" and "live" sections.
+void OutputURLAndLoadLog(const GURL& url,
+ const net::LoadLog* log,
+ std::string* out) {
+ out->append("<li>");
+ out->append("<nobr>");
+ out->append(EscapeForHTML(url.possibly_invalid_spec()));
+ out->append("</nobr>");
+ if (log)
+ OutputTextInPre(net::LoadLogUtil::PrettyPrintAsEventTree(log), out);
+ out->append("</li>");
+}
+
+class URLRequestLiveSubSection : public SubSection {
+ public:
+ explicit URLRequestLiveSubSection(SubSection* parent)
+ : SubSection(parent, "outstanding", "Outstanding requests") {
+ }
+
+ virtual void OutputBody(URLRequestContext* context, std::string* out) {
+ std::vector<URLRequest*> requests =
+ context->url_request_tracker()->GetLiveRequests();
+
+ out->append("<ol>");
+ for (size_t i = 0; i < requests.size(); ++i) {
+ // Reverse the list order, so we dispay from most recent to oldest.
+ size_t index = requests.size() - i - 1;
+ OutputURLAndLoadLog(requests[index]->original_url(),
+ requests[index]->load_log(),
+ out);
+ }
+ out->append("</ol>");
+ }
+};
+
+class URLRequestRecentSubSection : public SubSection {
+ public:
+ explicit URLRequestRecentSubSection(SubSection* parent)
+ : SubSection(parent, "recent", "Recently completed requests") {
+ }
+
+ virtual void OutputBody(URLRequestContext* context, std::string* out) {
+ RequestTracker<URLRequest>::RecentRequestInfoList recent =
+ context->url_request_tracker()->GetRecentlyDeceased();
+
+ DrawCommandButton("Clear", "clear-urlrequest-graveyard", out);
+
+ out->append("<ol>");
+ for (size_t i = 0; i < recent.size(); ++i) {
+ // Reverse the list order, so we dispay from most recent to oldest.
+ size_t index = recent.size() - i - 1;
+ OutputURLAndLoadLog(recent[index].original_url,
+ recent[index].load_log, out);
+ }
+ out->append("</ol>");
+ }
+};
+
+class URLRequestSubSection : public SubSection {
+ public:
+ explicit URLRequestSubSection(SubSection* parent)
+ : SubSection(parent, "urlrequest", "URLRequest") {
+ AddSubSection(new URLRequestLiveSubSection(this));
+ AddSubSection(new URLRequestRecentSubSection(this));
+ }
+};
+
+class HttpCacheStatsSubSection : public SubSection {
+ public:
+ explicit HttpCacheStatsSubSection(SubSection* parent)
+ : SubSection(parent, "stats", "Statistics") {
+ }
+
+ virtual void OutputBody(URLRequestContext* context, std::string* out) {
+ ViewCacheHelper::GetStatisticsHTML(context, out);
+ }
+};
+
+class HttpCacheSection : public SubSection {
+ public:
+ explicit HttpCacheSection(SubSection* parent)
+ : SubSection(parent, "httpcache", "HttpCache") {
+ AddSubSection(new HttpCacheStatsSubSection(this));
+ }
+
+ virtual void OutputBody(URLRequestContext* context, std::string* out) {
+ // Advertise the view-cache URL (too much data to inline it).
+ out->append("<p><a href='/");
+ out->append(kViewHttpCacheSubPath);
+ out->append("'>View all cache entries</a></p>");
+ }
+};
+
+class SocketStreamLiveSubSection : public SubSection {
+ public:
+ explicit SocketStreamLiveSubSection(SubSection* parent)
+ : SubSection(parent, "live", "Live SocketStreams") {
+ }
+
+ virtual void OutputBody(URLRequestContext* context, std::string* out) {
+ std::vector<net::SocketStream*> sockets =
+ context->socket_stream_tracker()->GetLiveRequests();
+
+ out->append("<ol>");
+ for (size_t i = 0; i < sockets.size(); ++i) {
+ // Reverse the list order, so we dispay from most recent to oldest.
+ size_t index = sockets.size() - i - 1;
+ OutputURLAndLoadLog(sockets[index]->url(),
+ sockets[index]->load_log(),
+ out);
+ }
+ out->append("</ol>");
+ }
+};
+
+class SocketStreamRecentSubSection : public SubSection {
+ public:
+ explicit SocketStreamRecentSubSection(SubSection* parent)
+ : SubSection(parent, "recent", "Recently completed SocketStreams") {
+ }
+
+ virtual void OutputBody(URLRequestContext* context, std::string* out) {
+ RequestTracker<net::SocketStream>::RecentRequestInfoList recent =
+ context->socket_stream_tracker()->GetRecentlyDeceased();
+
+ DrawCommandButton("Clear", "clear-socketstream-graveyard", out);
+
+ out->append("<ol>");
+ for (size_t i = 0; i < recent.size(); ++i) {
+ // Reverse the list order, so we dispay from most recent to oldest.
+ size_t index = recent.size() - i - 1;
+ OutputURLAndLoadLog(recent[index].original_url,
+ recent[index].load_log, out);
+ }
+ out->append("</ol>");
+ }
+};
+
+class SocketStreamSubSection : public SubSection {
+ public:
+ explicit SocketStreamSubSection(SubSection* parent)
+ : SubSection(parent, "socketstream", "SocketStream") {
+ AddSubSection(new SocketStreamLiveSubSection(this));
+ AddSubSection(new SocketStreamRecentSubSection(this));
+ }
+};
+
+class AllSubSections : public SubSection {
+ public:
+ AllSubSections() : SubSection(NULL, "", "") {
+ AddSubSection(new ProxyServiceSubSection(this));
+ AddSubSection(new HostResolverSubSection(this));
+ AddSubSection(new URLRequestSubSection(this));
+ AddSubSection(new HttpCacheSection(this));
+ AddSubSection(new SocketStreamSubSection(this));
+ }
+};
+
+bool HandleCommand(const std::string& command, URLRequestContext* context) {
+ if (StartsWithASCII(command, "full-logging-", true)) {
+ bool enable_full_logging = (command == "full-logging-enable");
+ context->url_request_tracker()->SetUnbounded(enable_full_logging);
+ context->socket_stream_tracker()->SetUnbounded(enable_full_logging);
+ return true;
+ }
+
+ if (StartsWithASCII(command, "hostresolver-trace-", true)) {
+ bool enable_tracing = (command == "hostresolver-trace-enable");
+ if (GetHostResolverImpl(context)) {
+ GetHostResolverImpl(context)->EnableRequestsTracing(enable_tracing);
+ }
+ }
+
+ if (command == "clear-urlrequest-graveyard") {
+ context->url_request_tracker()->ClearRecentlyDeceased();
+ return true;
+ }
+
+ if (command == "clear-socketstream-graveyard") {
+ context->socket_stream_tracker()->ClearRecentlyDeceased();
+ return true;
+ }
+
+ if (command == "clear-hostcache") {
+ net::HostCache* host_cache = GetHostCache(context);
+ if (host_cache)
+ host_cache->clear();
+ return true;
+ }
+
+ if (command == "clear-badproxies") {
+ context->proxy_service()->ClearBadProxiesCache();
+ return true;
+ }
+
+ if (command == "clear-hostresolver-trace") {
+ if (GetHostResolverImpl(context))
+ GetHostResolverImpl(context)->ClearRequestsTrace();
+ }
+
+ if (command == "reload-proxy-config") {
+ context->proxy_service()->ForceReloadProxyConfig();
+ return true;
+ }
+
+ return false;
+}
+
+// Process any query strings in the request (for actions like toggling
+// full logging.
+void ProcessQueryStringCommands(URLRequestContext* context,
+ const std::string& query) {
+ if (!StartsWithASCII(query, "commands=", true)) {
+ // Not a recognized format.
+ return;
+ }
+
+ std::string commands_str = query.substr(strlen("commands="));
+ commands_str = UnescapeURLComponent(commands_str, UnescapeRule::NORMAL);
+
+ // The command list is comma-separated.
+ std::vector<std::string> commands;
+ SplitString(commands_str, ',', &commands);
+
+ for (size_t i = 0; i < commands.size(); ++i)
+ HandleCommand(commands[i], context);
+}
+
+// Appends some HTML controls to |data| that allow the user to enable full
+// logging, and clear some of the already logged data.
+void DrawControlsHeader(URLRequestContext* context, std::string* data) {
+ bool is_full_logging_enabled =
+ context->url_request_tracker()->IsUnbounded() &&
+ context->socket_stream_tracker()->IsUnbounded();
+
+ data->append("<div style='margin-bottom: 10px'>");
+
+ if (is_full_logging_enabled) {
+ DrawCommandButton("Disable full logging", "full-logging-disable", data);
+ } else {
+ DrawCommandButton("Enable full logging", "full-logging-enable", data);
+ }
+
+ DrawCommandButton("Clear all data",
+ // Send a list of comma separated commands:
+ "clear-badproxies,"
+ "clear-hostcache,"
+ "clear-urlrequest-graveyard,"
+ "clear-socketstream-graveyard,"
+ "clear-hostresolver-trace",
+ data);
+
+ data->append("</div>");
+}
+
+bool ViewNetInternalsJob::GetData(std::string* mime_type,
+ std::string* charset,
+ std::string* data) const {
+ mime_type->assign("text/html");
+ charset->assign("UTF-8");
+
+ URLRequestContext* context = request_->context();
+
+ data->clear();
+
+ // Use a different handler for "view-cache/*" subpaths.
+ std::string cache_key;
+ if (GetViewCacheKeyForRequest(&cache_key)) {
+ GURL url = MakeURL(kViewHttpCacheSubPath + std::string("/"));
+ ViewCacheHelper::GetEntryInfoHTML(cache_key, context, url.spec(), data);
+ return true;
+ }
+
+ // Handle any query arguments as a command request, then redirect back to
+ // the same URL stripped of query parameters. The redirect happens as part
+ // of IsRedirectResponse().
+ if (request_->url().has_query()) {
+ ProcessQueryStringCommands(context, request_->url().query());
+ return true;
+ }
+
+ std::string details = GetDetails(request_->url());
+
+ data->append("<!DOCTYPE HTML>"
+ "<html><head><title>Network internals</title>"
+ "<style>"
+ "body { font-family: sans-serif; font-size: 0.8em; }\n"
+ "tt, code, pre { font-family: WebKitHack, monospace; }\n"
+ ".subsection_body { margin: 10px 0 10px 2em; }\n"
+ ".subsection_title { font-weight: bold; }\n"
+ "</style>"
+ "<script>\n"
+
+ // Unfortunately we can't do XHR from chrome://net-internals
+ // because the chrome:// protocol restricts access.
+ //
+ // So instead, we will send commands by doing a form
+ // submission (which as a side effect will reload the page).
+ "function DoCommand(command) {\n"
+ " document.getElementById('cmd').value = command;\n"
+ " document.getElementById('cmdsender').submit();\n"
+ "}\n"
+
+ "</script>\n"
+ "</head><body>"
+ "<form action='' method=GET id=cmdsender>"
+ "<input type='hidden' id=cmd name='commands'>"
+ "</form>"
+ "<p><a href='http://dev.chromium.org/"
+ "developers/design-documents/view-net-internals'>"
+ "Help: how do I use this?</a></p>");
+
+ DrawControlsHeader(context, data);
+
+ SubSection* all = Singleton<AllSubSections>::get();
+ SubSection* section = all;
+
+ // Display only the subsection tree asked for.
+ if (!details.empty())
+ section = all->FindSubSectionByName(details);
+
+ if (section) {
+ section->OutputRecursive(context, data);
+ } else {
+ data->append("<i>Nothing found for \"");
+ data->append(EscapeForHTML(details));
+ data->append("\"</i>");
+ }
+
+ data->append("</body></html>");
+
+ return true;
+}
+
+bool ViewNetInternalsJob::IsRedirectResponse(GURL* location,
+ int* http_status_code) {
+ if (request_->url().has_query() && !GetViewCacheKeyForRequest(NULL)) {
+ // Strip the query parameters.
+ GURL::Replacements replacements;
+ replacements.ClearQuery();
+ *location = request_->url().ReplaceComponents(replacements);
+ *http_status_code = 307;
+ return true;
+ }
+ return false;
+}
+
+bool ViewNetInternalsJob::GetViewCacheKeyForRequest(
+ std::string* key) const {
+ std::string path = GetDetails(request_->url());
+ if (!StartsWithASCII(path, kViewHttpCacheSubPath, true))
+ return false;
+
+ if (path.size() > strlen(kViewHttpCacheSubPath) &&
+ path[strlen(kViewHttpCacheSubPath)] != '/')
+ return false;
+
+ if (key && path.size() > strlen(kViewHttpCacheSubPath) + 1)
+ *key = path.substr(strlen(kViewHttpCacheSubPath) + 1);
+
+ return true;
+}
+
+} // namespace
+
+// static
+bool ViewNetInternalsJobFactory::IsSupportedURL(const GURL& url) {
+ // Note that kNetworkViewInternalsURL is terminated by a '/'.
+ return StartsWithASCII(url.spec(),
+ chrome::kNetworkViewInternalsURL,
+ true /*case_sensitive*/);
+}
+
+// static
+URLRequestJob* ViewNetInternalsJobFactory::CreateJobForRequest(
+ URLRequest* request) {
+ return new ViewNetInternalsJob(request);
+}
diff --git a/chrome/browser/net/view_net_internals_job_factory.h b/chrome/browser/net/view_net_internals_job_factory.h
new file mode 100644
index 0000000..a694c93
--- /dev/null
+++ b/chrome/browser/net/view_net_internals_job_factory.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2010 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 CHROME_BROWSER_NET_VIEW_NET_INTERNALS_JOB_FACTORY_H_
+#define CHROME_BROWSER_NET_VIEW_NET_INTERNALS_JOB_FACTORY_H_
+
+class GURL;
+class URLRequest;
+class URLRequestJob;
+
+class ViewNetInternalsJobFactory {
+ public:
+ static bool IsSupportedURL(const GURL& url);
+ static URLRequestJob* CreateJobForRequest(URLRequest* request);
+};
+
+#endif // CHROME_BROWSER_NET_VIEW_NET_INTERNALS_JOB_FACTORY_H_