Home | History | Annotate | Download | only in url_request
      1 // Copyright (c) 2009 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.
      4 
      5 #include "net/url_request/url_request_view_net_internals_job.h"
      6 
      7 #include <sstream>
      8 
      9 #include "base/format_macros.h"
     10 #include "base/stl_util-inl.h"
     11 #include "base/string_util.h"
     12 #include "net/base/escape.h"
     13 #include "net/base/host_resolver_impl.h"
     14 #include "net/base/load_log_util.h"
     15 #include "net/base/net_errors.h"
     16 #include "net/base/net_util.h"
     17 #include "net/base/sys_addrinfo.h"
     18 #include "net/proxy/proxy_service.h"
     19 #include "net/socket_stream/socket_stream.h"
     20 #include "net/url_request/url_request.h"
     21 #include "net/url_request/url_request_context.h"
     22 #include "net/url_request/view_cache_helper.h"
     23 
     24 namespace {
     25 
     26 const char kViewHttpCacheSubPath[] = "view-cache";
     27 
     28 //------------------------------------------------------------------------------
     29 // Format helpers.
     30 //------------------------------------------------------------------------------
     31 
     32 void OutputTextInPre(const std::string& text, std::string* out) {
     33   out->append("<pre>");
     34   out->append(EscapeForHTML(text));
     35   out->append("</pre>");
     36 }
     37 
     38 // Appends an input button to |data| with text |title| that sends the command
     39 // string |command| back to the browser, and then refreshes the page.
     40 void DrawCommandButton(const std::string& title,
     41                        const std::string& command,
     42                        std::string* data) {
     43   StringAppendF(data, "<input type=\"button\" value=\"%s\" "
     44                "onclick=\"DoCommand('%s')\" />",
     45                title.c_str(),
     46                command.c_str());
     47 }
     48 
     49 //------------------------------------------------------------------------------
     50 // URLRequestContext helpers.
     51 //------------------------------------------------------------------------------
     52 
     53 net::HostResolverImpl* GetHostResolverImpl(URLRequestContext* context) {
     54   if (context->host_resolver()->IsHostResolverImpl())
     55     return static_cast<net::HostResolverImpl*> (context->host_resolver());
     56   return NULL;
     57 }
     58 
     59 net::HostCache* GetHostCache(URLRequestContext* context) {
     60   if (GetHostResolverImpl(context))
     61     return GetHostResolverImpl(context)->cache();
     62   return NULL;
     63 }
     64 
     65 //------------------------------------------------------------------------------
     66 // Subsection definitions.
     67 //------------------------------------------------------------------------------
     68 
     69 class SubSection {
     70  public:
     71   // |name| is the URL path component for this subsection.
     72   // |title| is the textual description.
     73   SubSection(SubSection* parent, const char* name, const char* title)
     74       : parent_(parent),
     75         name_(name),
     76         title_(title) {
     77   }
     78 
     79   virtual ~SubSection() {
     80     STLDeleteContainerPointers(children_.begin(), children_.end());
     81   }
     82 
     83   // Outputs the subsection's contents to |out|.
     84   virtual void OutputBody(URLRequestContext* context, std::string* out) {}
     85 
     86   // Outputs this subsection, and all of its children.
     87   void OutputRecursive(URLRequestContext* context,
     88                        URLRequestViewNetInternalsJob::URLFormat* url_format,
     89                        std::string* out) {
     90     if (!is_root()) {
     91       // Canonicalizing the URL escapes characters which cause problems in HTML.
     92       std::string section_url =
     93           url_format->MakeURL(GetFullyQualifiedName()).spec();
     94 
     95       // Print the heading.
     96       StringAppendF(
     97           out,
     98           "<div>"
     99           "<span class=subsection_title>%s</span> "
    100           "<span class=subsection_name>(<a href='%s'>%s</a>)<span>"
    101           "</div>",
    102           EscapeForHTML(title_).c_str(),
    103           section_url.c_str(),
    104           EscapeForHTML(section_url).c_str());
    105 
    106       out->append("<div class=subsection_body>");
    107     }
    108 
    109     OutputBody(context, out);
    110 
    111     for (size_t i = 0; i < children_.size(); ++i)
    112       children_[i]->OutputRecursive(context, url_format, out);
    113 
    114     if (!is_root())
    115       out->append("</div>");
    116   }
    117 
    118   // Returns the SubSection contained by |this| with fully qualified name
    119   // |dotted_name|, or NULL if none was found.
    120   SubSection* FindSubSectionByName(const std::string& dotted_name) {
    121     if (dotted_name == "")
    122       return this;
    123 
    124     std::string child_name;
    125     std::string child_sub_name;
    126 
    127     size_t split_pos = dotted_name.find('.');
    128     if (split_pos == std::string::npos) {
    129       child_name = dotted_name;
    130       child_sub_name = std::string();
    131     } else {
    132       child_name = dotted_name.substr(0, split_pos);
    133       child_sub_name = dotted_name.substr(split_pos + 1);
    134     }
    135 
    136     for (size_t i = 0; i < children_.size(); ++i) {
    137       if (children_[i]->name_ == child_name)
    138         return children_[i]->FindSubSectionByName(child_sub_name);
    139     }
    140 
    141     return NULL;  // Not found.
    142   }
    143 
    144   std::string GetFullyQualifiedName() {
    145     if (!parent_)
    146       return name_;
    147 
    148     std::string parent_name = parent_->GetFullyQualifiedName();
    149     if (parent_name.empty())
    150       return name_;
    151 
    152     return parent_name + "." + name_;
    153   }
    154 
    155   bool is_root() const {
    156     return parent_ == NULL;
    157   }
    158 
    159  protected:
    160   typedef std::vector<SubSection*> SubSectionList;
    161 
    162   void AddSubSection(SubSection* subsection) {
    163     children_.push_back(subsection);
    164   }
    165 
    166   SubSection* parent_;
    167   std::string name_;
    168   std::string title_;
    169 
    170   SubSectionList children_;
    171 };
    172 
    173 class ProxyServiceCurrentConfigSubSection : public SubSection {
    174  public:
    175   explicit ProxyServiceCurrentConfigSubSection(SubSection* parent)
    176       : SubSection(parent, "config", "Current configuration") {
    177   }
    178 
    179   virtual void OutputBody(URLRequestContext* context, std::string* out) {
    180     DrawCommandButton("Force reload", "reload-proxy-config", out);
    181 
    182     net::ProxyService* proxy_service = context->proxy_service();
    183     if (proxy_service->config_has_been_initialized()) {
    184       // net::ProxyConfig defines an operator<<.
    185       std::ostringstream stream;
    186       stream << proxy_service->config();
    187       OutputTextInPre(stream.str(), out);
    188     } else {
    189       out->append("<i>Not yet initialized</i>");
    190     }
    191   }
    192 };
    193 
    194 class ProxyServiceLastInitLogSubSection : public SubSection {
    195  public:
    196   explicit ProxyServiceLastInitLogSubSection(SubSection* parent)
    197       : SubSection(parent, "init_log", "Last initialized load log") {
    198   }
    199 
    200   virtual void OutputBody(URLRequestContext* context, std::string* out) {
    201     net::ProxyService* proxy_service = context->proxy_service();
    202     net::LoadLog* log = proxy_service->init_proxy_resolver_log();
    203     if (log) {
    204       OutputTextInPre(net::LoadLogUtil::PrettyPrintAsEventTree(log), out);
    205     } else {
    206       out->append("<i>None.</i>");
    207     }
    208   }
    209 };
    210 
    211 class ProxyServiceBadProxiesSubSection : public SubSection {
    212  public:
    213   explicit ProxyServiceBadProxiesSubSection(SubSection* parent)
    214       : SubSection(parent, "bad_proxies", "Bad Proxies") {
    215   }
    216 
    217   virtual void OutputBody(URLRequestContext* context, std::string* out) {
    218     net::ProxyService* proxy_service = context->proxy_service();
    219     const net::ProxyRetryInfoMap& bad_proxies_map =
    220         proxy_service->proxy_retry_info();
    221 
    222     DrawCommandButton("Clear", "clear-badproxies", out);
    223 
    224     out->append("<table border=1>");
    225     out->append("<tr><th>Bad proxy server</th>"
    226                 "<th>Remaining time until retry (ms)</th></tr>");
    227 
    228     for (net::ProxyRetryInfoMap::const_iterator it = bad_proxies_map.begin();
    229          it != bad_proxies_map.end(); ++it) {
    230       const std::string& proxy_uri = it->first;
    231       const net::ProxyRetryInfo& retry_info = it->second;
    232 
    233       // Note that ttl_ms may be negative, for the cases where entries have
    234       // expired but not been garbage collected yet.
    235       int ttl_ms = static_cast<int>(
    236           (retry_info.bad_until - base::TimeTicks::Now()).InMilliseconds());
    237 
    238       // Color expired entries blue.
    239       if (ttl_ms > 0)
    240         out->append("<tr>");
    241       else
    242         out->append("<tr style='color:blue'>");
    243 
    244       StringAppendF(out, "<td>%s</td><td>%d</td>",
    245                     EscapeForHTML(proxy_uri).c_str(),
    246                     ttl_ms);
    247 
    248       out->append("</tr>");
    249     }
    250     out->append("</table>");
    251   }
    252 };
    253 
    254 class ProxyServiceSubSection : public SubSection {
    255  public:
    256   explicit ProxyServiceSubSection(SubSection* parent)
    257       : SubSection(parent, "proxyservice", "ProxyService") {
    258     AddSubSection(new ProxyServiceCurrentConfigSubSection(this));
    259     AddSubSection(new ProxyServiceLastInitLogSubSection(this));
    260     AddSubSection(new ProxyServiceBadProxiesSubSection(this));
    261   }
    262 };
    263 
    264 class HostResolverCacheSubSection : public SubSection {
    265  public:
    266   explicit HostResolverCacheSubSection(SubSection* parent)
    267       : SubSection(parent, "hostcache", "HostCache") {
    268   }
    269 
    270   virtual void OutputBody(URLRequestContext* context, std::string* out) {
    271     const net::HostCache* host_cache = GetHostCache(context);
    272 
    273     if (!host_cache || host_cache->caching_is_disabled()) {
    274       out->append("<i>Caching is disabled.</i>");
    275       return;
    276     }
    277 
    278     DrawCommandButton("Clear", "clear-hostcache", out);
    279 
    280     StringAppendF(
    281         out,
    282         "<ul><li>Size: %" PRIuS "</li>"
    283         "<li>Capacity: %" PRIuS "</li>"
    284         "<li>Time to live (ms) for success entries: %" PRId64"</li>"
    285         "<li>Time to live (ms) for failure entries: %" PRId64"</li></ul>",
    286         host_cache->size(),
    287         host_cache->max_entries(),
    288         host_cache->success_entry_ttl().InMilliseconds(),
    289         host_cache->failure_entry_ttl().InMilliseconds());
    290 
    291     out->append("<table border=1>"
    292                 "<tr>"
    293                 "<th>Host</th>"
    294                 "<th>Address family</th>"
    295                 "<th>Address list</th>"
    296                 "<th>Time to live (ms)</th>"
    297                 "</tr>");
    298 
    299     for (net::HostCache::EntryMap::const_iterator it =
    300              host_cache->entries().begin();
    301          it != host_cache->entries().end();
    302          ++it) {
    303       const net::HostCache::Key& key = it->first;
    304       const net::HostCache::Entry* entry = it->second.get();
    305 
    306       std::string address_family_str =
    307           AddressFamilyToString(key.address_family);
    308 
    309       // Note that ttl_ms may be negative, for the cases where entries have
    310       // expired but not been garbage collected yet.
    311       int ttl_ms = static_cast<int>(
    312           (entry->expiration - base::TimeTicks::Now()).InMilliseconds());
    313 
    314       // Color expired entries blue.
    315       if (ttl_ms > 0) {
    316         out->append("<tr>");
    317       } else {
    318         out->append("<tr style='color:blue'>");
    319       }
    320 
    321       // Stringify all of the addresses in the address list, separated
    322       // by newlines (br).
    323       std::string address_list_html;
    324 
    325       if (entry->error != net::OK) {
    326         address_list_html = "<span style='font-weight: bold; color:red'>" +
    327                             EscapeForHTML(net::ErrorToString(entry->error)) +
    328                             "</span>";
    329       } else {
    330         const struct addrinfo* current_address = entry->addrlist.head();
    331         while (current_address) {
    332           if (!address_list_html.empty())
    333             address_list_html += "<br>";
    334           address_list_html += EscapeForHTML(
    335               net::NetAddressToString(current_address));
    336           current_address = current_address->ai_next;
    337         }
    338       }
    339 
    340       StringAppendF(out,
    341                     "<td>%s</td><td>%s</td><td>%s</td>"
    342                     "<td>%d</td></tr>",
    343                     EscapeForHTML(key.hostname).c_str(),
    344                     EscapeForHTML(address_family_str).c_str(),
    345                     address_list_html.c_str(),
    346                     ttl_ms);
    347     }
    348 
    349     out->append("</table>");
    350   }
    351 
    352   static std::string AddressFamilyToString(net::AddressFamily address_family) {
    353     switch (address_family) {
    354       case net::ADDRESS_FAMILY_IPV4:
    355         return "IPV4";
    356       case net::ADDRESS_FAMILY_IPV6:
    357         return "IPV6";
    358       case net::ADDRESS_FAMILY_UNSPECIFIED:
    359         return "UNSPECIFIED";
    360       default:
    361         NOTREACHED();
    362         return "???";
    363     }
    364   }
    365 };
    366 
    367 class HostResolverTraceSubSection : public SubSection {
    368  public:
    369   explicit HostResolverTraceSubSection(SubSection* parent)
    370       : SubSection(parent, "trace", "Trace of requests") {
    371   }
    372 
    373   virtual void OutputBody(URLRequestContext* context, std::string* out) {
    374     net::HostResolverImpl* resolver = GetHostResolverImpl(context);
    375     if (!resolver) {
    376       out->append("<i>Tracing is not supported by this resolver.</i>");
    377       return;
    378     }
    379 
    380     DrawCommandButton("Clear", "clear-hostresolver-trace", out);
    381 
    382     if (resolver->IsRequestsTracingEnabled()) {
    383       DrawCommandButton("Disable tracing", "hostresolver-trace-disable", out);
    384     } else {
    385       DrawCommandButton("Enable tracing", "hostresolver-trace-enable", out);
    386     }
    387 
    388     scoped_refptr<net::LoadLog> log = resolver->GetRequestsTrace();
    389 
    390     if (log) {
    391       OutputTextInPre(net::LoadLogUtil::PrettyPrintAsEventTree(log), out);
    392     } else {
    393       out->append("<p><i>No trace information, must enable tracing.</i></p>");
    394     }
    395   }
    396 };
    397 
    398 class HostResolverSubSection : public SubSection {
    399  public:
    400   explicit HostResolverSubSection(SubSection* parent)
    401       : SubSection(parent, "hostresolver", "HostResolver") {
    402     AddSubSection(new HostResolverCacheSubSection(this));
    403     AddSubSection(new HostResolverTraceSubSection(this));
    404   }
    405 };
    406 
    407 // Helper for the URLRequest "outstanding" and "live" sections.
    408 void OutputURLAndLoadLog(const GURL& url,
    409                          const net::LoadLog* log,
    410                          std::string* out) {
    411   out->append("<li>");
    412   out->append("<nobr>");
    413   out->append(EscapeForHTML(url.possibly_invalid_spec()));
    414   out->append("</nobr>");
    415   if (log)
    416     OutputTextInPre(net::LoadLogUtil::PrettyPrintAsEventTree(log), out);
    417   out->append("</li>");
    418 }
    419 
    420 class URLRequestLiveSubSection : public SubSection {
    421  public:
    422   explicit URLRequestLiveSubSection(SubSection* parent)
    423       : SubSection(parent, "outstanding", "Outstanding requests") {
    424   }
    425 
    426   virtual void OutputBody(URLRequestContext* context, std::string* out) {
    427     std::vector<URLRequest*> requests =
    428         context->url_request_tracker()->GetLiveRequests();
    429 
    430     out->append("<ol>");
    431     for (size_t i = 0; i < requests.size(); ++i) {
    432       // Reverse the list order, so we dispay from most recent to oldest.
    433       size_t index = requests.size() - i - 1;
    434       OutputURLAndLoadLog(requests[index]->original_url(),
    435                           requests[index]->load_log(),
    436                           out);
    437     }
    438     out->append("</ol>");
    439   }
    440 };
    441 
    442 class URLRequestRecentSubSection : public SubSection {
    443  public:
    444   explicit URLRequestRecentSubSection(SubSection* parent)
    445       : SubSection(parent, "recent", "Recently completed requests") {
    446   }
    447 
    448   virtual void OutputBody(URLRequestContext* context, std::string* out) {
    449     RequestTracker<URLRequest>::RecentRequestInfoList recent =
    450         context->url_request_tracker()->GetRecentlyDeceased();
    451 
    452     DrawCommandButton("Clear", "clear-urlrequest-graveyard", out);
    453 
    454     out->append("<ol>");
    455     for (size_t i = 0; i < recent.size(); ++i) {
    456       // Reverse the list order, so we dispay from most recent to oldest.
    457       size_t index = recent.size() - i - 1;
    458       OutputURLAndLoadLog(recent[index].original_url,
    459                           recent[index].load_log, out);
    460     }
    461     out->append("</ol>");
    462   }
    463 };
    464 
    465 class URLRequestSubSection : public SubSection {
    466  public:
    467   explicit URLRequestSubSection(SubSection* parent)
    468       : SubSection(parent, "urlrequest", "URLRequest") {
    469     AddSubSection(new URLRequestLiveSubSection(this));
    470     AddSubSection(new URLRequestRecentSubSection(this));
    471   }
    472 };
    473 
    474 class HttpCacheStatsSubSection : public SubSection {
    475  public:
    476   explicit HttpCacheStatsSubSection(SubSection* parent)
    477       : SubSection(parent, "stats", "Statistics") {
    478   }
    479 
    480   virtual void OutputBody(URLRequestContext* context, std::string* out) {
    481     ViewCacheHelper::GetStatisticsHTML(context, out);
    482   }
    483 };
    484 
    485 class HttpCacheSection : public SubSection {
    486  public:
    487   explicit HttpCacheSection(SubSection* parent)
    488       : SubSection(parent, "httpcache", "HttpCache") {
    489     AddSubSection(new HttpCacheStatsSubSection(this));
    490   }
    491 
    492   virtual void OutputBody(URLRequestContext* context, std::string* out) {
    493     // Advertise the view-cache URL (too much data to inline it).
    494     out->append("<p><a href='/");
    495     out->append(kViewHttpCacheSubPath);
    496     out->append("'>View all cache entries</a></p>");
    497   }
    498 };
    499 
    500 class SocketStreamLiveSubSection : public SubSection {
    501  public:
    502   explicit SocketStreamLiveSubSection(SubSection* parent)
    503       : SubSection(parent, "live", "Live SocketStreams") {
    504   }
    505 
    506   virtual void OutputBody(URLRequestContext* context, std::string* out) {
    507     std::vector<net::SocketStream*> sockets =
    508         context->socket_stream_tracker()->GetLiveRequests();
    509 
    510     out->append("<ol>");
    511     for (size_t i = 0; i < sockets.size(); ++i) {
    512       // Reverse the list order, so we dispay from most recent to oldest.
    513       size_t index = sockets.size() - i - 1;
    514       OutputURLAndLoadLog(sockets[index]->url(),
    515                           sockets[index]->load_log(),
    516                           out);
    517     }
    518     out->append("</ol>");
    519   }
    520 };
    521 
    522 class SocketStreamRecentSubSection : public SubSection {
    523  public:
    524   explicit SocketStreamRecentSubSection(SubSection* parent)
    525       : SubSection(parent, "recent", "Recently completed SocketStreams") {
    526   }
    527 
    528   virtual void OutputBody(URLRequestContext* context, std::string* out) {
    529     RequestTracker<net::SocketStream>::RecentRequestInfoList recent =
    530         context->socket_stream_tracker()->GetRecentlyDeceased();
    531 
    532     DrawCommandButton("Clear", "clear-socketstream-graveyard", out);
    533 
    534     out->append("<ol>");
    535     for (size_t i = 0; i < recent.size(); ++i) {
    536       // Reverse the list order, so we dispay from most recent to oldest.
    537       size_t index = recent.size() - i - 1;
    538       OutputURLAndLoadLog(recent[index].original_url,
    539                           recent[index].load_log, out);
    540     }
    541     out->append("</ol>");
    542   }
    543 };
    544 
    545 class SocketStreamSubSection : public SubSection {
    546  public:
    547   explicit SocketStreamSubSection(SubSection* parent)
    548       : SubSection(parent, "socketstream", "SocketStream") {
    549     AddSubSection(new SocketStreamLiveSubSection(this));
    550     AddSubSection(new SocketStreamRecentSubSection(this));
    551   }
    552 };
    553 
    554 class AllSubSections : public SubSection {
    555  public:
    556   AllSubSections() : SubSection(NULL, "", "") {
    557     AddSubSection(new ProxyServiceSubSection(this));
    558     AddSubSection(new HostResolverSubSection(this));
    559     AddSubSection(new URLRequestSubSection(this));
    560     AddSubSection(new HttpCacheSection(this));
    561     AddSubSection(new SocketStreamSubSection(this));
    562   }
    563 };
    564 
    565 bool HandleCommand(const std::string& command, URLRequestContext* context) {
    566   if (StartsWithASCII(command, "full-logging-", true)) {
    567     bool enable_full_logging = (command == "full-logging-enable");
    568     context->url_request_tracker()->SetUnbounded(enable_full_logging);
    569     context->socket_stream_tracker()->SetUnbounded(enable_full_logging);
    570     return true;
    571   }
    572 
    573   if (StartsWithASCII(command, "hostresolver-trace-", true)) {
    574     bool enable_tracing = (command == "hostresolver-trace-enable");
    575     if (GetHostResolverImpl(context)) {
    576       GetHostResolverImpl(context)->EnableRequestsTracing(enable_tracing);
    577     }
    578   }
    579 
    580   if (command == "clear-urlrequest-graveyard") {
    581     context->url_request_tracker()->ClearRecentlyDeceased();
    582     return true;
    583   }
    584 
    585   if (command == "clear-socketstream-graveyard") {
    586     context->socket_stream_tracker()->ClearRecentlyDeceased();
    587     return true;
    588   }
    589 
    590   if (command == "clear-hostcache") {
    591     net::HostCache* host_cache = GetHostCache(context);
    592     if (host_cache)
    593       host_cache->clear();
    594     return true;
    595   }
    596 
    597   if (command == "clear-badproxies") {
    598     context->proxy_service()->ClearBadProxiesCache();
    599     return true;
    600   }
    601 
    602   if (command == "clear-hostresolver-trace") {
    603     if (GetHostResolverImpl(context))
    604       GetHostResolverImpl(context)->ClearRequestsTrace();
    605   }
    606 
    607   if (command == "reload-proxy-config") {
    608     context->proxy_service()->ForceReloadProxyConfig();
    609     return true;
    610   }
    611 
    612   return false;
    613 }
    614 
    615 // Process any query strings in the request (for actions like toggling
    616 // full logging.
    617 void ProcessQueryStringCommands(URLRequestContext* context,
    618                                 const std::string& query) {
    619   if (!StartsWithASCII(query, "commands=", true)) {
    620     // Not a recognized format.
    621     return;
    622   }
    623 
    624   std::string commands_str = query.substr(strlen("commands="));
    625   commands_str = UnescapeURLComponent(commands_str, UnescapeRule::NORMAL);
    626 
    627   // The command list is comma-separated.
    628   std::vector<std::string> commands;
    629   SplitString(commands_str, ',', &commands);
    630 
    631   for (size_t i = 0; i < commands.size(); ++i)
    632     HandleCommand(commands[i], context);
    633 }
    634 
    635 // Appends some HTML controls to |data| that allow the user to enable full
    636 // logging, and clear some of the already logged data.
    637 void DrawControlsHeader(URLRequestContext* context, std::string* data) {
    638   bool is_full_logging_enabled =
    639       context->url_request_tracker()->IsUnbounded() &&
    640       context->socket_stream_tracker()->IsUnbounded();
    641 
    642   data->append("<div style='margin-bottom: 10px'>");
    643 
    644   if (is_full_logging_enabled) {
    645     DrawCommandButton("Disable full logging", "full-logging-disable", data);
    646   } else {
    647     DrawCommandButton("Enable full logging", "full-logging-enable", data);
    648   }
    649 
    650   DrawCommandButton("Clear all data",
    651                     // Send a list of comma separated commands:
    652                     "clear-badproxies,"
    653                     "clear-hostcache,"
    654                     "clear-urlrequest-graveyard,"
    655                     "clear-socketstream-graveyard,"
    656                     "clear-hostresolver-trace",
    657                     data);
    658 
    659   data->append("</div>");
    660 }
    661 
    662 }  // namespace
    663 
    664 bool URLRequestViewNetInternalsJob::GetData(std::string* mime_type,
    665                                             std::string* charset,
    666                                             std::string* data) const {
    667   mime_type->assign("text/html");
    668   charset->assign("UTF-8");
    669 
    670   URLRequestContext* context = request_->context();
    671 
    672   data->clear();
    673 
    674   // Use a different handler for "view-cache/*" subpaths.
    675   std::string cache_key;
    676   if (GetViewCacheKeyForRequest(&cache_key)) {
    677     GURL url = url_format_->MakeURL(kViewHttpCacheSubPath + std::string("/"));
    678     ViewCacheHelper::GetEntryInfoHTML(cache_key, context, url.spec(), data);
    679     return true;
    680   }
    681 
    682   // Handle any query arguments as a command request, then redirect back to
    683   // the same URL stripped of query parameters. The redirect happens as part
    684   // of IsRedirectResponse().
    685   if (request_->url().has_query()) {
    686     ProcessQueryStringCommands(context, request_->url().query());
    687     return true;
    688   }
    689 
    690   std::string details = url_format_->GetDetails(request_->url());
    691 
    692   data->append("<!DOCTYPE HTML>"
    693                "<html><head><title>Network internals</title>"
    694                "<style>"
    695                "body { font-family: sans-serif; font-size: 0.8em; }\n"
    696                "tt, code, pre { font-family: WebKitHack, monospace; }\n"
    697                ".subsection_body { margin: 10px 0 10px 2em; }\n"
    698                ".subsection_title { font-weight: bold; }\n"
    699                "</style>"
    700                "<script>\n"
    701 
    702                // Unfortunately we can't do XHR from chrome://net-internals
    703                // because the chrome:// protocol restricts access.
    704                //
    705                // So instead, we will send commands by doing a form
    706                // submission (which as a side effect will reload the page).
    707                "function DoCommand(command) {\n"
    708                "  document.getElementById('cmd').value = command;\n"
    709                "  document.getElementById('cmdsender').submit();\n"
    710                "}\n"
    711 
    712                "</script>\n"
    713                "</head><body>"
    714                "<form action='' method=GET id=cmdsender>"
    715                "<input type='hidden' id=cmd name='commands'>"
    716                "</form>"
    717                "<p><a href='http://dev.chromium.org/"
    718                "developers/design-documents/view-net-internals'>"
    719                "Help: how do I use this?</a></p>");
    720 
    721   DrawControlsHeader(context, data);
    722 
    723   SubSection* all = Singleton<AllSubSections>::get();
    724   SubSection* section = all;
    725 
    726   // Display only the subsection tree asked for.
    727   if (!details.empty())
    728     section = all->FindSubSectionByName(details);
    729 
    730   if (section) {
    731     section->OutputRecursive(context, url_format_, data);
    732   } else {
    733     data->append("<i>Nothing found for \"");
    734     data->append(EscapeForHTML(details));
    735     data->append("\"</i>");
    736   }
    737 
    738   data->append("</body></html>");
    739 
    740   return true;
    741 }
    742 
    743 bool URLRequestViewNetInternalsJob::IsRedirectResponse(GURL* location,
    744                                                        int* http_status_code) {
    745   if (request_->url().has_query() && !GetViewCacheKeyForRequest(NULL)) {
    746     // Strip the query parameters.
    747     GURL::Replacements replacements;
    748     replacements.ClearQuery();
    749     *location = request_->url().ReplaceComponents(replacements);
    750     *http_status_code = 307;
    751     return true;
    752   }
    753   return false;
    754 }
    755 
    756 bool URLRequestViewNetInternalsJob::GetViewCacheKeyForRequest(
    757     std::string* key) const {
    758   std::string path = url_format_->GetDetails(request_->url());
    759   if (!StartsWithASCII(path, kViewHttpCacheSubPath, true))
    760     return false;
    761 
    762   if (path.size() > strlen(kViewHttpCacheSubPath) &&
    763       path[strlen(kViewHttpCacheSubPath)] != '/')
    764     return false;
    765 
    766   if (key && path.size() > strlen(kViewHttpCacheSubPath) + 1)
    767     *key = path.substr(strlen(kViewHttpCacheSubPath) + 1);
    768 
    769   return true;
    770 }
    771