Add the host resolver cache to the new net internals page.
BUG=37421
Review URL: http://codereview.chromium.org/1558027
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@43998 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/dom_ui/net_internals_ui.cc b/chrome/browser/dom_ui/net_internals_ui.cc
index d33b17f5a..a932d75 100644
--- a/chrome/browser/dom_ui/net_internals_ui.cc
+++ b/chrome/browser/dom_ui/net_internals_ui.cc
@@ -23,6 +23,10 @@
#include "chrome/common/chrome_paths.h"
#include "chrome/common/url_constants.h"
#include "net/base/escape.h"
+#include "net/base/host_resolver_impl.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/url_request/url_request_context.h"
@@ -33,6 +37,18 @@
return Int64ToString((t - base::TimeTicks()).InMilliseconds());
}
+// Returns the HostCache for |context|'s primary HostResolver, or NULL if
+// there is none.
+net::HostCache* GetHostResolverCache(URLRequestContext* context) {
+ net::HostResolverImpl* host_resolver_impl =
+ context->host_resolver()->GetAsHostResolverImpl();
+
+ if (!host_resolver_impl)
+ return NULL;
+
+ return host_resolver_impl->cache();
+}
+
// TODO(eroman): Bootstrap the net-internals page using the passively logged
// data.
@@ -132,6 +148,8 @@
void OnReloadProxySettings(const Value* value);
void OnGetBadProxies(const Value* value);
void OnClearBadProxies(const Value* value);
+ void OnGetHostResolverCache(const Value* value);
+ void OnClearHostResolverCache(const Value* value);
// ChromeNetLog::Observer implementation:
virtual void OnAddEntry(const net::NetLog::Entry& entry);
@@ -279,6 +297,10 @@
proxy_->CreateCallback(&IOThreadImpl::OnGetBadProxies));
dom_ui_->RegisterMessageCallback("clearBadProxies",
proxy_->CreateCallback(&IOThreadImpl::OnClearBadProxies));
+ dom_ui_->RegisterMessageCallback("getHostResolverCache",
+ proxy_->CreateCallback(&IOThreadImpl::OnGetHostResolverCache));
+ dom_ui_->RegisterMessageCallback("clearHostResolverCache",
+ proxy_->CreateCallback(&IOThreadImpl::OnClearHostResolverCache));
}
void NetInternalsMessageHandler::CallJavascriptFunction(
@@ -417,6 +439,7 @@
// Notify the client of the basic proxy data.
OnGetProxySettings(NULL);
OnGetBadProxies(NULL);
+ OnGetHostResolverCache(NULL);
}
void NetInternalsMessageHandler::IOThreadImpl::OnGetProxySettings(
@@ -481,6 +504,77 @@
OnGetBadProxies(NULL);
}
+void NetInternalsMessageHandler::IOThreadImpl::OnGetHostResolverCache(
+ const Value* value) {
+
+ net::HostCache* cache =
+ GetHostResolverCache(context_getter_->GetURLRequestContext());
+
+ if (!cache) {
+ CallJavascriptFunction(L"g_browser.receivedHostResolverCache", NULL);
+ return;
+ }
+
+ DictionaryValue* dict = new DictionaryValue();
+
+ dict->SetInteger(L"capacity", static_cast<int>(cache->max_entries()));
+ dict->SetInteger(
+ L"ttl_success_ms",
+ static_cast<int>(cache->success_entry_ttl().InMilliseconds()));
+ dict->SetInteger(
+ L"ttl_failure_ms",
+ static_cast<int>(cache->failure_entry_ttl().InMilliseconds()));
+
+ ListValue* entry_list = new ListValue();
+
+ for (net::HostCache::EntryMap::const_iterator it =
+ cache->entries().begin();
+ it != cache->entries().end();
+ ++it) {
+ const net::HostCache::Key& key = it->first;
+ const net::HostCache::Entry* entry = it->second.get();
+
+ DictionaryValue* entry_dict = new DictionaryValue();
+
+ entry_dict->SetString(L"hostname", key.hostname);
+ entry_dict->SetInteger(L"address_family",
+ static_cast<int>(key.address_family));
+ entry_dict->SetString(L"expiration", TickCountToString(entry->expiration));
+
+ if (entry->error != net::OK) {
+ entry_dict->SetInteger(L"error", entry->error);
+ } else {
+ // Append all of the resolved addresses.
+ ListValue* address_list = new ListValue();
+ const struct addrinfo* current_address = entry->addrlist.head();
+ while (current_address) {
+ address_list->Append(Value::CreateStringValue(
+ net::NetAddressToString(current_address)));
+ current_address = current_address->ai_next;
+ }
+ entry_dict->Set(L"addresses", address_list);
+ }
+
+ entry_list->Append(entry_dict);
+ }
+
+ dict->Set(L"entries", entry_list);
+
+ CallJavascriptFunction(L"g_browser.receivedHostResolverCache", dict);
+}
+
+void NetInternalsMessageHandler::IOThreadImpl::OnClearHostResolverCache(
+ const Value* value) {
+ net::HostCache* cache =
+ GetHostResolverCache(context_getter_->GetURLRequestContext());
+
+ if (cache)
+ cache->clear();
+
+ // Cause the renderer to be notified of the new values.
+ OnGetHostResolverCache(NULL);
+}
+
void NetInternalsMessageHandler::IOThreadImpl::OnAddEntry(
const net::NetLog::Entry& entry) {
DCHECK(is_observing_log_);
diff --git a/chrome/browser/resources/net_internals/dnsview.js b/chrome/browser/resources/net_internals/dnsview.js
new file mode 100644
index 0000000..b1f7d2f
--- /dev/null
+++ b/chrome/browser/resources/net_internals/dnsview.js
@@ -0,0 +1,82 @@
+// 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.
+
+/**
+ * This view displays information on the host resolver:
+ *
+ * - Shows the current host cache contents.
+ * - Has a button to clear the host cache.
+ * - Shows the parameters used to construct the host cache (capacity, ttl).
+ *
+ * @constructor
+ */
+function DnsView(mainBoxId,
+ cacheTbodyId,
+ clearCacheButtonId,
+ capacitySpanId,
+ ttlSuccessSpanId,
+ ttlFailureSpanId) {
+ DivView.call(this, mainBoxId);
+
+ // Hook up the UI components.
+ this.cacheTbody_ = document.getElementById(cacheTbodyId);
+ this.capacitySpan_ = document.getElementById(capacitySpanId);
+ this.ttlSuccessSpan_ = document.getElementById(ttlSuccessSpanId);
+ this.ttlFailureSpan_ = document.getElementById(ttlFailureSpanId);
+
+ var clearCacheButton = document.getElementById(clearCacheButtonId);
+ clearCacheButton.onclick =
+ g_browser.sendClearHostResolverCache.bind(g_browser);
+
+ // Register to receive changes to the host resolver cache.
+ g_browser.addHostResolverCacheObserver(this);
+}
+
+inherits(DnsView, DivView);
+
+DnsView.prototype.onHostResolverCacheChanged = function(hostResolverCache) {
+ // Clear the existing values.
+ this.capacitySpan_.innerHTML = '';
+ this.ttlSuccessSpan_.innerHTML = '';
+ this.ttlFailureSpan_.innerHTML = '';
+ this.cacheTbody_.innerHTML = '';
+
+ // No cache.
+ if (!hostResolverCache)
+ return;
+
+ // Fill in the basic cache information.
+ addTextNode(this.capacitySpan_, hostResolverCache.capacity);
+ addTextNode(this.ttlSuccessSpan_, hostResolverCache.ttl_success_ms);
+ addTextNode(this.ttlFailureSpan_, hostResolverCache.ttl_failure_ms);
+
+ // Fill in the cache contents table.
+ for (var i = 0; i < hostResolverCache.entries.length; ++i) {
+ var e = hostResolverCache.entries[i];
+ var tr = addNode(this.cacheTbody_, 'tr');
+
+ var hostnameCell = addNode(tr, 'td');
+ addTextNode(hostnameCell, e.hostname);
+
+ var familyCell = addNode(tr, 'td');
+ addTextNode(familyCell, e.address_family);
+
+ var addressesCell = addNode(tr, 'td');
+
+ if (e.error != undefined) {
+ addTextNode(addressesCell, 'error: ' + e.error);
+ } else {
+ for (var j = 0; j < e.addresses.length; ++j) {
+ var address = e.addresses[j];
+ if (j != 0)
+ addNode(addressesCell, 'br');
+ addTextNode(addressesCell, address);
+ }
+ }
+
+ var expiresDate = g_browser.convertTimeTicksToDate(e.expiration);
+ var expiresCell = addNode(tr, 'td');
+ addTextNode(expiresCell, expiresDate.toLocaleString());
+ }
+};
diff --git a/chrome/browser/resources/net_internals/index.html b/chrome/browser/resources/net_internals/index.html
index d8c9046..301eb16 100644
--- a/chrome/browser/resources/net_internals/index.html
+++ b/chrome/browser/resources/net_internals/index.html
@@ -10,6 +10,7 @@
<script src="view.js"></script>
<script src="tabswitcherview.js"></script>
<script src="main.js"></script>
+ <script src="dnsview.js"></script>
<script src="requestsview.js"></script>
<script src="detailsview.js"></script>
<script src="sourceentry.js"></script>
@@ -54,8 +55,32 @@
<tbody id=badProxiesTableBody></tbody>
</table>
</div>
+ <!-- Host resolver info -->
+ <div id=dnsTabContent>
+ <h4>
+ Host resolver cache
+ <input type=button value="Clear host cache" id=clearHostResolverCache />
+ </h4>
+ <ul>
+ <li>Capacity: <span id=hostResolverCacheCapacity></span></li>
+ <li>Time to live (ms) for success entries:
+ <span id=hostResolverCacheTTLSuccess></span></li>
+ <li>Time to live (ms) for failure entries:
+ <span id=hostResolverCacheTTLFailure></span></li>
+ </ul>
+
+ <table border=1>
+ <thead>
+ <th>Hostname</th>
+ <th>Family</th>
+ <th>Addresses</th>
+ <th>Expires</th>
+ </thead>
+ <tbody id=hostResolverCacheTbody>
+ </tbody>
+ </table>
+ </div>
<!-- Sections TODO -->
- <div id=dnsTabContent>TODO: display dns information (outstanding jobs, host cache).</div>
<div id=socketsTabContent>TODO: display socket information (outstanding connect jobs)</div>
<div id=httpCacheTabContent>TODO: display http cache information (disk cache navigator)</div>
diff --git a/chrome/browser/resources/net_internals/main.js b/chrome/browser/resources/net_internals/main.js
index d4c0c11..3ab30eb 100644
--- a/chrome/browser/resources/net_internals/main.js
+++ b/chrome/browser/resources/net_internals/main.js
@@ -50,6 +50,14 @@
"badProxiesTableBody",
"clearBadProxies");
+ // Create a view which will display information on the host resolver.
+ var dnsView = new DnsView("dnsTabContent",
+ "hostResolverCacheTbody",
+ "clearHostResolverCache",
+ "hostResolverCacheCapacity",
+ "hostResolverCacheTTLSuccess",
+ "hostResolverCacheTTLFailure");
+
// Create a view which lets you tab between the different sub-views.
var categoryTabSwitcher =
new TabSwitcherView(new DivView('categoryTabHandles'));
@@ -57,7 +65,7 @@
// Populate the main tabs.
categoryTabSwitcher.addTab('requestsTab', requestsView, false);
categoryTabSwitcher.addTab('proxyTab', proxyView, false);
- categoryTabSwitcher.addTab('dnsTab', new DivView('dnsTabContent'), false);
+ categoryTabSwitcher.addTab('dnsTab', dnsView, false);
categoryTabSwitcher.addTab('socketsTab', new DivView('socketsTabContent'),
false);
categoryTabSwitcher.addTab('httpCacheTab',
@@ -104,10 +112,11 @@
this.logObservers_ = [];
this.proxySettingsObservers_ = [];
this.badProxiesObservers_ = [];
+ this.hostResolverCacheObservers_ = [];
- // Map from observer method name (i.e. 'onProxySettingsChanged', 'onBadProxiesChanged')
- // to the previously received data for that type. Used to tell if the data has
- // actually changed since we last polled it.
+ // Map from observer method name (i.e. 'onProxySettingsChanged',
+ // 'onBadProxiesChanged') to the previously received data for that type. Used
+ // to tell if the data has actually changed since we last polled it.
this.prevPollData_ = {};
}
@@ -144,10 +153,19 @@
chrome.send('getBadProxies');
};
+BrowserBridge.prototype.sendGetHostResolverCache = function() {
+ // The browser will call receivedHostResolverCache on completion.
+ chrome.send('getHostResolverCache');
+};
+
BrowserBridge.prototype.sendClearBadProxies = function() {
chrome.send('clearBadProxies');
};
+BrowserBridge.prototype.sendClearHostResolverCache = function() {
+ chrome.send('clearHostResolverCache');
+};
+
//------------------------------------------------------------------------------
// Messages received from the browser
//------------------------------------------------------------------------------
@@ -161,11 +179,13 @@
LogEventType = constantsMap;
};
-BrowserBridge.prototype.receivedLogEventPhaseConstants = function(constantsMap) {
+BrowserBridge.prototype.receivedLogEventPhaseConstants =
+function(constantsMap) {
LogEventPhase = constantsMap;
};
-BrowserBridge.prototype.receivedLogSourceTypeConstants = function(constantsMap) {
+BrowserBridge.prototype.receivedLogSourceTypeConstants =
+function(constantsMap) {
LogSourceType = constantsMap;
};
@@ -187,6 +207,13 @@
this.badProxiesObservers_, 'onBadProxiesChanged', badProxies);
};
+BrowserBridge.prototype.receivedHostResolverCache =
+function(hostResolverCache) {
+ this.dispatchToObserversFromPoll_(
+ this.hostResolverCacheObservers_, 'onHostResolverCacheChanged',
+ hostResolverCache);
+};
+
//------------------------------------------------------------------------------
/**
@@ -228,6 +255,16 @@
};
/**
+ * Adds a listener of the host resolver cache. |observer| will be called back
+ * when data is received, through:
+ *
+ * observer.onHostResolverCacheChanged(hostResolverCache)
+ */
+BrowserBridge.prototype.addHostResolverCacheObserver = function(observer) {
+ this.hostResolverCacheObservers_.push(observer);
+};
+
+/**
* The browser gives us times in terms of "time ticks" in milliseconds.
* This function converts the tick count to a Date() object.
*
@@ -244,8 +281,12 @@
};
BrowserBridge.prototype.doPolling_ = function() {
+ // TODO(eroman): Optimize this by using a separate polling frequency for the
+ // data consumed by the currently active view. Everything else can be on a low
+ // frequency poll since it won't impact the display.
this.sendGetProxySettings();
this.sendGetBadProxies();
+ this.sendGetHostResolverCache();
};
/**
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index e73c611..7c328e8 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -3203,6 +3203,7 @@
'destination': '<(PRODUCT_DIR)/resources/net_internals',
'files': [
'browser/resources/net_internals/detailsview.js',
+ 'browser/resources/net_internals/dnsview.js',
'browser/resources/net_internals/index.html',
'browser/resources/net_internals/loggrouper.js',
'browser/resources/net_internals/logviewpainter.js',