[go: nahoru, domu]

blob: e9682dd2de2e5f15f7e7a98b8d31efa04bfa9ff4 [file] [log] [blame]
// 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/url_request/url_request_view_net_internals_job.h"
#if defined(OS_WIN)
#include <ws2tcpip.h>
#else
#include <netdb.h>
#endif
#include <sstream>
#include "base/format_macros.h"
#include "base/stl_util-inl.h"
#include "base/string_util.h"
#include "net/base/escape.h"
#include "net/base/host_cache.h"
#include "net/base/load_log_util.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.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/view_cache_helper.h"
namespace {
const char kViewHttpCacheSubPath[] = "view-cache";
//------------------------------------------------------------------------------
// Format helpers.
//------------------------------------------------------------------------------
void OutputTextInPre(const std::string& text, std::string* out) {
out->append("<pre>");
out->append(EscapeForHTML(text));
out->append("</pre>");
}
//------------------------------------------------------------------------------
// 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,
URLRequestViewNetInternalsJob::URLFormat* url_format,
std::string* out) {
if (!is_root()) {
// Canonicalizing the URL escapes characters which cause problems in HTML.
std::string section_url =
url_format->MakeURL(GetFullyQualifiedName()).spec();
// Print the heading.
out->append(StringPrintf("<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, url_format, 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:
ProxyServiceCurrentConfigSubSection(SubSection* parent)
: SubSection(parent, "config", "Current configuration") {
}
virtual void OutputBody(URLRequestContext* context, std::string* 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:
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:
ProxyServiceBadProxiesSubSection(SubSection* parent)
: SubSection(parent, "bad_proxies", "Bad Proxies") {
}
virtual void OutputBody(URLRequestContext* context, std::string* out) {
out->append("TODO");
}
};
class ProxyServiceSubSection : public SubSection {
public:
ProxyServiceSubSection(SubSection* parent)
: SubSection(parent, "proxyservice", "ProxyService") {
AddSubSection(new ProxyServiceCurrentConfigSubSection(this));
AddSubSection(new ProxyServiceLastInitLogSubSection(this));
AddSubSection(new ProxyServiceBadProxiesSubSection(this));
}
};
class HostResolverCacheSubSection : public SubSection {
public:
HostResolverCacheSubSection(SubSection* parent)
: SubSection(parent, "hostcache", "HostCache") {
}
virtual void OutputBody(URLRequestContext* context, std::string* out) {
const net::HostCache* host_cache = context->host_resolver()->GetHostCache();
if (!host_cache || host_cache->caching_is_disabled()) {
out->append("<i>Caching is disabled.</i>");
return;
}
out->append(StringPrintf("<ul><li>Size: %" PRIuS "</li>"
"<li>Capacity: %" PRIuS "</li>"
"<li>Time to live (ms): %" PRIuS "</li></ul>",
host_cache->size(),
host_cache->max_entries(),
host_cache->cache_duration_ms()));
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);
if (entry->error == net::OK) {
// 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;
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;
}
out->append(StringPrintf("<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));
} else {
// This was an entry that failed to be resolved.
// Color negative entries red.
out->append(StringPrintf(
"<tr style='color:red'><td>%s</td><td>%s</td>"
"<td colspan=2>%s</td></tr>",
EscapeForHTML(key.hostname).c_str(),
EscapeForHTML(address_family_str).c_str(),
EscapeForHTML(net::ErrorToString(entry->error)).c_str()));
}
}
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 HostResolverSubSection : public SubSection {
public:
HostResolverSubSection(SubSection* parent)
: SubSection(parent, "hostresolver", "HostResolver") {
AddSubSection(new HostResolverCacheSubSection(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:
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:
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();
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:
URLRequestSubSection(SubSection* parent)
: SubSection(parent, "urlrequest", "URLRequest") {
AddSubSection(new URLRequestLiveSubSection(this));
AddSubSection(new URLRequestRecentSubSection(this));
}
};
class HttpCacheStatsSubSection : public SubSection {
public:
HttpCacheStatsSubSection(SubSection* parent)
: SubSection(parent, "stats", "Statistics") {
}
virtual void OutputBody(URLRequestContext* context, std::string* out) {
ViewCacheHelper::GetStatisticsHTML(context, out);
}
};
class HttpCacheSection : public SubSection {
public:
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:
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:
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();
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:
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));
}
};
// Returns true if |path| is a subpath for "view-cache".
// If it is, then |key| is assigned the subpath.
bool GetViewCacheKeyFromPath(const std::string path,
std::string* key) {
if (!StartsWithASCII(path, kViewHttpCacheSubPath, true))
return false;
if (path.size() > strlen(kViewHttpCacheSubPath) &&
path[strlen(kViewHttpCacheSubPath)] != '/')
return false;
if (path.size() > strlen(kViewHttpCacheSubPath) + 1)
*key = path.substr(strlen(kViewHttpCacheSubPath) + 1);
return true;
}
} // namespace
bool URLRequestViewNetInternalsJob::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();
std::string details = url_format_->GetDetails(request_->url());
data->clear();
// Use a different handler for "view-cache/*" subpaths.
std::string cache_key;
if (GetViewCacheKeyFromPath(details, &cache_key)) {
GURL url = url_format_->MakeURL(kViewHttpCacheSubPath + std::string("/"));
ViewCacheHelper::GetEntryInfoHTML(cache_key, context, url.spec(), data);
return true;
}
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>"
"</head><body>"
"<p><a href='http://dev.chromium.org/"
"developers/design-documents/view-net-internals'>"
"Help: how do I use this?</a></p>");
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, url_format_, data);
} else {
data->append("<i>Nothing found for \"");
data->append(EscapeForHTML(details));
data->append("\"</i>");
}
data->append("</body></html>");
return true;
}