summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorjar@chromium.org <jar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-18 00:39:18 +0000
committerjar@chromium.org <jar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-18 00:39:18 +0000
commit760d970aa408a7ea9a00e4e2ab792ef05f9355e5 (patch)
treee6699d0e0b2fb930685ce1e346ca41db566c8c21 /chrome
parentd71cc6cc3834d8824c063b9fccf75b3559f545f3 (diff)
downloadchromium_src-760d970aa408a7ea9a00e4e2ab792ef05f9355e5.zip
chromium_src-760d970aa408a7ea9a00e4e2ab792ef05f9355e5.tar.gz
chromium_src-760d970aa408a7ea9a00e4e2ab792ef05f9355e5.tar.bz2
Support speculative pre-connection to search URLs
Implement several flavors of TCP/IP speculative preconnection under a command line flag (not yet on by default). The first area of preconnection takes place when a user types a query into the omnibox, as we preconnect to the search service when the omnibox suggests it is going to do a search. The second area involves subresources, such as images. When a navigation takes place, and we've seen navigations to that domain/port before, and the history-based probabability that we'll need to make a connection to a second site (host/port) is sufficiently large, then we preconnect to that second site while we are still connecting to the primary site (and before we've gotten content from the primary site. We also fall-back to mere DNS pre-resolution of subresource hostnames when the probability of a connection to the subresource is not high enough. BUG=42694 r=pkasting,willchan,mbelshe Review URL: http://codereview.chromium.org/1585029 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@47479 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/autocomplete/autocomplete_edit.cc22
-rw-r--r--chrome/browser/browser_main.cc5
-rw-r--r--chrome/browser/browser_process_impl.cc4
-rw-r--r--chrome/browser/io_thread.cc13
-rw-r--r--chrome/browser/io_thread.h6
-rw-r--r--chrome/browser/net/dns_global.cc137
-rw-r--r--chrome/browser/net/dns_global.h12
-rw-r--r--chrome/browser/net/dns_host_info.cc29
-rw-r--r--chrome/browser/net/dns_host_info.h23
-rw-r--r--chrome/browser/net/dns_host_info_unittest.cc26
-rw-r--r--chrome/browser/net/dns_master.cc287
-rw-r--r--chrome/browser/net/dns_master.h70
-rw-r--r--chrome/browser/net/dns_master_unittest.cc416
-rw-r--r--chrome/browser/net/preconnect.cc67
-rw-r--r--chrome/browser/net/preconnect.h40
-rw-r--r--chrome/browser/net/referrer.cc156
-rw-r--r--chrome/browser/net/referrer.h69
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/common/chrome_switches.cc3
-rw-r--r--chrome/common/chrome_switches.h1
-rw-r--r--chrome/common/pref_names.cc4
21 files changed, 948 insertions, 444 deletions
diff --git a/chrome/browser/autocomplete/autocomplete_edit.cc b/chrome/browser/autocomplete/autocomplete_edit.cc
index 9060153..a650e1d 100644
--- a/chrome/browser/autocomplete/autocomplete_edit.cc
+++ b/chrome/browser/autocomplete/autocomplete_edit.cc
@@ -569,6 +569,25 @@ bool AutocompleteEditModel::OnAfterPossibleChange(const std::wstring& new_text,
return true;
}
+// Return true if the suggestion type warrants a TCP/IP preconnection.
+// i.e., it is now highly likely that the user will select the related domain.
+static bool IsPreconnectable(AutocompleteMatch::Type type) {
+ UMA_HISTOGRAM_ENUMERATION("Autocomplete.MatchType", type,
+ AutocompleteMatch::NUM_TYPES);
+ switch (type) {
+ // Matches using the user's default search engine.
+ case AutocompleteMatch::SEARCH_WHAT_YOU_TYPED:
+ case AutocompleteMatch::SEARCH_HISTORY:
+ case AutocompleteMatch::SEARCH_SUGGEST:
+ // A match that uses a non-default search engine (e.g. for tab-to-search).
+ case AutocompleteMatch::SEARCH_OTHER_ENGINE:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
void AutocompleteEditModel::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
@@ -588,7 +607,8 @@ void AutocompleteEditModel::Observe(NotificationType type,
match->fill_into_edit.substr(match->inline_autocomplete_offset);
}
// Warm up DNS Prefetch Cache.
- chrome_browser_net::DnsPrefetchUrl(match->destination_url);
+ chrome_browser_net::DnsPrefetchUrl(match->destination_url,
+ IsPreconnectable(match->type));
// We could prefetch the alternate nav URL, if any, but because there
// can be many of these as a user types an initial series of characters,
// the OS DNS cache could suffer eviction problems for minimal gain.
diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc
index 5d05685..75d74c4 100644
--- a/chrome/browser/browser_main.cc
+++ b/chrome/browser/browser_main.cc
@@ -1077,7 +1077,10 @@ int BrowserMain(const MainFunctionParams& parameters) {
// Initialize and maintain DNS prefetcher module. Also registers an observer
// to clear the host cache when closing incognito mode.
- chrome_browser_net::DnsGlobalInit dns_prefetch(user_prefs, local_state);
+ chrome_browser_net::DnsGlobalInit dns_prefetch(
+ user_prefs,
+ local_state,
+ parsed_command_line.HasSwitch(switches::kEnablePreconnect));
#if defined(OS_WIN)
win_util::ScopedCOMInitializer com_initializer;
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 75a47c2..9c118bb 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -211,7 +211,7 @@ unsigned int BrowserProcessImpl::AddRefModule() {
unsigned int BrowserProcessImpl::ReleaseModule() {
DCHECK(CalledOnValidThread());
- DCHECK(0 != module_ref_count_);
+ DCHECK_NE(0u, module_ref_count_);
module_ref_count_--;
if (0 == module_ref_count_) {
MessageLoop::current()->PostTask(
@@ -459,7 +459,7 @@ void BrowserProcessImpl::CheckForInspectorFiles() {
#if (defined(OS_WIN) || defined(OS_LINUX)) && !defined(OS_CHROMEOS)
void BrowserProcessImpl::StartAutoupdateTimer() {
autoupdate_timer_.Start(
- TimeDelta::FromHours(kUpdateCheckIntervalHours),
+ base::TimeDelta::FromHours(kUpdateCheckIntervalHours),
this,
&BrowserProcessImpl::OnAutoupdateTimer);
}
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc
index 7436750..ca5151b 100644
--- a/chrome/browser/io_thread.cc
+++ b/chrome/browser/io_thread.cc
@@ -110,7 +110,8 @@ void IOThread::InitDnsMaster(
base::TimeDelta max_queue_delay,
size_t max_concurrent,
const chrome_common_net::NameList& hostnames_to_prefetch,
- ListValue* referral_list) {
+ ListValue* referral_list,
+ bool preconnect_enabled) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
message_loop()->PostTask(
FROM_HERE,
@@ -118,7 +119,7 @@ void IOThread::InitDnsMaster(
this,
&IOThread::InitDnsMasterOnIOThread,
prefetching_enabled, max_queue_delay, max_concurrent,
- hostnames_to_prefetch, referral_list));
+ hostnames_to_prefetch, referral_list, preconnect_enabled));
}
void IOThread::ChangedToOnTheRecord() {
@@ -250,14 +251,18 @@ void IOThread::InitDnsMasterOnIOThread(
base::TimeDelta max_queue_delay,
size_t max_concurrent,
chrome_common_net::NameList hostnames_to_prefetch,
- ListValue* referral_list) {
+ ListValue* referral_list,
+ bool preconnect_enabled) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
CHECK(!dns_master_);
chrome_browser_net::EnableDnsPrefetch(prefetching_enabled);
dns_master_ = new chrome_browser_net::DnsMaster(
- globals_->host_resolver, max_queue_delay, max_concurrent);
+ globals_->host_resolver,
+ max_queue_delay,
+ max_concurrent,
+ preconnect_enabled);
dns_master_->AddRef();
DCHECK(!prefetch_observer_);
diff --git a/chrome/browser/io_thread.h b/chrome/browser/io_thread.h
index a2860ac..c624a99 100644
--- a/chrome/browser/io_thread.h
+++ b/chrome/browser/io_thread.h
@@ -53,7 +53,8 @@ class IOThread : public BrowserProcessSubThread {
base::TimeDelta max_queue_delay,
size_t max_concurrent,
const chrome_common_net::NameList& hostnames_to_prefetch,
- ListValue* referral_list);
+ ListValue* referral_list,
+ bool preconnect_enabled);
// Handles changing to On The Record mode. Posts a task for this onto the
// IOThread's message loop.
@@ -72,7 +73,8 @@ class IOThread : public BrowserProcessSubThread {
base::TimeDelta max_queue_delay,
size_t max_concurrent,
chrome_common_net::NameList hostnames_to_prefetch,
- ListValue* referral_list);
+ ListValue* referral_list,
+ bool preconnect_enabled);
void ChangedToOnTheRecordOnIOThread();
diff --git a/chrome/browser/net/dns_global.cc b/chrome/browser/net/dns_global.cc
index 88da39e..f57e029 100644
--- a/chrome/browser/net/dns_global.cc
+++ b/chrome/browser/net/dns_global.cc
@@ -9,6 +9,7 @@
#include "base/singleton.h"
#include "base/stats_counters.h"
+#include "base/stl_util-inl.h"
#include "base/string_util.h"
#include "base/thread.h"
#include "base/waitable_event.h"
@@ -18,6 +19,7 @@
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/io_thread.h"
#include "chrome/browser/net/dns_host_info.h"
+#include "chrome/browser/net/preconnect.h"
#include "chrome/browser/net/referrer.h"
#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
@@ -33,10 +35,10 @@ using base::TimeDelta;
namespace chrome_browser_net {
-static void DnsMotivatedPrefetch(const std::string& hostname,
- DnsHostInfo::ResolutionMotivation motivation);
-static void DnsPrefetchMotivatedList(
- const NameList& hostnames,
+static void DnsMotivatedPrefetch(const net::HostPortPair& hostport,
+ DnsHostInfo::ResolutionMotivation motivation);
+
+static void DnsPrefetchMotivatedList(const NameList& hostnames,
DnsHostInfo::ResolutionMotivation motivation);
static NameList GetDnsPrefetchHostNamesAtStartup(
@@ -48,6 +50,10 @@ const size_t DnsGlobalInit::kMaxPrefetchConcurrentLookups = 8;
// static
const int DnsGlobalInit::kMaxPrefetchQueueingDelayMs = 500;
+// A version number for prefs that are saved. This should be incremented when
+// we change the format so that we discard old data.
+static const int kDnsStartupFormatVersion = 0;
+
//------------------------------------------------------------------------------
// This section contains all the globally accessable API entry points for the
// DNS Prefetching feature.
@@ -117,25 +123,62 @@ static void DnsPrefetchMotivatedList(
}
// This API is used by the autocomplete popup box (where URLs are typed).
-void DnsPrefetchUrl(const GURL& url) {
+void DnsPrefetchUrl(const GURL& url, bool preconnectable) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
if (!dns_prefetch_enabled || NULL == dns_master)
return;
- if (url.is_valid())
- DnsMotivatedPrefetch(url.host(), DnsHostInfo::OMNIBOX_MOTIVATED);
+ if (!url.is_valid())
+ return;
+
+ static std::string last_host;
+ std::string host = url.HostNoBrackets();
+ bool is_new_host_request = (host != last_host);
+ last_host = host;
+
+ // Omnibox tends to call in pairs (just a few milliseconds apart), and we
+ // really don't need to keep resolving a name that often.
+ // TODO(jar): A/B tests could check for perf impact of the early returns.
+ static base::TimeTicks last_prefetch_for_host;
+ base::TimeTicks now = base::TimeTicks::Now();
+ if (!is_new_host_request) {
+ const int kMinPreresolveSeconds(10);
+ if (kMinPreresolveSeconds > (now - last_prefetch_for_host).InSeconds())
+ return;
+ }
+ last_prefetch_for_host = now;
+
+ net::HostPortPair hostport(url.HostNoBrackets(), url.EffectiveIntPort());
+
+ if (dns_master->preconnect_enabled() && preconnectable) {
+ static base::TimeTicks last_keepalive;
+ // TODO(jar): The wild guess of 30 seconds could be tuned/tested, but it
+ // currently is just a guess that most sockets will remain open for at least
+ // 30 seconds.
+ const int kMaxSearchKeepaliveSeconds(30);
+ if ((now - last_keepalive).InSeconds() < kMaxSearchKeepaliveSeconds)
+ return;
+ last_keepalive = now;
+
+ if (Preconnect::PreconnectOnUIThread(hostport))
+ return; // Skip pre-resolution, since we'll open a connection.
+ }
+
+ // Perform at least DNS pre-resolution.
+ // TODO(jar): We could propogate a hostport here instead of a host.
+ DnsMotivatedPrefetch(hostport, DnsHostInfo::OMNIBOX_MOTIVATED);
}
-static void DnsMotivatedPrefetch(const std::string& hostname,
+static void DnsMotivatedPrefetch(const net::HostPortPair& hostport,
DnsHostInfo::ResolutionMotivation motivation) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
- if (!dns_prefetch_enabled || NULL == dns_master || !hostname.size())
+ if (!dns_prefetch_enabled || NULL == dns_master || hostport.host.empty())
return;
ChromeThread::PostTask(
ChromeThread::IO,
FROM_HERE,
NewRunnableMethod(dns_master,
- &DnsMaster::Resolve, hostname, motivation));
+ &DnsMaster::Resolve, hostport, motivation));
}
//------------------------------------------------------------------------------
@@ -152,16 +195,18 @@ static bool AccruePrefetchBenefits(const GURL& referrer,
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
if (!dns_prefetch_enabled || NULL == dns_master)
return false;
- return dns_master->AccruePrefetchBenefits(referrer, navigation_info);
+ return dns_master->AccruePrefetchBenefits(
+ net::HostPortPair(referrer.host(), referrer.EffectiveIntPort()),
+ navigation_info);
}
// When we navigate, we may know in advance some other domains that will need to
// be resolved. This function initiates those side effects.
-static void NavigatingTo(const std::string& host_name) {
+static void NavigatingTo(const net::HostPortPair& hostport) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
if (!dns_prefetch_enabled || NULL == dns_master)
return;
- dns_master->NavigatingTo(host_name);
+ dns_master->NavigatingTo(hostport);
}
// The observer class needs to connect starts and finishes of HTTP network
@@ -173,6 +218,8 @@ typedef std::map<int, DnsHostInfo> ObservedResolutionMap;
// resolutions made by the network stack.
class PrefetchObserver : public net::HostResolver::Observer {
public:
+ typedef std::map<net::HostPortPair, DnsHostInfo> FirstResolutionMap;
+
// net::HostResolver::Observer implementation:
virtual void OnStartResolution(
int request_id,
@@ -194,7 +241,7 @@ class PrefetchObserver : public net::HostResolver::Observer {
// Map of pending resolutions seen by observer.
ObservedResolutionMap resolutions_;
// List of the first N hostname resolutions observed in this run.
- Results first_resolutions_;
+ FirstResolutionMap first_resolutions_;
// The number of hostnames we'll save for prefetching at next startup.
static const size_t kStartupResolutionCount = 10;
};
@@ -212,8 +259,10 @@ void PrefetchObserver::OnStartResolution(
if (request_info.is_speculative())
return; // One of our own requests.
DCHECK_NE(0U, request_info.hostname().length());
+
DnsHostInfo navigation_info;
- navigation_info.SetHostname(request_info.hostname());
+ navigation_info.SetHostname(net::HostPortPair(request_info.hostname(),
+ request_info.port()));
navigation_info.SetStartedState();
// This entry will be deleted either by OnFinishResolutionWithStatus(), or
@@ -233,7 +282,7 @@ void PrefetchObserver::OnFinishResolutionWithStatus(
{
ObservedResolutionMap::iterator it = resolutions_.find(request_id);
if (resolutions_.end() == it) {
- DCHECK(false);
+ NOTREACHED();
return;
}
navigation_info = it->second;
@@ -247,7 +296,7 @@ void PrefetchObserver::OnFinishResolutionWithStatus(
// Handle sub-resource resolutions now that the critical navigational
// resolution has completed. This prevents us from in any way delaying that
// navigational resolution.
- NavigatingTo(request_info.hostname());
+ NavigatingTo(net::HostPortPair(request_info.hostname(), request_info.port()));
if (kStartupResolutionCount <= startup_count || !was_resolved)
return;
@@ -267,7 +316,7 @@ void PrefetchObserver::OnCancelResolution(
// Remove the entry from |resolutions| that was added by OnStartResolution().
ObservedResolutionMap::iterator it = resolutions_.find(request_id);
if (resolutions_.end() == it) {
- DCHECK(false);
+ NOTREACHED();
return;
}
resolutions_.erase(it);
@@ -280,10 +329,9 @@ void PrefetchObserver::StartupListAppend(const DnsHostInfo& navigation_info) {
return;
if (kStartupResolutionCount <= first_resolutions_.size())
return; // Someone just added the last item.
- std::string host_name = navigation_info.hostname();
- if (first_resolutions_.find(host_name) != first_resolutions_.end())
+ if (ContainsKey(first_resolutions_, navigation_info.hostport()))
return; // We already have this hostname listed.
- first_resolutions_[host_name] = navigation_info;
+ first_resolutions_[navigation_info.hostport()] = navigation_info;
}
void PrefetchObserver::GetInitialDnsResolutionList(ListValue* startup_list) {
@@ -291,11 +339,12 @@ void PrefetchObserver::GetInitialDnsResolutionList(ListValue* startup_list) {
DCHECK(startup_list);
startup_list->Clear();
DCHECK_EQ(0u, startup_list->GetSize());
- for (Results::iterator it = first_resolutions_.begin();
+ startup_list->Append(new FundamentalValue(kDnsStartupFormatVersion));
+ for (FirstResolutionMap::iterator it = first_resolutions_.begin();
it != first_resolutions_.end();
- it++) {
- const std::string hostname = it->first;
- startup_list->Append(Value::CreateStringValue(hostname));
+ ++it) {
+ startup_list->Append(new StringValue(it->first.host));
+ startup_list->Append(new FundamentalValue(it->first.port));
}
}
@@ -304,7 +353,7 @@ void PrefetchObserver::DnsGetFirstResolutionsHtml(std::string* output) {
DnsHostInfo::DnsInfoTable resolution_list;
{
- for (Results::iterator it(first_resolutions_.begin());
+ for (FirstResolutionMap::iterator it(first_resolutions_.begin());
it != first_resolutions_.end();
it++) {
resolution_list.push_back(it->second);
@@ -398,7 +447,8 @@ void DnsPrefetchGetHtmlInfo(std::string* output) {
//------------------------------------------------------------------------------
static void InitDnsPrefetch(TimeDelta max_queue_delay, size_t max_concurrent,
- PrefService* user_prefs, PrefService* local_state) {
+ PrefService* user_prefs, PrefService* local_state,
+ bool preconnect_enabled) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
bool prefetching_enabled =
@@ -414,7 +464,7 @@ static void InitDnsPrefetch(TimeDelta max_queue_delay, size_t max_concurrent,
g_browser_process->io_thread()->InitDnsMaster(
prefetching_enabled, max_queue_delay, max_concurrent, hostnames,
- referral_list);
+ referral_list, preconnect_enabled);
}
void FinalizeDnsPrefetchInitialization(
@@ -505,12 +555,23 @@ static NameList GetDnsPrefetchHostNamesAtStartup(PrefService* user_prefs,
ListValue* startup_list =
local_state->GetMutableList(prefs::kDnsStartupPrefetchList);
if (startup_list) {
- for (ListValue::iterator it = startup_list->begin();
- it != startup_list->end();
- it++) {
- std::string hostname;
- (*it)->GetAsString(&hostname);
- hostnames.push_back(hostname);
+ ListValue::iterator it = startup_list->begin();
+ int format_version = -1;
+ if (it != startup_list->end() &&
+ (*it)->GetAsInteger(&format_version) &&
+ format_version == kDnsStartupFormatVersion) {
+ ++it;
+ for (; it != startup_list->end(); ++it) {
+ std::string hostname;
+ if (!(*it)->GetAsString(&hostname))
+ break; // Format incompatibility.
+ int port;
+ if (!(*++it)->GetAsInteger(&port))
+ break; // Format incompatibility.
+
+ // TODO(jar): We sohould accept hostport pairs.
+ hostnames.push_back(hostname);
+ }
}
}
@@ -522,7 +583,7 @@ static NameList GetDnsPrefetchHostNamesAtStartup(PrefService* user_prefs,
for (size_t i = 0; i < tab_start_pref.urls.size(); i++) {
GURL gurl = tab_start_pref.urls[i];
if (gurl.is_valid() && !gurl.host().empty())
- hostnames.push_back(gurl.host());
+ hostnames.push_back(gurl.HostNoBrackets());
}
}
@@ -537,7 +598,8 @@ static NameList GetDnsPrefetchHostNamesAtStartup(PrefService* user_prefs,
// DNS prefetch system.
DnsGlobalInit::DnsGlobalInit(PrefService* user_prefs,
- PrefService* local_state) {
+ PrefService* local_state,
+ bool preconnect_enabled) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
// Set up a field trial to see what disabling DNS pre-resolution does to
// latency of page loads.
@@ -616,8 +678,9 @@ DnsGlobalInit::DnsGlobalInit(PrefService* user_prefs,
DCHECK(!dns_master);
InitDnsPrefetch(max_queueing_delay, max_concurrent, user_prefs,
- local_state);
+ local_state, preconnect_enabled);
}
}
+
} // namespace chrome_browser_net
diff --git a/chrome/browser/net/dns_global.h b/chrome/browser/net/dns_global.h
index bc44b79..639f614 100644
--- a/chrome/browser/net/dns_global.h
+++ b/chrome/browser/net/dns_global.h
@@ -16,6 +16,7 @@
#include "base/field_trial.h"
#include "base/scoped_ptr.h"
+#include "chrome/browser/autocomplete/autocomplete.h"
#include "chrome/browser/net/dns_master.h"
#include "net/base/host_resolver.h"
@@ -42,10 +43,16 @@ net::HostResolver::Observer* CreatePrefetchObserver();
void EnableDnsPrefetch(bool enable);
void RegisterPrefs(PrefService* local_state);
void RegisterUserPrefs(PrefService* user_prefs);
+
// Renderer bundles up list and sends to this browser API via IPC.
void DnsPrefetchList(const NameList& hostnames);
+
// This API is used by the autocomplete popup box (as user types).
-void DnsPrefetchUrl(const GURL& url);
+// This will either preresolve the domain name, or possibly preconnect creating
+// an open TCP/IP connection to the host.
+void DnsPrefetchUrl(const GURL& url, bool preconnectable);
+
+// When displaying info in about:dns, the following API is called.
void DnsPrefetchGetHtmlInfo(std::string* output);
//------------------------------------------------------------------------------
@@ -63,7 +70,8 @@ class DnsGlobalInit {
// of that state. The following is the suggested default time limit.
static const int kMaxPrefetchQueueingDelayMs;
- DnsGlobalInit(PrefService* user_prefs, PrefService* local_state);
+ DnsGlobalInit(PrefService* user_prefs, PrefService* local_state,
+ bool preconnect_enabled);
private:
// Maintain a field trial instance when we do A/B testing.
diff --git a/chrome/browser/net/dns_host_info.cc b/chrome/browser/net/dns_host_info.cc
index 45e088b..2fb626d 100644
--- a/chrome/browser/net/dns_host_info.cc
+++ b/chrome/browser/net/dns_host_info.cc
@@ -32,8 +32,7 @@ void EnableDnsDetailedLog(bool enable) {
int DnsHostInfo::sequence_counter = 1;
-bool DnsHostInfo::NeedsDnsUpdate(const std::string& hostname) {
- DCHECK(hostname == hostname_);
+bool DnsHostInfo::NeedsDnsUpdate() {
switch (state_) {
case PENDING: // Just now created info.
return true;
@@ -48,7 +47,7 @@ bool DnsHostInfo::NeedsDnsUpdate(const std::string& hostname) {
return !IsStillCached(); // See if DNS cache expired.
default:
- DCHECK(false);
+ NOTREACHED();
return false;
}
}
@@ -171,11 +170,11 @@ void DnsHostInfo::SetFinishedState(bool was_resolved) {
DLogResultsStats("DNS HTTP Finished");
}
-void DnsHostInfo::SetHostname(const std::string& hostname) {
- if (hostname != hostname_) {
- DCHECK_EQ(hostname_.size(), 0u); // Not yet initialized.
- hostname_ = hostname;
- }
+void DnsHostInfo::SetHostname(const net::HostPortPair& hostport) {
+ if (hostport_.host.empty()) // Not yet initialized.
+ hostport_ = hostport;
+ else
+ DCHECK(hostport_.Equals(hostport));
}
// IsStillCached() guesses if the DNS cache still has IP data,
@@ -201,7 +200,7 @@ bool DnsHostInfo::IsStillCached() const {
DnsBenefit DnsHostInfo::AccruePrefetchBenefits(DnsHostInfo* navigation_info) {
DCHECK(FINISHED == navigation_info->state_ ||
FINISHED_UNRESOLVED == navigation_info->state_);
- DCHECK_EQ(navigation_info->hostname_, hostname_.data());
+ DCHECK(navigation_info->hostport().Equals(hostport_));
if ((0 == benefits_remaining_.InMilliseconds()) ||
(FOUND != state_ && NO_SUCH_NAME != state_)) {
@@ -221,7 +220,7 @@ DnsBenefit DnsHostInfo::AccruePrefetchBenefits(DnsHostInfo* navigation_info) {
navigation_info->motivation_ = motivation_;
if (LEARNED_REFERAL_MOTIVATED == motivation_ ||
STATIC_REFERAL_MOTIVATED == motivation_)
- navigation_info->referring_hostname_ = referring_hostname_;
+ navigation_info->referring_hostport_ = referring_hostport_;
if (navigation_info->resolve_duration_ > kMaxNonNetworkDnsLookupDuration) {
// Our precache effort didn't help since HTTP stack hit the network.
@@ -256,7 +255,7 @@ void DnsHostInfo::DLogResultsStats(const char* message) const {
<< resolve_duration().InMilliseconds() << "ms\tp="
<< benefits_remaining_.InMilliseconds() << "ms\tseq="
<< sequence_number_
- << "\t" << hostname_;
+ << "\t" << hostport_.ToString();
}
//------------------------------------------------------------------------------
@@ -270,7 +269,7 @@ static std::string RemoveJs(const std::string& text) {
size_t length = output.length();
for (size_t i = 0; i < length; i++) {
char next = output[i];
- if (isalnum(next) || isspace(next) || strchr(".-:", next) != NULL)
+ if (isalnum(next) || isspace(next) || strchr(".-:/", next) != NULL)
continue;
output[i] = '?';
}
@@ -362,7 +361,7 @@ void DnsHostInfo::GetHtmlTable(const DnsInfoTable host_infos,
it != host_infos.end(); it++) {
queue.sample((it->queue_duration_.InMilliseconds()));
StringAppendF(output, row_format,
- RemoveJs(it->hostname_).c_str(),
+ RemoveJs(it->hostport_.ToString()).c_str(),
preresolve.sample((it->benefits_remaining_.InMilliseconds())),
resolve.sample((it->resolve_duration_.InMilliseconds())),
HoursMinutesSeconds(when.sample(
@@ -427,10 +426,10 @@ std::string DnsHostInfo::GetAsciiMotivation() const {
return "n/a";
case STATIC_REFERAL_MOTIVATED:
- return RemoveJs(referring_hostname_) + "*";
+ return RemoveJs(referring_hostport_.ToString()) + "*";
case LEARNED_REFERAL_MOTIVATED:
- return RemoveJs(referring_hostname_);
+ return RemoveJs(referring_hostport_.ToString());
default:
return "";
diff --git a/chrome/browser/net/dns_host_info.h b/chrome/browser/net/dns_host_info.h
index 34160d5..73a0b2e 100644
--- a/chrome/browser/net/dns_host_info.h
+++ b/chrome/browser/net/dns_host_info.h
@@ -15,6 +15,7 @@
#include "base/time.h"
#include "googleurl/src/gurl.h"
+#include "net/base/host_port_pair.h"
namespace chrome_browser_net {
@@ -43,7 +44,7 @@ class DnsHostInfo {
NO_PREFETCH_MOTIVATION, // Browser navigation info (not prefetch related).
// The following involve predictive prefetching, triggered by a navigation.
- // The referring_hostname_ is also set when these are used.
+ // The referring_hostport_ is also set when these are used.
// TODO(jar): Support STATIC_REFERAL_MOTIVATED API and integration.
STATIC_REFERAL_MOTIVATED, // External database suggested this resolution.
LEARNED_REFERAL_MOTIVATED, // Prior navigation taught us this resolution.
@@ -89,7 +90,7 @@ class DnsHostInfo {
// if it would be valuable to attempt to update (prefectch)
// DNS data for hostname. This decision is based
// on how recently we've done DNS prefetching for hostname.
- bool NeedsDnsUpdate(const std::string& hostname);
+ bool NeedsDnsUpdate();
static void set_cache_expiration(base::TimeDelta time);
@@ -105,13 +106,13 @@ class DnsHostInfo {
void SetFinishedState(bool was_resolved);
// Finish initialization. Must only be called once.
- void SetHostname(const std::string& hostname);
+ void SetHostname(const net::HostPortPair& hostport);
bool was_linked() const { return was_linked_; }
- std::string referring_hostname() const { return referring_hostname_; }
- void SetReferringHostname(const std::string& hostname) {
- referring_hostname_ = hostname;
+ net::HostPortPair referring_hostname() const { return referring_hostport_; }
+ void SetReferringHostname(const net::HostPortPair& hostport) {
+ referring_hostport_ = hostport;
}
bool was_found() const { return FOUND == state_; }
@@ -120,10 +121,10 @@ class DnsHostInfo {
return ASSIGNED == state_ || ASSIGNED_BUT_MARKED == state_;
}
bool is_marked_to_delete() const { return ASSIGNED_BUT_MARKED == state_; }
- const std::string hostname() const { return hostname_; }
+ const net::HostPortPair hostport() const { return hostport_; }
- bool HasHostname(const std::string& hostname) const {
- return (hostname == hostname_);
+ bool HasHostname(const net::HostPortPair& hostport) const {
+ return (hostport.Equals(hostport_));
}
base::TimeDelta resolve_duration() const { return resolve_duration_;}
@@ -166,7 +167,7 @@ class DnsHostInfo {
// out of the queue.
DnsProcessingState old_prequeue_state_;
- std::string hostname_; // Hostname for this info.
+ net::HostPortPair hostport_; // Hostname for this info.
// When was last state changed (usually lookup completed).
base::TimeTicks time_;
@@ -189,7 +190,7 @@ class DnsHostInfo {
// If this instance holds data about a navigation, we store the referrer.
// If this instance hold data about a prefetch, and the prefetch was
// instigated by a referrer, we store it here (for use in about:dns).
- std::string referring_hostname_;
+ net::HostPortPair referring_hostport_;
// We put these objects into a std::map, and hence we
// need some "evil" constructors.
diff --git a/chrome/browser/net/dns_host_info_unittest.cc b/chrome/browser/net/dns_host_info_unittest.cc
index 87f64bc..7c145a4 100644
--- a/chrome/browser/net/dns_host_info_unittest.cc
+++ b/chrome/browser/net/dns_host_info_unittest.cc
@@ -22,7 +22,7 @@ typedef chrome_browser_net::DnsHostInfo DnsHostInfo;
TEST(DnsHostInfoTest, StateChangeTest) {
DnsHostInfo info_practice, info;
- std::string hostname1("domain1.com"), hostname2("domain2.com");
+ net::HostPortPair hostname1("domain1.com", 80), hostname2("domain2.com", 443);
// First load DLL, so that their load time won't interfere with tests.
// Some tests involve timing function performance, and DLL time can overwhelm
@@ -36,14 +36,14 @@ TEST(DnsHostInfoTest, StateChangeTest) {
// Complete the construction of real test object.
info.SetHostname(hostname1);
- EXPECT_TRUE(info.NeedsDnsUpdate(hostname1)) << "error in construction state";
+ EXPECT_TRUE(info.NeedsDnsUpdate()) << "error in construction state";
info.SetQueuedState(DnsHostInfo::UNIT_TEST_MOTIVATED);
- EXPECT_FALSE(info.NeedsDnsUpdate(hostname1))
+ EXPECT_FALSE(info.NeedsDnsUpdate())
<< "update needed after being queued";
info.SetAssignedState();
- EXPECT_FALSE(info.NeedsDnsUpdate(hostname1));
+ EXPECT_FALSE(info.NeedsDnsUpdate());
info.SetFoundState();
- EXPECT_FALSE(info.NeedsDnsUpdate(hostname1))
+ EXPECT_FALSE(info.NeedsDnsUpdate())
<< "default expiration time is TOOOOO short";
// Note that time from ASSIGNED to FOUND was VERY short (probably 0ms), so the
@@ -55,31 +55,31 @@ TEST(DnsHostInfoTest, StateChangeTest) {
<< "Non-net time is set too low";
info.set_cache_expiration(TimeDelta::FromMilliseconds(300));
- EXPECT_FALSE(info.NeedsDnsUpdate(hostname1)) << "expiration time not honored";
+ EXPECT_FALSE(info.NeedsDnsUpdate()) << "expiration time not honored";
PlatformThread::Sleep(80); // Not enough time to pass our 300ms mark.
- EXPECT_FALSE(info.NeedsDnsUpdate(hostname1)) << "expiration time not honored";
+ EXPECT_FALSE(info.NeedsDnsUpdate()) << "expiration time not honored";
// That was a nice life when the object was found.... but next time it won't
// be found. We'll sleep for a while, and then come back with not-found.
info.SetQueuedState(DnsHostInfo::UNIT_TEST_MOTIVATED);
info.SetAssignedState();
- EXPECT_FALSE(info.NeedsDnsUpdate(hostname1));
+ EXPECT_FALSE(info.NeedsDnsUpdate());
// Greater than minimal expected network latency on DNS lookup.
PlatformThread::Sleep(25);
info.SetNoSuchNameState();
- EXPECT_FALSE(info.NeedsDnsUpdate(hostname1))
+ EXPECT_FALSE(info.NeedsDnsUpdate())
<< "default expiration time is TOOOOO short";
// Note that now we'll actually utilize an expiration of 300ms,
// since there was detected network activity time during lookup.
// We're assuming the caching just started with our lookup.
PlatformThread::Sleep(80); // Not enough time to pass our 300ms mark.
- EXPECT_FALSE(info.NeedsDnsUpdate(hostname1)) << "expiration time not honored";
+ EXPECT_FALSE(info.NeedsDnsUpdate()) << "expiration time not honored";
// Still not past our 300ms mark (only about 4+2ms)
PlatformThread::Sleep(80);
- EXPECT_FALSE(info.NeedsDnsUpdate(hostname1)) << "expiration time not honored";
+ EXPECT_FALSE(info.NeedsDnsUpdate()) << "expiration time not honored";
PlatformThread::Sleep(150);
- EXPECT_TRUE(info.NeedsDnsUpdate(hostname1)) << "expiration time not honored";
+ EXPECT_TRUE(info.NeedsDnsUpdate()) << "expiration time not honored";
}
// When a system gets "congested" relative to DNS, it means it is doing too many
@@ -93,7 +93,7 @@ TEST(DnsHostInfoTest, StateChangeTest) {
// the state transitions used in such congestion handling.
TEST(DnsHostInfoTest, CongestionResetStateTest) {
DnsHostInfo info;
- std::string hostname1("domain1.com");
+ net::HostPortPair hostname1("domain1.com", 80);
info.SetHostname(hostname1);
info.SetQueuedState(DnsHostInfo::UNIT_TEST_MOTIVATED);
diff --git a/chrome/browser/net/dns_master.cc b/chrome/browser/net/dns_master.cc
index 431842d..11f65d7 100644
--- a/chrome/browser/net/dns_master.cc
+++ b/chrome/browser/net/dns_master.cc
@@ -14,8 +14,10 @@
#include "base/string_util.h"
#include "base/time.h"
#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/net/preconnect.h"
#include "net/base/address_list.h"
#include "net/base/completion_callback.h"
+#include "net/base/host_port_pair.h"
#include "net/base/host_resolver.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
@@ -28,11 +30,11 @@ class DnsMaster::LookupRequest {
public:
LookupRequest(DnsMaster* master,
net::HostResolver* host_resolver,
- const std::string& hostname)
+ const net::HostPortPair& hostport)
: ALLOW_THIS_IN_INITIALIZER_LIST(
net_callback_(this, &LookupRequest::OnLookupFinished)),
master_(master),
- hostname_(hostname),
+ hostport_(hostport),
resolver_(host_resolver) {
}
@@ -41,8 +43,7 @@ class DnsMaster::LookupRequest {
// net:ERR_IO_PENDING ==> Network will callback later with result.
// anything else ==> Host was not found synchronously.
int Start() {
- // Port doesn't really matter.
- net::HostResolver::RequestInfo resolve_info(hostname_, 80);
+ net::HostResolver::RequestInfo resolve_info(hostport_.host, hostport_.port);
// Make a note that this is a speculative resolve request. This allows us
// to separate it from real navigations in the observer's callback, and
@@ -54,7 +55,7 @@ class DnsMaster::LookupRequest {
private:
void OnLookupFinished(int result) {
- master_->OnLookupFinished(this, hostname_, result == net::OK);
+ master_->OnLookupFinished(this, hostport_, result == net::OK);
}
// HostResolver will call us using this callback when resolution is complete.
@@ -62,7 +63,7 @@ class DnsMaster::LookupRequest {
DnsMaster* master_; // Master which started us.
- const std::string hostname_; // Hostname to resolve.
+ const net::HostPortPair hostport_; // Hostname to resolve.
net::SingleRequestHostResolver resolver_;
net::AddressList addresses_;
@@ -70,13 +71,16 @@ class DnsMaster::LookupRequest {
};
DnsMaster::DnsMaster(net::HostResolver* host_resolver,
- TimeDelta max_queue_delay,
- size_t max_concurrent)
- : peak_pending_lookups_(0),
- shutdown_(false),
- max_concurrent_lookups_(max_concurrent),
- max_queue_delay_(max_queue_delay),
- host_resolver_(host_resolver) {
+ base::TimeDelta max_queue_delay,
+ size_t max_concurrent,
+ bool preconnect_enabled)
+ : peak_pending_lookups_(0),
+ shutdown_(false),
+ max_concurrent_lookups_(max_concurrent),
+ max_queue_delay_(max_queue_delay),
+ host_resolver_(host_resolver),
+ preconnect_enabled_(preconnect_enabled) {
+ Referrer::SetUsePreconnectValuations(preconnect_enabled);
}
DnsMaster::~DnsMaster() {
@@ -100,25 +104,25 @@ void DnsMaster::ResolveList(const NameList& hostnames,
NameList::const_iterator it;
for (it = hostnames.begin(); it < hostnames.end(); ++it)
- AppendToResolutionQueue(*it, motivation);
+ // TODO(jar): I should pass port all the way in from renderer.
+ AppendToResolutionQueue(net::HostPortPair(*it, 80), motivation);
}
// Basic Resolve() takes an invidual name, and adds it
// to the queue.
-void DnsMaster::Resolve(const std::string& hostname,
+void DnsMaster::Resolve(const net::HostPortPair& hostport,
DnsHostInfo::ResolutionMotivation motivation) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
- if (0 == hostname.length())
+ if (hostport.host.empty())
return;
- AppendToResolutionQueue(hostname, motivation);
+ AppendToResolutionQueue(hostport, motivation);
}
-bool DnsMaster::AccruePrefetchBenefits(const GURL& referrer,
+bool DnsMaster::AccruePrefetchBenefits(const net::HostPortPair& referrer,
DnsHostInfo* navigation_info) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
- std::string hostname = navigation_info->hostname();
-
- Results::iterator it = results_.find(hostname);
+ net::HostPortPair hostport = navigation_info->hostport();
+ Results::iterator it = results_.find(hostport);
if (it == results_.end()) {
// Use UMA histogram to quantify potential future gains here.
UMA_HISTOGRAM_LONG_TIMES("DNS.UnexpectedResolutionL",
@@ -147,17 +151,15 @@ bool DnsMaster::AccruePrefetchBenefits(const GURL& referrer,
case PREFETCH_NAME_NONEXISTANT:
cache_hits_.push_back(*navigation_info);
if (referrer_based_prefetch) {
- std::string motivating_referrer(
- prefetched_host_info.referring_hostname());
- if (!motivating_referrer.empty()) {
- referrers_[motivating_referrer].AccrueValue(
- navigation_info->benefits_remaining(), hostname);
+ if (!referrer.host.empty()) {
+ referrers_[referrer].AccrueValue(
+ navigation_info->benefits_remaining(), hostport);
}
}
return true;
case PREFETCH_CACHE_EVICTION:
- cache_eviction_map_[hostname] = *navigation_info;
+ cache_eviction_map_[hostport] = *navigation_info;
return false;
case PREFETCH_NO_BENEFIT:
@@ -165,58 +167,74 @@ bool DnsMaster::AccruePrefetchBenefits(const GURL& referrer,
return false;
default:
- DCHECK(false);
+ NOTREACHED();
return false;
}
}
-void DnsMaster::NonlinkNavigation(const GURL& referrer,
+void DnsMaster::NonlinkNavigation(const net::HostPortPair& referring_hostport,
const DnsHostInfo* navigation_info) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
- std::string referring_host = referrer.host();
- if (referring_host.empty() || referring_host == navigation_info->hostname())
+ if (referring_hostport.host.empty() ||
+ referring_hostport.Equals(navigation_info->hostport()))
return;
-
- referrers_[referring_host].SuggestHost(navigation_info->hostname());
+ referrers_[referring_hostport].SuggestHost(navigation_info->hostport());
}
-void DnsMaster::NavigatingTo(const std::string& host_name) {
+void DnsMaster::NavigatingTo(const net::HostPortPair& hostport) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
-
- Referrers::iterator it = referrers_.find(host_name);
+ Referrers::iterator it = referrers_.find(hostport);
if (referrers_.end() == it)
return;
Referrer* referrer = &(it->second);
- for (Referrer::iterator future_host = referrer->begin();
- future_host != referrer->end(); ++future_host) {
+ referrer->IncrementUseCount();
+ for (Referrer::iterator future_hostport = referrer->begin();
+ future_hostport != referrer->end(); ++future_hostport) {
+ if (preconnect_enabled_) {
+ if (future_hostport->second.IsPreconnectWorthDoing()) {
+ Preconnect::PreconnectOnIOThread(future_hostport->first);
+ continue; // No need he pre-resolve DNS.
+ }
+ // Fall through and do DNS pre-resolution.
+ }
DnsHostInfo* queued_info = AppendToResolutionQueue(
- future_host->first,
+ future_hostport->first,
DnsHostInfo::LEARNED_REFERAL_MOTIVATED);
if (queued_info)
- queued_info->SetReferringHostname(host_name);
+ queued_info->SetReferringHostname(hostport);
}
}
// Provide sort order so all .com's are together, etc.
struct RightToLeftStringSorter {
- bool operator()(const std::string& left, const std::string& right) const {
- if (left == right) return true;
- size_t left_already_matched = left.size();
- size_t right_already_matched = right.size();
+ bool operator()(const net::HostPortPair& left,
+ const net::HostPortPair& right) const {
+ return string_compare(left.host, right.host);
+ }
+ bool operator()(const GURL& left,
+ const GURL& right) const {
+ return string_compare(left.host(), right.host());
+ }
+
+ static bool string_compare(const std::string& left_host,
+ const std::string right_host) {
+ if (left_host == right_host) return true;
+ size_t left_already_matched = left_host.size();
+ size_t right_already_matched = right_host.size();
// Ensure both strings have characters.
if (!left_already_matched) return true;
if (!right_already_matched) return false;
// Watch for trailing dot, so we'll always be safe to go one beyond dot.
- if ('.' == left[left.size() - 1]) {
- if ('.' != right[right.size() - 1])
+ if ('.' == left_host[left_already_matched - 1]) {
+ if ('.' != right_host[right_already_matched - 1])
return true;
// Both have dots at end of string.
--left_already_matched;
--right_already_matched;
} else {
- if ('.' == right[right.size() - 1])
+ if ('.' == right_host[right_already_matched - 1])
return false;
}
@@ -225,7 +243,7 @@ struct RightToLeftStringSorter {
if (!right_already_matched) return false;
size_t left_length, right_length;
- size_t left_start = left.find_last_of('.', left_already_matched - 1);
+ size_t left_start = left_host.find_last_of('.', left_already_matched - 1);
if (std::string::npos == left_start) {
left_length = left_already_matched;
left_already_matched = left_start = 0;
@@ -234,7 +252,8 @@ struct RightToLeftStringSorter {
left_already_matched = left_start;
++left_start; // Don't compare the dot.
}
- size_t right_start = right.find_last_of('.', right_already_matched - 1);
+ size_t right_start = right_host.find_last_of('.',
+ right_already_matched - 1);
if (std::string::npos == right_start) {
right_length = right_already_matched;
right_already_matched = right_start = 0;
@@ -244,8 +263,8 @@ struct RightToLeftStringSorter {
++right_start; // Don't compare the dot.
}
- int diff = left.compare(left_start, left.size(),
- right, right_start, right.size());
+ int diff = left_host.compare(left_start, left_host.size(),
+ right_host, right_start, right_host.size());
if (diff > 0) return false;
if (diff < 0) return true;
}
@@ -259,7 +278,8 @@ void DnsMaster::GetHtmlReferrerLists(std::string* output) {
// TODO(jar): Remove any plausible JavaScript from names before displaying.
- typedef std::set<std::string, struct RightToLeftStringSorter> SortedNames;
+ typedef std::set<net::HostPortPair, struct RightToLeftStringSorter>
+ SortedNames;
SortedNames sorted_names;
for (Referrers::iterator it = referrers_.begin();
@@ -267,22 +287,37 @@ void DnsMaster::GetHtmlReferrerLists(std::string* output) {
sorted_names.insert(it->first);
output->append("<br><table border>");
- StringAppendF(output,
- "<tr><th>%s</th><th>%s</th></tr>",
- "Host for Page", "Host(s) in Page<br>(benefits in ms)");
+ output->append(
+ "<tr><th>Host for Page</th>"
+ "<th>Page Load<br>Count</th>"
+ "<th>Subresource<br>Navigations</th>"
+ "<th>Subresource<br>PreConnects</th>"
+ "<th>Expected<br>Connects</th>"
+ "<th>DNS<br>Savings</th>"
+ "<th>Subresource Spec</th></tr>");
for (SortedNames::iterator it = sorted_names.begin();
sorted_names.end() != it; ++it) {
Referrer* referrer = &(referrers_[*it]);
- StringAppendF(output, "<tr align=right><td>%s</td><td>", it->c_str());
- output->append("<table>");
- for (Referrer::iterator future_host = referrer->begin();
- future_host != referrer->end(); ++future_host) {
- StringAppendF(output, "<tr align=right><td>(%dms)</td><td>%s</td></tr>",
- static_cast<int>(future_host->second.latency().InMilliseconds()),
- future_host->first.c_str());
+ bool first_set_of_futures = true;
+ for (Referrer::iterator future_hostport = referrer->begin();
+ future_hostport != referrer->end(); ++future_hostport) {
+ output->append("<tr align=right>");
+ if (first_set_of_futures)
+ StringAppendF(output, "<td rowspan=%d>%s</td><td rowspan=%d>%d</td>",
+ static_cast<int>(referrer->size()),
+ it->ToString().c_str(),
+ static_cast<int>(referrer->size()),
+ static_cast<int>(referrer->use_count()));
+ first_set_of_futures = false;
+ StringAppendF(output,
+ "<td>%d</td><td>%d</td><td>%2.3f</td><td>%dms</td><td>%s</td></tr>",
+ static_cast<int>(future_hostport->second.navigation_count()),
+ static_cast<int>(future_hostport->second.preconnection_count()),
+ static_cast<double>(future_hostport->second.subresource_use_rate()),
+ static_cast<int>(future_hostport->second.latency().InMilliseconds()),
+ future_hostport->first.ToString().c_str());
}
- output->append("</table></td></tr>");
}
output->append("</table>");
}
@@ -297,7 +332,8 @@ void DnsMaster::GetHtmlInfo(std::string* output) {
DnsHostInfo::DnsInfoTable already_cached;
// Get copies of all useful data.
- typedef std::map<std::string, DnsHostInfo, RightToLeftStringSorter> Snapshot;
+ typedef std::map<net::HostPortPair, DnsHostInfo, RightToLeftStringSorter>
+ Snapshot;
Snapshot snapshot;
{
// DnsHostInfo supports value semantics, so we can do a shallow copy.
@@ -357,28 +393,28 @@ void DnsMaster::GetHtmlInfo(std::string* output) {
}
DnsHostInfo* DnsMaster::AppendToResolutionQueue(
- const std::string& hostname,
+ const net::HostPortPair& hostport,
DnsHostInfo::ResolutionMotivation motivation) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
- DCHECK_NE(0u, hostname.length());
+ DCHECK(!hostport.host.empty());
if (shutdown_)
return NULL;
- DnsHostInfo* info = &results_[hostname];
- info->SetHostname(hostname); // Initialize or DCHECK.
+ DnsHostInfo* info = &results_[hostport];
+ info->SetHostname(hostport); // Initialize or DCHECK.
// TODO(jar): I need to discard names that have long since expired.
// Currently we only add to the domain map :-/
- DCHECK(info->HasHostname(hostname));
+ DCHECK(info->HasHostname(hostport));
- if (!info->NeedsDnsUpdate(hostname)) {
+ if (!info->NeedsDnsUpdate()) {
info->DLogResultsStats("DNS PrefetchNotUpdated");
return NULL;
}
info->SetQueuedState(motivation);
- work_queue_.Push(hostname, motivation);
+ work_queue_.Push(hostport, motivation);
StartSomeQueuedResolutions();
return info;
}
@@ -388,9 +424,9 @@ void DnsMaster::StartSomeQueuedResolutions() {
while (!work_queue_.IsEmpty() &&
pending_lookups_.size() < max_concurrent_lookups_) {
- const std::string hostname(work_queue_.Pop());
- DnsHostInfo* info = &results_[hostname];
- DCHECK(info->HasHostname(hostname));
+ const net::HostPortPair hostport(work_queue_.Pop());
+ DnsHostInfo* info = &results_[hostport];
+ DCHECK(info->HasHostname(hostport));
info->SetAssignedState();
if (CongestionControlPerformed(info)) {
@@ -398,7 +434,7 @@ void DnsMaster::StartSomeQueuedResolutions() {
return;
}
- LookupRequest* request = new LookupRequest(this, host_resolver_, hostname);
+ LookupRequest* request = new LookupRequest(this, host_resolver_, hostport);
int status = request->Start();
if (status == net::ERR_IO_PENDING) {
// Will complete asynchronously.
@@ -409,7 +445,7 @@ void DnsMaster::StartSomeQueuedResolutions() {
// Completed synchronously (was already cached by HostResolver), or else
// there was (equivalently) some network error that prevents us from
// finding the name. Status net::OK means it was "found."
- LookupFinished(request, hostname, status == net::OK);
+ LookupFinished(request, hostport, status == net::OK);
delete request;
}
}
@@ -434,10 +470,11 @@ bool DnsMaster::CongestionControlPerformed(DnsHostInfo* info) {
}
void DnsMaster::OnLookupFinished(LookupRequest* request,
- const std::string& hostname, bool found) {
+ const net::HostPortPair& hostport,
+ bool found) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
- LookupFinished(request, hostname, found);
+ LookupFinished(request, hostport, found);
pending_lookups_.erase(request);
delete request;
@@ -445,13 +482,13 @@ void DnsMaster::OnLookupFinished(LookupRequest* request,
}
void DnsMaster::LookupFinished(LookupRequest* request,
- const std::string& hostname,
+ const net::HostPortPair& hostport,
bool found) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
- DnsHostInfo* info = &results_[hostname];
- DCHECK(info->HasHostname(hostname));
+ DnsHostInfo* info = &results_[hostport];
+ DCHECK(info->HasHostname(hostport));
if (info->is_marked_to_delete()) {
- results_.erase(hostname);
+ results_.erase(hostport);
} else {
if (found)
info->SetFoundState();
@@ -471,9 +508,9 @@ void DnsMaster::DiscardAllResults() {
// Try to delete anything in our work queue.
while (!work_queue_.IsEmpty()) {
// Emulate processing cycle as though host was not found.
- std::string hostname = work_queue_.Pop();
- DnsHostInfo* info = &results_[hostname];
- DCHECK(info->HasHostname(hostname));
+ net::HostPortPair hostport = work_queue_.Pop();
+ DnsHostInfo* info = &results_[hostport];
+ DCHECK(info->HasHostname(hostport));
info->SetAssignedState();
info->SetNoSuchNameState();
}
@@ -484,12 +521,12 @@ void DnsMaster::DiscardAllResults() {
// We can't erase anything being worked on.
Results assignees;
for (Results::iterator it = results_.begin(); results_.end() != it; ++it) {
- std::string hostname = it->first;
+ net::HostPortPair hostport(it->first);
DnsHostInfo* info = &it->second;
- DCHECK(info->HasHostname(hostname));
+ DCHECK(info->HasHostname(hostport));
if (info->is_assigned()) {
info->SetPendingDeleteState();
- assignees[hostname] = *info;
+ assignees[hostport] = *info;
}
}
DCHECK(assignees.size() <= max_concurrent_lookups_);
@@ -503,7 +540,7 @@ void DnsMaster::DiscardAllResults() {
void DnsMaster::TrimReferrers() {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
- std::vector<std::string> hosts;
+ std::vector<net::HostPortPair> hosts;
for (Referrers::const_iterator it = referrers_.begin();
it != referrers_.end(); ++it)
hosts.push_back(it->first);
@@ -515,35 +552,57 @@ void DnsMaster::TrimReferrers() {
void DnsMaster::SerializeReferrers(ListValue* referral_list) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
referral_list->Clear();
+ referral_list->Append(new FundamentalValue(DNS_REFERRER_VERSION));
for (Referrers::const_iterator it = referrers_.begin();
it != referrers_.end(); ++it) {
// Serialize the list of subresource names.
Value* subresource_list(it->second.Serialize());
// Create a list for each referer.
- ListValue* motivating_host(new ListValue);
- motivating_host->Append(new StringValue(it->first));
- motivating_host->Append(subresource_list);
+ ListValue* motivator(new ListValue);
+ motivator->Append(new FundamentalValue(it->first.port));
+ motivator->Append(new StringValue(it->first.host));
+ motivator->Append(subresource_list);
- referral_list->Append(motivating_host);
+ referral_list->Append(motivator);
}
}
void DnsMaster::DeserializeReferrers(const ListValue& referral_list) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
- for (size_t i = 0; i < referral_list.GetSize(); ++i) {
- ListValue* motivating_host;
- if (!referral_list.GetList(i, &motivating_host))
- continue;
- std::string motivating_referrer;
- if (!motivating_host->GetString(0, &motivating_referrer))
- continue;
- Value* subresource_list;
- if (!motivating_host->Get(1, &subresource_list))
- continue;
- if (motivating_referrer.empty())
- continue;
- referrers_[motivating_referrer].Deserialize(*subresource_list);
+ int format_version = -1;
+ if (referral_list.GetSize() > 0 &&
+ referral_list.GetInteger(0, &format_version) &&
+ format_version == DNS_REFERRER_VERSION) {
+ for (size_t i = 1; i < referral_list.GetSize(); ++i) {
+ ListValue* motivator;
+ if (!referral_list.GetList(i, &motivator)) {
+ NOTREACHED();
+ continue;
+ }
+ int motivating_port;
+ if (!motivator->GetInteger(0, &motivating_port)) {
+ NOTREACHED();
+ continue;
+ }
+ std::string motivating_host;
+ if (!motivator->GetString(1, &motivating_host)) {
+ NOTREACHED();
+ continue;
+ }
+ if (motivating_host.empty()) {
+ NOTREACHED();
+ continue;
+ }
+
+ Value* subresource_list;
+ if (!motivator->Get(2, &subresource_list)) {
+ NOTREACHED();
+ continue;
+ }
+ net::HostPortPair motivating_hostport(motivating_host, motivating_port);
+ referrers_[motivating_hostport].Deserialize(*subresource_list);
+ }
}
}
@@ -556,17 +615,17 @@ DnsMaster::HostNameQueue::HostNameQueue() {
DnsMaster::HostNameQueue::~HostNameQueue() {
}
-void DnsMaster::HostNameQueue::Push(const std::string& hostname,
+void DnsMaster::HostNameQueue::Push(const net::HostPortPair& hostport,
DnsHostInfo::ResolutionMotivation motivation) {
switch (motivation) {
case DnsHostInfo::STATIC_REFERAL_MOTIVATED:
case DnsHostInfo::LEARNED_REFERAL_MOTIVATED:
case DnsHostInfo::MOUSE_OVER_MOTIVATED:
- rush_queue_.push(hostname);
+ rush_queue_.push(hostport);
break;
default:
- background_queue_.push(hostname);
+ background_queue_.push(hostport);
break;
}
}
@@ -575,16 +634,16 @@ bool DnsMaster::HostNameQueue::IsEmpty() const {
return rush_queue_.empty() && background_queue_.empty();
}
-std::string DnsMaster::HostNameQueue::Pop() {
+net::HostPortPair DnsMaster::HostNameQueue::Pop() {
DCHECK(!IsEmpty());
if (!rush_queue_.empty()) {
- std::string hostname(rush_queue_.front());
+ net::HostPortPair hostport(rush_queue_.front());
rush_queue_.pop();
- return hostname;
+ return hostport;
}
- std::string hostname(background_queue_.front());
+ net::HostPortPair hostport(background_queue_.front());
background_queue_.pop();
- return hostname;
+ return hostport;
}
} // namespace chrome_browser_net
diff --git a/chrome/browser/net/dns_master.h b/chrome/browser/net/dns_master.h
index b665358..8449731 100644
--- a/chrome/browser/net/dns_master.h
+++ b/chrome/browser/net/dns_master.h
@@ -23,10 +23,9 @@
#include "chrome/browser/net/dns_host_info.h"
#include "chrome/browser/net/referrer.h"
#include "chrome/common/net/dns.h"
+#include "net/base/host_port_pair.h"
#include "testing/gtest/include/gtest/gtest_prod.h"
-using base::TimeDelta;
-
namespace net {
class HostResolver;
} // namespace net
@@ -34,16 +33,21 @@ class HostResolver;
namespace chrome_browser_net {
typedef chrome_common_net::NameList NameList;
-typedef std::map<std::string, DnsHostInfo> Results;
+typedef std::map<net::HostPortPair, DnsHostInfo> Results;
// Note that DNS master is not thread safe, and must only be called from
// the IO thread. Failure to do so will result in a DCHECK at runtime.
class DnsMaster : public base::RefCountedThreadSafe<DnsMaster> {
public:
- // |max_concurrent| specifies how many concurrent (parallel) prefetches will
+ // A version number for prefs that are saved. This should be incremented when
+ // we change the format so that we discard old data.
+ enum {DNS_REFERRER_VERSION = 0 };
+
+// |max_concurrent| specifies how many concurrent (parallel) prefetches will
// be performed. Host lookups will be issued through |host_resolver|.
DnsMaster(net::HostResolver* host_resolver,
- TimeDelta max_queue_delay_ms, size_t max_concurrent);
+ base::TimeDelta max_queue_delay_ms, size_t max_concurrent,
+ bool preconnect_enabled);
// Cancel pending requests and prevent new ones from being made.
void Shutdown();
@@ -58,21 +62,21 @@ class DnsMaster : public base::RefCountedThreadSafe<DnsMaster> {
// Add hostname(s) to the queue for processing.
void ResolveList(const NameList& hostnames,
DnsHostInfo::ResolutionMotivation motivation);
- void Resolve(const std::string& hostname,
+ void Resolve(const net::HostPortPair& hostport,
DnsHostInfo::ResolutionMotivation motivation);
// Get latency benefit of the prefetch that we are navigating to.
- bool AccruePrefetchBenefits(const GURL& referrer,
+ bool AccruePrefetchBenefits(const net::HostPortPair& referrer,
DnsHostInfo* navigation_info);
// Instigate prefetch of any domains we predict will be needed after this
// navigation.
- void NavigatingTo(const std::string& host_name);
+ void NavigatingTo(const net::HostPortPair& hostport);
// Record details of a navigation so that we can preresolve the host name
// ahead of time the next time the users navigates to the indicated host.
// TODO(eroman): can this be a const& instead?
- void NonlinkNavigation(const GURL& referrer,
+ void NonlinkNavigation(const net::HostPortPair& referrer,
const DnsHostInfo* navigation_info);
// Dump HTML table containing list of referrers for about:dns.
@@ -106,6 +110,9 @@ class DnsMaster : public base::RefCountedThreadSafe<DnsMaster> {
// For unit test code only.
size_t max_concurrent_lookups() const { return max_concurrent_lookups_; }
+ // Flag setting to use preconnection instead of just DNS pre-fetching.
+ bool preconnect_enabled() const { return preconnect_enabled_; }
+
private:
friend class base::RefCountedThreadSafe<DnsMaster>;
FRIEND_TEST(DnsMasterTest, BenefitLookupTest);
@@ -133,39 +140,41 @@ class DnsMaster : public base::RefCountedThreadSafe<DnsMaster> {
public:
HostNameQueue();
~HostNameQueue();
- void Push(const std::string& hostname,
+ void Push(const net::HostPortPair& hostport,
DnsHostInfo::ResolutionMotivation motivation);
bool IsEmpty() const;
- std::string Pop();
+ net::HostPortPair Pop();
private:
// The names in the queue that should be serviced (popped) ASAP.
- std::queue<std::string> rush_queue_;
+ std::queue<net::HostPortPair> rush_queue_;
// The names in the queue that should only be serviced when rush_queue is
// empty.
- std::queue<std::string> background_queue_;
+ std::queue<net::HostPortPair> background_queue_;
DISALLOW_COPY_AND_ASSIGN(HostNameQueue);
};
- // A map that is keyed with the hostnames that we've learned were the cause
- // of loading additional hostnames. The list of additional hostnames in held
- // in a Referrer instance, which is found in this type.
- typedef std::map<std::string, Referrer> Referrers;
+ // A map that is keyed with the host/port that we've learned were the cause
+ // of loading additional URLs. The list of additional targets is held
+ // in a Referrer instance, which is a value in this map.
+ typedef std::map<net::HostPortPair, Referrer> Referrers;
// Only for testing. Returns true if hostname has been successfully resolved
// (name found).
- bool WasFound(const std::string& hostname) {
- return (results_.find(hostname) != results_.end()) &&
- results_[hostname].was_found();
+ bool WasFound(const net::HostPortPair& hostport) const {
+ Results::const_iterator it(results_.find(hostport));
+ return (it != results_.end()) &&
+ it->second.was_found();
}
// Only for testing. Return how long was the resolution
// or DnsHostInfo::kNullDuration if it hasn't been resolved yet.
- base::TimeDelta GetResolutionDuration(const std::string& hostname) {
- if (results_.find(hostname) == results_.end())
+ base::TimeDelta GetResolutionDuration(const net::HostPortPair& hostport) {
+
+ if (results_.find(hostport) == results_.end())
return DnsHostInfo::kNullDuration;
- return results_[hostname].resolve_duration();
+ return results_[hostport].resolve_duration();
}
// Only for testing;
@@ -173,16 +182,15 @@ class DnsMaster : public base::RefCountedThreadSafe<DnsMaster> {
// Access method for use by async lookup request to pass resolution result.
void OnLookupFinished(LookupRequest* request,
- const std::string& hostname, bool found);
+ const net::HostPortPair& hostport, bool found);
// Underlying method for both async and synchronous lookup to update state.
void LookupFinished(LookupRequest* request,
- const std::string& hostname,
- bool found);
+ const net::HostPortPair& hostport, bool found);
// Queue hostname for resolution. If queueing was done, return the pointer
// to the queued instance, otherwise return NULL.
- DnsHostInfo* AppendToResolutionQueue(const std::string& hostname,
+ DnsHostInfo* AppendToResolutionQueue(const net::HostPortPair& hostport,
DnsHostInfo::ResolutionMotivation motivation);
// Check to see if too much queuing delay has been noted for the given info,
@@ -209,7 +217,7 @@ class DnsMaster : public base::RefCountedThreadSafe<DnsMaster> {
// results_ contains information for existing/prior prefetches.
Results results_;
- // For each hostname that we might navigate to (that we've "learned about")
+ // For each URL that we might navigate to (that we've "learned about")
// we have a Referrer list. Each Referrer list has all hostnames we need to
// pre-resolve when there is a navigation to the orginial hostname.
Referrers referrers_;
@@ -233,11 +241,15 @@ class DnsMaster : public base::RefCountedThreadSafe<DnsMaster> {
// The maximum queueing delay that is acceptable before we enter congestion
// reduction mode, and discard all queued (but not yet assigned) resolutions.
- const TimeDelta max_queue_delay_;
+ const base::TimeDelta max_queue_delay_;
// The host resovler we warm DNS entries for.
scoped_refptr<net::HostResolver> host_resolver_;
+ // Are we currently using preconnection, rather than just DNS resolution, for
+ // subresources and omni-box search URLs.
+ bool preconnect_enabled_;
+
DISALLOW_COPY_AND_ASSIGN(DnsMaster);
};
diff --git a/chrome/browser/net/dns_master_unittest.cc b/chrome/browser/net/dns_master_unittest.cc
index 2f83d30..2699b1f 100644
--- a/chrome/browser/net/dns_master_unittest.cc
+++ b/chrome/browser/net/dns_master_unittest.cc
@@ -41,7 +41,8 @@ class WaitForResolutionHelper {
void Run() {
for (NameList::const_iterator i = hosts_.begin(); i != hosts_.end(); ++i)
- if (master_->GetResolutionDuration(*i) == DnsHostInfo::kNullDuration)
+ if (master_->GetResolutionDuration(net::HostPortPair(*i, 80)) ==
+ DnsHostInfo::kNullDuration)
return; // We don't have resolution for that host.
// When all hostnames have been resolved, exit the loop.
@@ -109,19 +110,22 @@ class DnsMasterTest : public testing::Test {
TEST_F(DnsMasterTest, StartupShutdownTest) {
scoped_refptr<DnsMaster> testing_master = new DnsMaster(host_resolver_,
default_max_queueing_delay_,
- DnsGlobalInit::kMaxPrefetchConcurrentLookups);
+ DnsGlobalInit::kMaxPrefetchConcurrentLookups,
+ false);
testing_master->Shutdown();
}
TEST_F(DnsMasterTest, BenefitLookupTest) {
- scoped_refptr<DnsMaster> testing_master = new DnsMaster(host_resolver_,
+ scoped_refptr<DnsMaster> testing_master = new DnsMaster(
+ host_resolver_,
default_max_queueing_delay_,
- DnsGlobalInit::kMaxPrefetchConcurrentLookups);
+ DnsGlobalInit::kMaxPrefetchConcurrentLookups,
+ false);
- std::string goog("www.google.com"),
- goog2("gmail.google.com.com"),
- goog3("mail.google.com"),
- goog4("gmail.com");
+ net::HostPortPair goog("www.google.com", 80),
+ goog2("gmail.google.com.com", 80),
+ goog3("mail.google.com", 80),
+ goog4("gmail.com", 80);
DnsHostInfo goog_info, goog2_info, goog3_info, goog4_info;
// Simulate getting similar names from a network observer
@@ -141,10 +145,10 @@ TEST_F(DnsMasterTest, BenefitLookupTest) {
goog4_info.SetFinishedState(true);
NameList names;
- names.insert(names.end(), goog);
- names.insert(names.end(), goog2);
- names.insert(names.end(), goog3);
- names.insert(names.end(), goog4);
+ names.push_back(goog.host);
+ names.push_back(goog2.host);
+ names.push_back(goog3.host);
+ names.push_back(goog4.host);
testing_master->ResolveList(names, DnsHostInfo::PAGE_SCAN_MOTIVATED);
@@ -158,18 +162,20 @@ TEST_F(DnsMasterTest, BenefitLookupTest) {
// With the mock DNS, each of these should have taken some time, and hence
// shown a benefit (i.e., prefetch cost more than network access time).
+ net::HostPortPair referer; // Null host.
+
// Simulate actual navigation, and acrue the benefit for "helping" the DNS
// part of the navigation.
- EXPECT_TRUE(testing_master->AccruePrefetchBenefits(GURL(), &goog_info));
- EXPECT_TRUE(testing_master->AccruePrefetchBenefits(GURL(), &goog2_info));
- EXPECT_TRUE(testing_master->AccruePrefetchBenefits(GURL(), &goog3_info));
- EXPECT_TRUE(testing_master->AccruePrefetchBenefits(GURL(), &goog4_info));
+ EXPECT_TRUE(testing_master->AccruePrefetchBenefits(referer, &goog_info));
+ EXPECT_TRUE(testing_master->AccruePrefetchBenefits(referer, &goog2_info));
+ EXPECT_TRUE(testing_master->AccruePrefetchBenefits(referer, &goog3_info));
+ EXPECT_TRUE(testing_master->AccruePrefetchBenefits(referer, &goog4_info));
// Benefits can ONLY be reported once (for the first navigation).
- EXPECT_FALSE(testing_master->AccruePrefetchBenefits(GURL(), &goog_info));
- EXPECT_FALSE(testing_master->AccruePrefetchBenefits(GURL(), &goog2_info));
- EXPECT_FALSE(testing_master->AccruePrefetchBenefits(GURL(), &goog3_info));
- EXPECT_FALSE(testing_master->AccruePrefetchBenefits(GURL(), &goog4_info));
+ EXPECT_FALSE(testing_master->AccruePrefetchBenefits(referer, &goog_info));
+ EXPECT_FALSE(testing_master->AccruePrefetchBenefits(referer, &goog2_info));
+ EXPECT_FALSE(testing_master->AccruePrefetchBenefits(referer, &goog3_info));
+ EXPECT_FALSE(testing_master->AccruePrefetchBenefits(referer, &goog4_info));
testing_master->Shutdown();
}
@@ -181,11 +187,12 @@ TEST_F(DnsMasterTest, ShutdownWhenResolutionIsPendingTest) {
scoped_refptr<DnsMaster> testing_master = new DnsMaster(host_resolver_,
default_max_queueing_delay_,
- DnsGlobalInit::kMaxPrefetchConcurrentLookups);
+ DnsGlobalInit::kMaxPrefetchConcurrentLookups,
+ false);
- std::string localhost("127.0.0.1");
+ net::HostPortPair localhost("127.0.0.1", 80);
NameList names;
- names.insert(names.end(), localhost);
+ names.push_back(localhost.host);
testing_master->ResolveList(names, DnsHostInfo::PAGE_SCAN_MOTIVATED);
@@ -205,12 +212,13 @@ TEST_F(DnsMasterTest, ShutdownWhenResolutionIsPendingTest) {
TEST_F(DnsMasterTest, SingleLookupTest) {
scoped_refptr<DnsMaster> testing_master = new DnsMaster(host_resolver_,
default_max_queueing_delay_,
- DnsGlobalInit::kMaxPrefetchConcurrentLookups);
+ DnsGlobalInit::kMaxPrefetchConcurrentLookups,
+ false);
- std::string goog("www.google.com");
+ net::HostPortPair goog("www.google.com", 80);
NameList names;
- names.insert(names.end(), goog);
+ names.push_back(goog.host);
// Try to flood the master with many concurrent requests.
for (int i = 0; i < 10; i++)
@@ -235,23 +243,24 @@ TEST_F(DnsMasterTest, ConcurrentLookupTest) {
scoped_refptr<DnsMaster> testing_master = new DnsMaster(host_resolver_,
default_max_queueing_delay_,
- DnsGlobalInit::kMaxPrefetchConcurrentLookups);
+ DnsGlobalInit::kMaxPrefetchConcurrentLookups,
+ false);
- std::string goog("www.google.com"),
- goog2("gmail.google.com.com"),
- goog3("mail.google.com"),
- goog4("gmail.com");
- std::string bad1("bad1.notfound"),
- bad2("bad2.notfound");
+ net::HostPortPair goog("www.google.com", 80),
+ goog2("gmail.google.com.com", 80),
+ goog3("mail.google.com", 80),
+ goog4("gmail.com", 80);
+ net::HostPortPair bad1("bad1.notfound", 80),
+ bad2("bad2.notfound", 80);
NameList names;
- names.insert(names.end(), goog);
- names.insert(names.end(), goog3);
- names.insert(names.end(), bad1);
- names.insert(names.end(), goog2);
- names.insert(names.end(), bad2);
- names.insert(names.end(), goog4);
- names.insert(names.end(), goog);
+ names.push_back(goog.host);
+ names.push_back(goog3.host);
+ names.push_back(bad1.host);
+ names.push_back(goog2.host);
+ names.push_back(bad2.host);
+ names.push_back(goog4.host);
+ names.push_back(goog.host);
// Try to flood the master with many concurrent requests.
for (int i = 0; i < 10; i++)
@@ -282,9 +291,11 @@ TEST_F(DnsMasterTest, ConcurrentLookupTest) {
TEST_F(DnsMasterTest, MassiveConcurrentLookupTest) {
host_resolver_->rules()->AddSimulatedFailure("*.notfound");
- scoped_refptr<DnsMaster> testing_master = new DnsMaster(host_resolver_,
+ scoped_refptr<DnsMaster> testing_master = new DnsMaster(
+ host_resolver_,
default_max_queueing_delay_,
- DnsGlobalInit::kMaxPrefetchConcurrentLookups);
+ DnsGlobalInit::kMaxPrefetchConcurrentLookups,
+ false);
NameList names;
for (int i = 0; i < 100; i++)
@@ -311,25 +322,39 @@ TEST_F(DnsMasterTest, MassiveConcurrentLookupTest) {
// Return a motivation_list if we can find one for the given motivating_host (or
// NULL if a match is not found).
-static ListValue* FindSerializationMotivation(const std::string& motivation,
- const ListValue& referral_list) {
+static ListValue* FindSerializationMotivation(
+ const net::HostPortPair& motivation, const ListValue& referral_list) {
+ CHECK_LT(0u, referral_list.GetSize()); // Room for version.
+ int format_version = -1;
+ CHECK(referral_list.GetInteger(0, &format_version));
+ CHECK_EQ(DnsMaster::DNS_REFERRER_VERSION, format_version);
ListValue* motivation_list(NULL);
- for (size_t i = 0; i < referral_list.GetSize(); ++i) {
+ for (size_t i = 1; i < referral_list.GetSize(); ++i) {
referral_list.GetList(i, &motivation_list);
- std::string existing_motivation;
- EXPECT_TRUE(motivation_list->GetString(i, &existing_motivation));
- if (existing_motivation == motivation)
- break;
- motivation_list = NULL;
+ std::string existing_host;
+ int existing_port;
+ EXPECT_TRUE(motivation_list->GetInteger(0, &existing_port));
+ EXPECT_TRUE(motivation_list->GetString(1, &existing_host));
+ if (motivation.host == existing_host && motivation.port == existing_port)
+ return motivation_list;
}
- return motivation_list;
+ return NULL;
+}
+
+// Create a new empty serialization list.
+static ListValue* NewEmptySerializationList() {
+ ListValue* list = new ListValue;
+ list->Append(new FundamentalValue(DnsMaster::DNS_REFERRER_VERSION));
+ return list;
}
// Add a motivating_host and a subresource_host to a serialized list, using
// this given latency. This is a helper function for quickly building these
// lists.
-static void AddToSerializedList(const std::string& motivation,
- const std::string& subresource, int latency,
+static void AddToSerializedList(const net::HostPortPair& motivation,
+ const net::HostPortPair& subresource,
+ int latency,
+ double rate,
ListValue* referral_list ) {
// Find the motivation if it is already used.
ListValue* motivation_list = FindSerializationMotivation(motivation,
@@ -337,7 +362,8 @@ static void AddToSerializedList(const std::string& motivation,
if (!motivation_list) {
// This is the first mention of this motivation, so build a list.
motivation_list = new ListValue;
- motivation_list->Append(new StringValue(motivation));
+ motivation_list->Append(new FundamentalValue(motivation.port));
+ motivation_list->Append(new StringValue(motivation.host));
// Provide empty subresource list.
motivation_list->Append(new ListValue());
@@ -346,41 +372,48 @@ static void AddToSerializedList(const std::string& motivation,
}
ListValue* subresource_list(NULL);
- EXPECT_TRUE(motivation_list->GetList(1, &subresource_list));
+ // 0 == port; 1 == host; 2 == subresource_list.
+ EXPECT_TRUE(motivation_list->GetList(2, &subresource_list));
// We won't bother to check for the subresource being there already. Worst
// case, during deserialization, the latency value we supply plus the
// existing value(s) will be added to the referrer.
- subresource_list->Append(new StringValue(subresource));
+
+ subresource_list->Append(new FundamentalValue(subresource.port));
+ subresource_list->Append(new StringValue(subresource.host));
subresource_list->Append(new FundamentalValue(latency));
+ subresource_list->Append(new FundamentalValue(rate));
}
static const int kLatencyNotFound = -1;
-// For a given motivation_hostname, and subresource_hostname, find what latency
-// is currently listed. This assume a well formed serialization, which has
-// at most one such entry for any pair of names. If no such pair is found, then
-// return kLatencyNotFound.
-int GetLatencyFromSerialization(const std::string& motivation,
- const std::string& subresource,
- const ListValue& referral_list) {
+// For a given motivation, and subresource, find what latency is currently
+// listed. This assume a well formed serialization, which has at most one such
+// entry for any pair of names. If no such pair is found, then return false.
+// Data is written into rate and latency arguments.
+static bool GetDataFromSerialization(const net::HostPortPair& motivation,
+ const net::HostPortPair& subresource,
+ const ListValue& referral_list,
+ double* rate,
+ int* latency) {
ListValue* motivation_list = FindSerializationMotivation(motivation,
referral_list);
if (!motivation_list)
- return kLatencyNotFound;
+ return false;
ListValue* subresource_list;
- EXPECT_TRUE(motivation_list->GetList(1, &subresource_list));
- for (size_t i = 0; i < subresource_list->GetSize(); ++i) {
- std::string subresource_name;
- EXPECT_TRUE(subresource_list->GetString(i, &subresource_name));
- if (subresource_name == subresource) {
- int latency;
- EXPECT_TRUE(subresource_list->GetInteger(i + 1, &latency));
- return latency;
+ EXPECT_TRUE(motivation_list->GetList(2, &subresource_list));
+ for (size_t i = 0; i < subresource_list->GetSize();) {
+ std::string host;
+ int port;
+ EXPECT_TRUE(subresource_list->GetInteger(i++, &port));
+ EXPECT_TRUE(subresource_list->GetString(i++, &host));
+ EXPECT_TRUE(subresource_list->GetInteger(i++, latency));
+ EXPECT_TRUE(subresource_list->GetReal(i++, rate));
+ if (subresource.host == host && subresource.port == port) {
+ return true;
}
- ++i; // Skip latency value.
}
- return kLatencyNotFound;
+ return false;
}
//------------------------------------------------------------------------------
@@ -389,12 +422,14 @@ int GetLatencyFromSerialization(const std::string& motivation,
TEST_F(DnsMasterTest, ReferrerSerializationNilTest) {
scoped_refptr<DnsMaster> master = new DnsMaster(host_resolver_,
default_max_queueing_delay_,
- DnsGlobalInit::kMaxPrefetchConcurrentLookups);
- ListValue referral_list;
- master->SerializeReferrers(&referral_list);
- EXPECT_EQ(0U, referral_list.GetSize());
- EXPECT_EQ(kLatencyNotFound, GetLatencyFromSerialization("a.com", "b.com",
- referral_list));
+ DnsGlobalInit::kMaxPrefetchConcurrentLookups,
+ false);
+ scoped_ptr<ListValue> referral_list(NewEmptySerializationList());
+ master->SerializeReferrers(referral_list.get());
+ EXPECT_EQ(1U, referral_list->GetSize());
+ EXPECT_FALSE(GetDataFromSerialization(
+ net::HostPortPair("a.com", 79), net::HostPortPair("b.com", 78),
+ *referral_list.get(), NULL, NULL));
master->Shutdown();
}
@@ -405,23 +440,29 @@ TEST_F(DnsMasterTest, ReferrerSerializationNilTest) {
TEST_F(DnsMasterTest, ReferrerSerializationSingleReferrerTest) {
scoped_refptr<DnsMaster> master = new DnsMaster(host_resolver_,
default_max_queueing_delay_,
- DnsGlobalInit::kMaxPrefetchConcurrentLookups);
- std::string motivation_hostname = "www.google.com";
- std::string subresource_hostname = "icons.google.com";
+ DnsGlobalInit::kMaxPrefetchConcurrentLookups,
+ false);
+ const net::HostPortPair motivation_hostport("www.google.com", 91);
+ const net::HostPortPair subresource_hostport("icons.google.com", 90);
const int kLatency = 3;
- ListValue referral_list;
+ const double kRate = 23.4;
+ scoped_ptr<ListValue> referral_list(NewEmptySerializationList());
- AddToSerializedList(motivation_hostname, subresource_hostname, kLatency,
- &referral_list);
+ AddToSerializedList(motivation_hostport, subresource_hostport,
+ kLatency, kRate, referral_list.get());
- master->DeserializeReferrers(referral_list);
+ master->DeserializeReferrers(*referral_list.get());
ListValue recovered_referral_list;
master->SerializeReferrers(&recovered_referral_list);
- EXPECT_EQ(1U, recovered_referral_list.GetSize());
- EXPECT_EQ(kLatency, GetLatencyFromSerialization(motivation_hostname,
- subresource_hostname,
- recovered_referral_list));
+ EXPECT_EQ(2U, recovered_referral_list.GetSize());
+ int latency;
+ double rate;
+ EXPECT_TRUE(GetDataFromSerialization(
+ motivation_hostport, subresource_hostport, recovered_referral_list, &rate,
+ &latency));
+ EXPECT_EQ(rate, kRate);
+ EXPECT_EQ(latency, kLatency);
master->Shutdown();
}
@@ -430,72 +471,104 @@ TEST_F(DnsMasterTest, ReferrerSerializationSingleReferrerTest) {
TEST_F(DnsMasterTest, ReferrerSerializationTrimTest) {
scoped_refptr<DnsMaster> master = new DnsMaster(host_resolver_,
default_max_queueing_delay_,
- DnsGlobalInit::kMaxPrefetchConcurrentLookups);
- std::string motivation_hostname = "www.google.com";
- std::string icon_subresource_hostname = "icons.google.com";
- std::string img_subresource_hostname = "img.google.com";
- ListValue referral_list;
-
- AddToSerializedList(motivation_hostname, icon_subresource_hostname, 10,
- &referral_list);
- AddToSerializedList(motivation_hostname, img_subresource_hostname, 3,
- &referral_list);
-
- master->DeserializeReferrers(referral_list);
+ DnsGlobalInit::kMaxPrefetchConcurrentLookups,
+ false);
+ net::HostPortPair motivation_hostport("www.google.com", 110);
+
+ net::HostPortPair icon_subresource_hostport("icons.google.com", 111);
+ const int kLatencyIcon = 10;
+ const double kRateIcon = 0.; // User low rate, so latency will dominate.
+ net::HostPortPair img_subresource_hostport("img.google.com", 118);
+ const int kLatencyImg = 3;
+ const double kRateImg = 0.;
+
+ scoped_ptr<ListValue> referral_list(NewEmptySerializationList());
+ AddToSerializedList(
+ motivation_hostport, icon_subresource_hostport,
+ kLatencyIcon, kRateIcon, referral_list.get());
+ AddToSerializedList(
+ motivation_hostport, img_subresource_hostport,
+ kLatencyImg, kRateImg, referral_list.get());
+
+ master->DeserializeReferrers(*referral_list.get());
ListValue recovered_referral_list;
master->SerializeReferrers(&recovered_referral_list);
- EXPECT_EQ(1U, recovered_referral_list.GetSize());
- EXPECT_EQ(10, GetLatencyFromSerialization(motivation_hostname,
- icon_subresource_hostname,
- recovered_referral_list));
- EXPECT_EQ(3, GetLatencyFromSerialization(motivation_hostname,
- img_subresource_hostname,
- recovered_referral_list));
+ EXPECT_EQ(2U, recovered_referral_list.GetSize());
+ int latency;
+ double rate;
+ EXPECT_TRUE(GetDataFromSerialization(
+ motivation_hostport, icon_subresource_hostport, recovered_referral_list,
+ &rate, &latency));
+ EXPECT_EQ(latency, kLatencyIcon);
+ EXPECT_EQ(rate, kRateIcon);
+
+ EXPECT_TRUE(GetDataFromSerialization(
+ motivation_hostport, img_subresource_hostport, recovered_referral_list,
+ &rate, &latency));
+ EXPECT_EQ(latency, kLatencyImg);
+ EXPECT_EQ(rate, kRateImg);
// Each time we Trim, the latency figures should reduce by a factor of two,
// until they both are 0, an then a trim will delete the whole entry.
master->TrimReferrers();
master->SerializeReferrers(&recovered_referral_list);
- EXPECT_EQ(1U, recovered_referral_list.GetSize());
- EXPECT_EQ(5, GetLatencyFromSerialization(motivation_hostname,
- icon_subresource_hostname,
- recovered_referral_list));
- EXPECT_EQ(1, GetLatencyFromSerialization(motivation_hostname,
- img_subresource_hostname,
- recovered_referral_list));
+ EXPECT_EQ(2U, recovered_referral_list.GetSize());
+ EXPECT_TRUE(GetDataFromSerialization(
+ motivation_hostport, icon_subresource_hostport, recovered_referral_list,
+ &rate, &latency));
+ EXPECT_EQ(latency, kLatencyIcon / 2);
+ EXPECT_EQ(rate, kRateIcon);
+
+ EXPECT_TRUE(GetDataFromSerialization(
+ motivation_hostport, img_subresource_hostport, recovered_referral_list,
+ &rate, &latency));
+ EXPECT_EQ(latency, kLatencyImg / 2);
+ EXPECT_EQ(rate, kRateImg);
master->TrimReferrers();
master->SerializeReferrers(&recovered_referral_list);
- EXPECT_EQ(1U, recovered_referral_list.GetSize());
- EXPECT_EQ(2, GetLatencyFromSerialization(motivation_hostname,
- icon_subresource_hostname,
- recovered_referral_list));
- EXPECT_EQ(0, GetLatencyFromSerialization(motivation_hostname,
- img_subresource_hostname,
- recovered_referral_list));
+ EXPECT_EQ(2U, recovered_referral_list.GetSize());
+ EXPECT_TRUE(GetDataFromSerialization(
+ motivation_hostport, icon_subresource_hostport, recovered_referral_list,
+ &rate, &latency));
+ EXPECT_EQ(latency, kLatencyIcon / 4);
+ EXPECT_EQ(rate, kRateIcon);
+ // Img is down to zero, but we don't delete it yet.
+ EXPECT_TRUE(GetDataFromSerialization(
+ motivation_hostport, img_subresource_hostport, recovered_referral_list,
+ &rate, &latency));
+ EXPECT_EQ(kLatencyImg / 4, 0);
+ EXPECT_EQ(latency, kLatencyImg / 4);
+ EXPECT_EQ(rate, kRateImg);
master->TrimReferrers();
master->SerializeReferrers(&recovered_referral_list);
- EXPECT_EQ(1U, recovered_referral_list.GetSize());
- EXPECT_EQ(1, GetLatencyFromSerialization(motivation_hostname,
- icon_subresource_hostname,
- recovered_referral_list));
- EXPECT_EQ(0, GetLatencyFromSerialization(motivation_hostname,
- img_subresource_hostname,
- recovered_referral_list));
+ EXPECT_EQ(2U, recovered_referral_list.GetSize());
+ EXPECT_TRUE(GetDataFromSerialization(
+ motivation_hostport, icon_subresource_hostport, recovered_referral_list,
+ &rate, &latency));
+ EXPECT_EQ(latency, kLatencyIcon / 8);
+ EXPECT_EQ(rate, kRateIcon);
+
+ // Img is down to zero, but we don't delete it yet.
+ EXPECT_TRUE(GetDataFromSerialization(
+ motivation_hostport, img_subresource_hostport, recovered_referral_list,
+ &rate, &latency));
+ EXPECT_EQ(kLatencyImg / 8, 0);
+ EXPECT_EQ(latency, kLatencyImg / 8);
+ EXPECT_EQ(rate, kRateImg);
master->TrimReferrers();
master->SerializeReferrers(&recovered_referral_list);
- EXPECT_EQ(0U, recovered_referral_list.GetSize());
- EXPECT_EQ(kLatencyNotFound,
- GetLatencyFromSerialization(motivation_hostname,
- icon_subresource_hostname,
- recovered_referral_list));
- EXPECT_EQ(kLatencyNotFound,
- GetLatencyFromSerialization(motivation_hostname,
- img_subresource_hostname,
- recovered_referral_list));
+ // Icon is also trimmed away, so entire set gets discarded.
+ EXPECT_EQ(1U, recovered_referral_list.GetSize());
+ EXPECT_FALSE(GetDataFromSerialization(
+ motivation_hostport, icon_subresource_hostport, recovered_referral_list,
+ &rate, &latency));
+ EXPECT_FALSE(GetDataFromSerialization(
+ motivation_hostport, img_subresource_hostport, recovered_referral_list,
+ &rate, &latency));
master->Shutdown();
}
@@ -504,25 +577,27 @@ TEST_F(DnsMasterTest, ReferrerSerializationTrimTest) {
TEST_F(DnsMasterTest, PriorityQueuePushPopTest) {
DnsMaster::HostNameQueue queue;
+ net::HostPortPair first("first", 80), second("second", 90);
+
// First check high priority queue FIFO functionality.
EXPECT_TRUE(queue.IsEmpty());
- queue.Push("a", DnsHostInfo::LEARNED_REFERAL_MOTIVATED);
+ queue.Push(first, DnsHostInfo::LEARNED_REFERAL_MOTIVATED);
EXPECT_FALSE(queue.IsEmpty());
- queue.Push("b", DnsHostInfo::MOUSE_OVER_MOTIVATED);
+ queue.Push(second, DnsHostInfo::MOUSE_OVER_MOTIVATED);
EXPECT_FALSE(queue.IsEmpty());
- EXPECT_EQ(queue.Pop(), "a");
+ EXPECT_EQ(queue.Pop().ToString(), first.ToString());
EXPECT_FALSE(queue.IsEmpty());
- EXPECT_EQ(queue.Pop(), "b");
+ EXPECT_EQ(queue.Pop().ToString(), second.ToString());
EXPECT_TRUE(queue.IsEmpty());
// Then check low priority queue FIFO functionality.
- queue.Push("a", DnsHostInfo::PAGE_SCAN_MOTIVATED);
+ queue.Push(first, DnsHostInfo::PAGE_SCAN_MOTIVATED);
EXPECT_FALSE(queue.IsEmpty());
- queue.Push("b", DnsHostInfo::OMNIBOX_MOTIVATED);
+ queue.Push(second, DnsHostInfo::OMNIBOX_MOTIVATED);
EXPECT_FALSE(queue.IsEmpty());
- EXPECT_EQ(queue.Pop(), "a");
+ EXPECT_EQ(queue.Pop().ToString(), first.ToString());
EXPECT_FALSE(queue.IsEmpty());
- EXPECT_EQ(queue.Pop(), "b");
+ EXPECT_EQ(queue.Pop().ToString(), second.ToString());
EXPECT_TRUE(queue.IsEmpty());
}
@@ -530,31 +605,40 @@ TEST_F(DnsMasterTest, PriorityQueueReorderTest) {
DnsMaster::HostNameQueue queue;
// Push all the low priority items.
+ net::HostPortPair low1("low1", 80),
+ low2("low2", 80),
+ low3("low3", 443),
+ low4("low4", 80),
+ low5("low5", 80),
+ hi1("hi1", 80),
+ hi2("hi2", 80),
+ hi3("hi3", 80);
+
EXPECT_TRUE(queue.IsEmpty());
- queue.Push("scan", DnsHostInfo::PAGE_SCAN_MOTIVATED);
- queue.Push("unit", DnsHostInfo::UNIT_TEST_MOTIVATED);
- queue.Push("lmax", DnsHostInfo::LINKED_MAX_MOTIVATED);
- queue.Push("omni", DnsHostInfo::OMNIBOX_MOTIVATED);
- queue.Push("startup", DnsHostInfo::STARTUP_LIST_MOTIVATED);
- queue.Push("omni", DnsHostInfo::OMNIBOX_MOTIVATED);
+ queue.Push(low1, DnsHostInfo::PAGE_SCAN_MOTIVATED);
+ queue.Push(low2, DnsHostInfo::UNIT_TEST_MOTIVATED);
+ queue.Push(low3, DnsHostInfo::LINKED_MAX_MOTIVATED);
+ queue.Push(low4, DnsHostInfo::OMNIBOX_MOTIVATED);
+ queue.Push(low5, DnsHostInfo::STARTUP_LIST_MOTIVATED);
+ queue.Push(low4, DnsHostInfo::OMNIBOX_MOTIVATED);
// Push all the high prority items
- queue.Push("learned", DnsHostInfo::LEARNED_REFERAL_MOTIVATED);
- queue.Push("refer", DnsHostInfo::STATIC_REFERAL_MOTIVATED);
- queue.Push("mouse", DnsHostInfo::MOUSE_OVER_MOTIVATED);
+ queue.Push(hi1, DnsHostInfo::LEARNED_REFERAL_MOTIVATED);
+ queue.Push(hi2, DnsHostInfo::STATIC_REFERAL_MOTIVATED);
+ queue.Push(hi3, DnsHostInfo::MOUSE_OVER_MOTIVATED);
// Check that high priority stuff comes out first, and in FIFO order.
- EXPECT_EQ(queue.Pop(), "learned");
- EXPECT_EQ(queue.Pop(), "refer");
- EXPECT_EQ(queue.Pop(), "mouse");
+ EXPECT_EQ(queue.Pop().ToString(), hi1.ToString());
+ EXPECT_EQ(queue.Pop().ToString(), hi2.ToString());
+ EXPECT_EQ(queue.Pop().ToString(), hi3.ToString());
// ...and then low priority strings.
- EXPECT_EQ(queue.Pop(), "scan");
- EXPECT_EQ(queue.Pop(), "unit");
- EXPECT_EQ(queue.Pop(), "lmax");
- EXPECT_EQ(queue.Pop(), "omni");
- EXPECT_EQ(queue.Pop(), "startup");
- EXPECT_EQ(queue.Pop(), "omni");
+ EXPECT_EQ(queue.Pop().ToString(), low1.ToString());
+ EXPECT_EQ(queue.Pop().ToString(), low2.ToString());
+ EXPECT_EQ(queue.Pop().ToString(), low3.ToString());
+ EXPECT_EQ(queue.Pop().ToString(), low4.ToString());
+ EXPECT_EQ(queue.Pop().ToString(), low5.ToString());
+ EXPECT_EQ(queue.Pop().ToString(), low4.ToString());
EXPECT_TRUE(queue.IsEmpty());
}
diff --git a/chrome/browser/net/preconnect.cc b/chrome/browser/net/preconnect.cc
new file mode 100644
index 0000000..ff200de
--- /dev/null
+++ b/chrome/browser/net/preconnect.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2006-2008 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/preconnect.h"
+
+#include "base/logging.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/common/net/url_request_context_getter.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_transaction_factory.h"
+#include "net/url_request/url_request_context.h"
+
+namespace chrome_browser_net {
+
+// We will deliberately leak this singular instance, which is used only for
+// callbacks.
+// static
+Preconnect* Preconnect::callback_instance_;
+
+// static
+bool Preconnect::PreconnectOnUIThread(const net::HostPortPair& hostport) {
+ // Try to do connection warming for this search provider.
+ URLRequestContextGetter* getter = Profile::GetDefaultRequestContext();
+ if (!getter)
+ return false;
+ // Prewarm connection to Search URL.
+ ChromeThread::PostTask(
+ ChromeThread::IO,
+ FROM_HERE,
+ NewRunnableFunction(Preconnect::PreconnectOnIOThread, hostport));
+ return true;
+}
+
+// static
+void Preconnect::PreconnectOnIOThread(const net::HostPortPair& hostport) {
+ URLRequestContextGetter* getter = Profile::GetDefaultRequestContext();
+ if (!getter)
+ return;
+ if (!ChromeThread::CurrentlyOn(ChromeThread::IO)) {
+ LOG(DFATAL) << "This must be run only on the IO thread.";
+ return;
+ }
+ URLRequestContext* context = getter->GetURLRequestContext();
+ net::HttpTransactionFactory* factory = context->http_transaction_factory();
+ net::HttpNetworkSession* session = factory->GetSession();
+ scoped_refptr<net::TCPClientSocketPool> pool = session->tcp_socket_pool();
+
+ net::TCPSocketParams params(hostport.host, hostport.port, net::LOW,
+ GURL(), false);
+
+ net::ClientSocketHandle handle;
+ if (!callback_instance_)
+ callback_instance_ = new Preconnect;
+
+ // TODO(jar): This does not handle proxies currently.
+ handle.Init(hostport.ToString() , params, net::LOWEST,
+ callback_instance_, pool, net::BoundNetLog());
+ handle.Reset();
+}
+
+void Preconnect::RunWithParams(const Tuple1<int>& params) {
+ // This will rarely be called, as we reset the connection just after creating.
+ NOTREACHED();
+}
+} // chrome_browser_net
diff --git a/chrome/browser/net/preconnect.h b/chrome/browser/net/preconnect.h
new file mode 100644
index 0000000..d4fb0dd
--- /dev/null
+++ b/chrome/browser/net/preconnect.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2006-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.
+
+// A Preconnect instance maintains state while a TCP/IP connection is made, and
+// and then released into the pool of available connections for future use.
+
+#ifndef CHROME_BROWSER_NET_PRECONNECT_H_
+#define CHROME_BROWSER_NET_PRECONNECT_H_
+
+#include "base/ref_counted.h"
+#include "net/base/completion_callback.h"
+#include "net/base/host_port_pair.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/socket/tcp_client_socket_pool.h"
+#include "net/url_request/url_request_context.h"
+
+namespace chrome_browser_net {
+
+class Preconnect : public net::CompletionCallback {
+ public:
+ static bool PreconnectOnUIThread(const net::HostPortPair& hostport);
+
+ static void PreconnectOnIOThread(const net::HostPortPair& hostport);
+
+ private:
+ Preconnect() {}
+
+ // Supply an instance that could have been used in an IO callback, but will
+ // never actually be used (because we reset the connection so quickly).
+ static Preconnect* callback_instance_;
+
+ // IO Callback which whould be performed when the connection is established.
+ virtual void RunWithParams(const Tuple1<int>& params);
+
+ DISALLOW_COPY_AND_ASSIGN(Preconnect);
+};
+} // chrome_browser_net
+
+#endif // CHROME_BROWSER_NET_PRECONNECT_H_
diff --git a/chrome/browser/net/referrer.cc b/chrome/browser/net/referrer.cc
index bf171e5..736790a 100644
--- a/chrome/browser/net/referrer.cc
+++ b/chrome/browser/net/referrer.cc
@@ -4,69 +4,119 @@
#include "chrome/browser/net/referrer.h"
+#include <limits.h>
+
#include "base/logging.h"
namespace chrome_browser_net {
-void Referrer::SuggestHost(const std::string& host) {
+//------------------------------------------------------------------------------
+// Smoothing parameter for updating subresource_use_rate_.
+
+// We always combine our old expected value, weighted by some factor, with the
+// new expected value Enew. The new "expected value" is the number of actual
+// connections made due to the curernt navigations.
+// This means the formula (in a concise form) is:
+// Eupdated = Eold * W + Enew * (1 - W)
+// That means that IF we end up needing to connect, we should apply the formula:
+// Pupdated = Pold * W + Enew * (1 - W)
+// If we visit the containing url, but don't end up needing a connection:
+// Pupdated = Pold * W
+// To achive the above upating algorithm, we end up doing the multiplication
+// by W every time we contemplate doing a preconneciton (i.e., when we navigate
+// to the containing URL, and consider doing a preconnection), and then IFF we
+// learn that we really needed a connection to the subresource, we complete the
+// above algorithm by adding the (1 - W) for each connection we make.
+
+// We weight the new expected value by a factor which is in the range of 0.0 to
+// 1.0.
+static const double kWeightingForOldExpectedValue = 0.66;
+
+// The expected value needed before we actually do a preconnection.
+static const double kPreconnectWorthyExpectedValue = 0.7;
+
+// The expected value that we'll need a preconnection when we first see the
+// subresource getting fetched. Very conservative is 0.0, which will mean that
+// we have to wait for a while before using preconnection... but we do persist
+// results, so we'll have the learned answer in the long run.
+static const double kInitialExpectedValue = 0.0;
+
+// static
+bool Referrer::use_preconnect_valuations_ = false;
+
+void Referrer::SuggestHost(const net::HostPortPair& hostport) {
// Limit how large our list can get, in case we make mistakes about what
// hostnames are in sub-resources (example: Some advertisments have a link to
// the ad agency, and then provide a "surprising" redirect to the advertised
// entity, which then (mistakenly) appears to be a subresource on the page
// hosting the ad).
// TODO(jar): Do experiments to optimize the max count of suggestions.
- static const size_t kMaxSuggestions = 8;
+ static const size_t kMaxSuggestions = 10;
- if (host.empty())
+ if (hostport.host.empty()) // Is this really needed????
+ return;
+ SubresourceMap::iterator it = find(hostport);
+ if (it != end()) {
+ it->second.SubresourceIsNeeded();
return;
+ }
+
if (kMaxSuggestions <= size()) {
DeleteLeastUseful();
DCHECK(kMaxSuggestions > size());
}
- // Add in the new suggestion.
- (*this)[host];
+ (*this)[hostport].SubresourceIsNeeded();
}
void Referrer::DeleteLeastUseful() {
- std::string least_useful_name;
+ // Find the item with the lowest value. Most important is preconnection_rate,
+ // next is latency savings, and last is lifetime (age).
+ net::HostPortPair least_useful_hostport;
+ double lowest_rate_seen = 0.0;
// We use longs for durations because we will use multiplication on them.
- int64 least_useful_latency = 0; // Duration in milliseconds.
+ int64 lowest_latency_seen = 0; // Duration in milliseconds.
int64 least_useful_lifetime = 0; // Duration in milliseconds.
const base::Time kNow(base::Time::Now()); // Avoid multiple calls.
- for (HostNameMap::iterator it = begin(); it != end(); ++it) {
+ for (SubresourceMap::iterator it = begin(); it != end(); ++it) {
int64 lifetime = (kNow - it->second.birth_time()).InMilliseconds();
int64 latency = it->second.latency().InMilliseconds();
- if (!least_useful_name.empty()) {
- if (!latency && !least_useful_latency) {
+ double rate = it->second.subresource_use_rate();
+ if (!least_useful_hostport.host.empty()) {
+ if (rate > lowest_rate_seen)
+ continue;
+ if (!latency && !lowest_latency_seen) {
// Older name is less useful.
if (lifetime <= least_useful_lifetime)
continue;
} else {
- // Compare the ratios latency/lifetime vs.
- // least_useful_latency/least_useful_lifetime by cross multiplying (to
- // avoid integer division hassles). Overflow's won't happen until
- // both latency and lifetime pass about 49 days.
- if (latency * least_useful_lifetime >=
- least_useful_latency * lifetime) {
+ // Compare the ratios:
+ // latency/lifetime
+ // vs.
+ // lowest_latency_seen/least_useful_lifetime
+ // by cross multiplying (to avoid integer division hassles). Overflow's
+ // won't happen until both latency and lifetime pass about 49 days.
+ if (latency * least_useful_lifetime >
+ lowest_latency_seen * lifetime) {
continue;
}
}
}
- least_useful_name = it->first;
- least_useful_latency = latency;
+ least_useful_hostport = it->first;
+ lowest_rate_seen = rate;
+ lowest_latency_seen = latency;
least_useful_lifetime = lifetime;
}
- erase(least_useful_name);
- // Note: there is a small chance that we will discard a least_useful_name
+ erase(least_useful_hostport);
+ // Note: there is a small chance that we will discard a least_useful_hostport
// that is currently being prefetched because it *was* in this referer list.
// In that case, when a benefit appears in AccrueValue() below, we are careful
// to check before accessing the member.
}
void Referrer::AccrueValue(const base::TimeDelta& delta,
- const std::string& host) {
- HostNameMap::iterator it = find(host);
+ const net::HostPortPair& hostport) {
+ SubresourceMap::iterator it = find(hostport);
// Be careful that we weren't evicted from this referrer in DeleteLeastUseful.
if (it != end())
it->second.AccrueValue(delta);
@@ -74,7 +124,7 @@ void Referrer::AccrueValue(const base::TimeDelta& delta,
bool Referrer::Trim() {
bool has_some_latency_left = false;
- for (HostNameMap::iterator it = begin(); it != end(); ++it)
+ for (SubresourceMap::iterator it = begin(); it != end(); ++it)
if (it->second.Trim())
has_some_latency_left = true;
return has_some_latency_left;
@@ -83,7 +133,8 @@ bool Referrer::Trim() {
bool ReferrerValue::Trim() {
int64 latency_ms = latency_.InMilliseconds() / 2;
latency_ = base::TimeDelta::FromMilliseconds(latency_ms);
- return latency_ms > 0;
+ return latency_ms > 0 ||
+ subresource_use_rate_ > kPreconnectWorthyExpectedValue / 2;
}
@@ -91,42 +142,85 @@ void Referrer::Deserialize(const Value& value) {
if (value.GetType() != Value::TYPE_LIST)
return;
const ListValue* subresource_list(static_cast<const ListValue*>(&value));
- for (size_t index = 0; index + 1 < subresource_list->GetSize(); index += 2) {
+ size_t index = 0; // Bounds checking is done by subresource_list->Get*().
+ while (true) {
+ int port;
+ if (!subresource_list->GetInteger(index++, &port))
+ return;
std::string host;
- if (!subresource_list->GetString(index, &host))
+ if (!subresource_list->GetString(index++, &host))
return;
int latency_ms;
- if (!subresource_list->GetInteger(index + 1, &latency_ms))
+ if (!subresource_list->GetInteger(index++, &latency_ms))
return;
+ double rate;
+ if (!subresource_list->GetReal(index++, &rate))
+ return;
+
+ net::HostPortPair hostport(host, port);
base::TimeDelta latency = base::TimeDelta::FromMilliseconds(latency_ms);
// TODO(jar): We could be more direct, and change birth date or similar to
// show that this is a resurrected value we're adding in. I'm not yet sure
// of how best to optimize the learning and pruning (Trim) algorithm at this
// level, so for now, we just suggest subresources, which leaves them all
// with the same birth date (typically start of process).
- SuggestHost(host);
- AccrueValue(latency, host);
+ SuggestHost(hostport);
+ AccrueValue(latency, hostport);
+ (*this)[hostport].SetSubresourceUseRate(rate);
}
}
Value* Referrer::Serialize() const {
ListValue* subresource_list(new ListValue);
for (const_iterator it = begin(); it != end(); ++it) {
- StringValue* host(new StringValue(it->first));
+ FundamentalValue* port(new FundamentalValue(it->first.port));
+ StringValue* host(new StringValue(it->first.host));
int latency_integer = static_cast<int>(it->second.latency().
InMilliseconds());
// Watch out for overflow in the above static_cast! Check to see if we went
// negative, and just use a "big" value. The value seems unimportant once
// we get to such high latencies. Probable cause of high latency is a bug
// in other code, so also do a DCHECK.
- DCHECK(latency_integer >= 0);
+ DCHECK_GE(latency_integer, 0);
if (latency_integer < 0)
latency_integer = INT_MAX;
FundamentalValue* latency(new FundamentalValue(latency_integer));
+ FundamentalValue* rate(new FundamentalValue(
+ it->second.subresource_use_rate()));
+
+ subresource_list->Append(port);
subresource_list->Append(host);
subresource_list->Append(latency);
+ subresource_list->Append(rate);
}
return subresource_list;
}
+//------------------------------------------------------------------------------
+
+ReferrerValue::ReferrerValue()
+ : birth_time_(base::Time::Now()),
+ navigation_count_(0),
+ preconnection_count_(0),
+ subresource_use_rate_(kInitialExpectedValue) {
+}
+
+void ReferrerValue::SubresourceIsNeeded() {
+ DCHECK_GE(kWeightingForOldExpectedValue, 0);
+ DCHECK_LE(kWeightingForOldExpectedValue, 1.0);
+ ++navigation_count_;
+ subresource_use_rate_ += 1 - kWeightingForOldExpectedValue;
+}
+
+bool ReferrerValue::IsPreconnectWorthDoing() {
+ bool preconnecting = kPreconnectWorthyExpectedValue < subresource_use_rate_;
+ if (preconnecting)
+ ++preconnection_count_;
+ subresource_use_rate_ *= kWeightingForOldExpectedValue;
+ // Note: the use rate is temporarilly possibly incorect, as we need to find
+ // out if we really end up connecting. This will happen in a few hundred
+ // milliseconds (when content arrives, etc.).
+ return preconnecting;
+}
+
} // namespace chrome_browser_net
diff --git a/chrome/browser/net/referrer.h b/chrome/browser/net/referrer.h
index 8c252b6..bef13a1 100644
--- a/chrome/browser/net/referrer.h
+++ b/chrome/browser/net/referrer.h
@@ -20,36 +20,61 @@
#include "base/basictypes.h"
#include "base/time.h"
#include "base/values.h"
-#include "googleurl/src/gurl.h"
+#include "net/base/host_port_pair.h"
namespace chrome_browser_net {
//------------------------------------------------------------------------------
// For each hostname in a Referrer, we have a ReferrerValue. It indicates
-// exactly how much value (re: latency reduction) has resulted from having this
-// entry.
+// exactly how much value (re: latency reduction, or connection use) has
+// resulted from having this entry.
class ReferrerValue {
public:
- ReferrerValue() : birth_time_(base::Time::Now()) {}
+ ReferrerValue();
+
+ // Used during deserialization.
+ void SetSubresourceUseRate(double rate) { subresource_use_rate_ = rate; }
base::TimeDelta latency() const { return latency_; }
base::Time birth_time() const { return birth_time_; }
void AccrueValue(const base::TimeDelta& delta) { latency_ += delta; }
+ // Record the fact that we navigated to the associated subresource URL.
+ void SubresourceIsNeeded();
+
+ // Evaluate if it is worth making this preconnection, and return true if it
+ // seems worthwhile. As a side effect, we also tally the proconnection for
+ // statistical purposes only.
+ bool IsPreconnectWorthDoing();
+
+ int64 navigation_count() const { return navigation_count_; }
+ int64 preconnection_count() const { return preconnection_count_; }
+ double subresource_use_rate() const { return subresource_use_rate_; }
+
// Reduce the latency figure by a factor of 2, and return true if any latency
// remains.
bool Trim();
private:
- base::TimeDelta latency_; // Accumulated latency savings.
+ base::TimeDelta latency_; // Accumulated DNS resolution latency savings.
const base::Time birth_time_;
+
+ // The number of times this item was navigated to with the fixed referrer.
+ int64 navigation_count_;
+
+ // The number of times this item was preconnected as a consequence of its
+ // referrer.
+ int64 preconnection_count_;
+
+ // A smoothed estimate of the probability that a connection will be needed.
+ double subresource_use_rate_;
};
//------------------------------------------------------------------------------
// A list of domain names to pre-resolve. The names are the keys to this map,
// and the values indicate the amount of benefit derived from having each name
// around.
-typedef std::map<std::string, ReferrerValue> HostNameMap;
+typedef std::map<net::HostPortPair, ReferrerValue> SubresourceMap;
//------------------------------------------------------------------------------
// There is one Referrer instance for each hostname that has acted as an HTTP
@@ -58,18 +83,23 @@ typedef std::map<std::string, ReferrerValue> HostNameMap;
// was probably needed as a subresource of a page, and was not otherwise
// predictable until the content with the reference arrived). Most typically,
// an outer page was a page fetched by the user, and this instance lists names
-// in HostNameMap which are subresources and that were needed to complete the
+// in SubresourceMap which are subresources and that were needed to complete the
// rendering of the outer page.
-class Referrer : public HostNameMap {
+class Referrer : public SubresourceMap {
public:
- // Add the indicated host to the list of hosts that are resolved via DNS when
- // the user navigates to this referrer. Note that if the list is long, an
- // entry may be discarded to make room for this insertion.
- void SuggestHost(const std::string& host);
+ Referrer() : use_count_(1) {}
+ void IncrementUseCount() { ++use_count_; }
+ int64 use_count() const { return use_count_; }
+
+ // Add the indicated host/port to the list of hosts that are resolved via DNS
+ // when the user navigates to this referrer. Note that if the list is long,
+ // an entry may be discarded to make room for this insertion.
+ void SuggestHost(const net::HostPortPair& hostport);
- // Record additional usefulness of having this host name in the list.
+ // Record additional usefulness of having this host/port name in the list.
// Value is expressed as positive latency of amount delta.
- void AccrueValue(const base::TimeDelta& delta, const std::string& host);
+ void AccrueValue(const base::TimeDelta& delta,
+ const net::HostPortPair& hostport);
// Trim the Referrer, by first diminishing (scaling down) the latency for each
// ReferredValue.
@@ -80,6 +110,10 @@ class Referrer : public HostNameMap {
Value* Serialize() const;
void Deserialize(const Value& referrers);
+ static void SetUsePreconnectValuations(bool dns) {
+ use_preconnect_valuations_ = dns;
+ }
+
private:
// Helper function for pruning list. Metric for usefulness is "large accrued
// value," in the form of latency_ savings associated with a host name. We
@@ -89,6 +123,13 @@ class Referrer : public HostNameMap {
// accrue savings as quickly.
void DeleteLeastUseful();
+ // The number of times this referer had its subresources scaned for possible
+ // preconnection or DNS preresolution.
+ int64 use_count_;
+
+ // Select between DNS prefetch latency savings, or preconnection valuations
+ // for a metric to decide which referers to save.
+ static bool use_preconnect_valuations_;
// We put these into a std::map<>, so we need copy constructors.
// DISALLOW_COPY_AND_ASSIGN(Referrer);
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 55a75ce..def903d 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1644,6 +1644,8 @@
'browser/net/metadata_url_request.h',
'browser/net/passive_log_collector.cc',
'browser/net/passive_log_collector.h',
+ 'browser/net/preconnect.cc',
+ 'browser/net/preconnect.h',
'browser/net/referrer.cc',
'browser/net/referrer.h',
'browser/net/resolve_proxy_msg_helper.cc',
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index c9028cf..2b14989 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -321,6 +321,9 @@ const char kEnableNaCl[] = "enable-nacl";
// Enable Native Web Worker support.
const char kEnableNativeWebWorkers[] = "enable-native-web-workers";
+// Enable speculative TCP/IP preconnection.
+const char kEnablePreconnect[] = "enable-preconnect";
+
// Enable Privacy Blacklists.
const char kEnablePrivacyBlacklists[] = "enable-privacy-blacklists";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index bc82fd5..d9ac1e9 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -105,6 +105,7 @@ extern const char kEnableLogging[];
extern const char kEnableMonitorProfile[];
extern const char kEnableNaCl[];
extern const char kEnableNativeWebWorkers[];
+extern const char kEnablePreconnect[];
extern const char kEnablePrivacyBlacklists[];
extern const char kEnableRendererAccessibility[];
extern const char kEnableStatsTable[];
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 4d73e6a..b7dc105 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -164,8 +164,8 @@ const wchar_t kDnsPrefetchingEnabled[] = L"dns_prefetching.enabled";
const wchar_t kDnsStartupPrefetchList[] = L"StartupDNSPrefetchList";
// A list of host names used to fetch web pages, and their commonly used
-// sub-resource hostnames (and expected latency benefits from pre-resolving such
-// sub-resource hostnames).
+// sub-resource hostnames (and expected latency benefits from pre-resolving, or
+// preconnecting to, such sub-resource hostnames).
// This list is adaptively grown and pruned.
const wchar_t kDnsHostReferralList[] = L"HostReferralList";