summaryrefslogtreecommitdiffstats
path: root/chrome/browser/net
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/net
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/net')
-rw-r--r--chrome/browser/net/dns_global.cc463
-rw-r--r--chrome/browser/net/dns_global.h80
-rw-r--r--chrome/browser/net/dns_host_info.cc361
-rw-r--r--chrome/browser/net/dns_host_info.h174
-rw-r--r--chrome/browser/net/dns_host_info_unittest.cc114
-rw-r--r--chrome/browser/net/dns_master.cc454
-rw-r--r--chrome/browser/net/dns_master.h201
-rw-r--r--chrome/browser/net/dns_master_unittest.cc451
-rw-r--r--chrome/browser/net/dns_slave.cc115
-rw-r--r--chrome/browser/net/dns_slave.h96
10 files changed, 2509 insertions, 0 deletions
diff --git a/chrome/browser/net/dns_global.cc b/chrome/browser/net/dns_global.cc
new file mode 100644
index 0000000..114f548
--- /dev/null
+++ b/chrome/browser/net/dns_global.cc
@@ -0,0 +1,463 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/net/dns_global.h"
+
+#include <map>
+#include <string>
+
+#include "base/stats_counters.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "chrome/browser/net/dns_host_info.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/session_startup_pref.h"
+#include "chrome/common/notification_types.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/dns_resolution_observer.h"
+
+namespace chrome_browser_net {
+
+static void DiscardAllPrefetchState();
+
+//------------------------------------------------------------------------------
+// This section contains all the globally accessable API entry points for the
+// DNS Prefetching feature.
+//------------------------------------------------------------------------------
+
+// Status of prefetch feature, controlling whether any prefetching is done.
+static bool dns_prefetch_enabled = true;
+
+// Cached inverted copy of the off_the_record pref.
+static bool on_the_record_switch = true;
+
+// Enable/disable Dns prefetch activity (either via command line, or via pref).
+void EnableDnsPrefetch(bool enable) {
+ dns_prefetch_enabled = enable;
+}
+
+void OnTheRecord(bool enable) {
+ if (on_the_record_switch == enable)
+ return;
+ on_the_record_switch = enable;
+ if (on_the_record_switch)
+ DiscardAllPrefetchState(); // Destroy all evidence of our OTR session.
+}
+
+void RegisterPrefs(PrefService* local_state) {
+ local_state->RegisterListPref(prefs::kDnsStartupPrefetchList);
+}
+
+void RegisterUserPrefs(PrefService* user_prefs) {
+ user_prefs->RegisterBooleanPref(prefs::kDnsPrefetchingEnabled, true);
+}
+
+// When enabled, we use the following instance to service all requests in the
+// browser process.
+static DnsMaster* dns_master;
+
+// This API is only used in the browser process.
+void DnsPrefetchList(const NameList& hostnames) {
+ if (!dns_prefetch_enabled)
+ return;
+ DCHECK(NULL != dns_master);
+ if (NULL != dns_master)
+ dns_master->ResolveList(hostnames);
+}
+
+// This API is used by the autocomplete popup box (wher URLs are typed).
+void DnsPrefetchUrlString(const url_canon::UTF16String& url_string) {
+ if (!dns_prefetch_enabled || NULL == dns_master)
+ return;
+ GURL gurl(url_string);
+ if (gurl.is_valid()) {
+ DnsPrefetch(gurl.host());
+ }
+}
+
+// This API currently used after translating a url_string.
+void DnsPrefetch(const std::string& hostname) {
+ if (!dns_prefetch_enabled || NULL == dns_master || !hostname.size())
+ return;
+ dns_master->Resolve(hostname);
+}
+
+//------------------------------------------------------------------------------
+// This section intermingles prefetch results with actual browser HTTP
+// network activity. It supports calculating of the benefit of a prefetch, as
+// well as recording what prefetched hostname resolutions might be potentially
+// helpful during the next chrome-startup.
+//------------------------------------------------------------------------------
+
+// This function determines if there was a saving by prefetching the hostname
+// for which the host_info is supplied.
+static bool AcruePrefetchBenefits(DnsHostInfo* host_info) {
+ if (!dns_prefetch_enabled || NULL == dns_master)
+ return false;
+ return dns_master->AcruePrefetchBenefits(host_info);
+}
+
+// The observer class needs to connect starts and finishes of HTTP network
+// resolutions. We use the following type for that map.
+typedef std::map<void*, DnsHostInfo> ObservedResolutionMap;
+
+// There will only be one instance ever created of the following Observer
+// class. As a result, we get away with using static members for data local
+// to that instance (to better comply with a google style guide exemption).
+class PrefetchObserver : public net::DnsResolutionObserver {
+ public:
+ PrefetchObserver();
+ ~PrefetchObserver();
+
+ virtual void OnStartResolution(const std::string& name, void* context);
+ virtual void OnFinishResolutionWithStatus(bool was_resolved, void* context);
+
+ static void DnsGetFirstResolutionsHtml(std::string* output);
+ static void SaveStartupListAsPref(PrefService* local_state);
+
+ private:
+ static void StartupListAppend(const DnsHostInfo& host_info);
+
+ // We avoid using member variables to better comply with the style guide.
+ // We had permission to instantiate only a very minimal class as a global
+ // data item, so we avoid putting members in that class.
+ // There is really only one instance of this class, and it would have been
+ // much simpler to use member variables than these static members.
+ static Lock* lock;
+ // Map of pending resolutions seen by observer.
+ static ObservedResolutionMap* resolutions;
+ // List of the first N hostname resolutions observed in this run.
+ static Results* first_resolutions;
+ // The number of hostnames we'll save for prefetching at next startup.
+ static const size_t kStartupResolutionCount = 10;
+};
+
+//------------------------------------------------------------------------------
+// Member definitions for above Observer class.
+
+PrefetchObserver::PrefetchObserver() {
+ DCHECK(!lock && !resolutions && !first_resolutions);
+ lock = new Lock;
+ resolutions = new ObservedResolutionMap;
+ first_resolutions = new Results;
+}
+
+PrefetchObserver::~PrefetchObserver() {
+ DCHECK(lock && resolutions && first_resolutions);
+ delete first_resolutions;
+ first_resolutions = NULL;
+ delete resolutions;
+ resolutions = NULL;
+ delete lock;
+ lock = NULL;
+}
+
+void PrefetchObserver::OnStartResolution(const std::string& name,
+ void* context) {
+ DCHECK_NE(0, name.length());
+ DnsHostInfo host_info;
+ host_info.SetHostname(name);
+ host_info.SetStartedState();
+
+ AutoLock auto_lock(*lock);
+ (*resolutions)[context] = host_info;
+}
+
+void PrefetchObserver::OnFinishResolutionWithStatus(bool was_resolved,
+ void* context) {
+ DnsHostInfo host_info;
+ size_t startup_count;
+ {
+ AutoLock auto_lock(*lock);
+ ObservedResolutionMap::iterator it = resolutions->find(context);
+ if (resolutions->end() == it) {
+ return;
+ }
+ host_info = it->second;
+ resolutions->erase(it);
+ startup_count = first_resolutions->size();
+ }
+ host_info.SetFinishedState(was_resolved); // Get timing info
+ AcruePrefetchBenefits(&host_info); // Update prefetch benefit (if any).
+ if (kStartupResolutionCount <= startup_count || !was_resolved)
+ return;
+ StartupListAppend(host_info);
+}
+
+// static
+void PrefetchObserver::StartupListAppend(const DnsHostInfo& host_info) {
+ if (!on_the_record_switch || NULL == dns_master)
+ return;
+ AutoLock auto_lock(*lock);
+ if (kStartupResolutionCount <= first_resolutions->size())
+ return; // Someone just added the last item.
+ std::string host_name = host_info.hostname();
+ if (first_resolutions->find(host_name) != first_resolutions->end())
+ return; // We already have this hostname listed.
+ (*first_resolutions)[host_name] = host_info;
+}
+
+// static
+void PrefetchObserver::SaveStartupListAsPref(PrefService* local_state) {
+ ListValue* startup_list =
+ local_state->GetMutableList(prefs::kDnsStartupPrefetchList);
+
+ DCHECK(startup_list);
+ if (!startup_list)
+ return;
+ startup_list->Clear();
+ DCHECK(startup_list->GetSize() == 0);
+ AutoLock auto_lock(*lock);
+ for (Results::iterator it = first_resolutions->begin();
+ it != first_resolutions->end();
+ it++) {
+ const std::wstring hostname = ASCIIToWide(it->first);
+ startup_list->Append(Value::CreateStringValue(hostname));
+ }
+}
+
+// static
+void PrefetchObserver::DnsGetFirstResolutionsHtml(std::string* output) {
+ DnsHostInfo::DnsInfoTable resolution_list;
+ {
+ AutoLock auto_lock(*lock);
+ for (Results::iterator it(first_resolutions->begin());
+ it != first_resolutions->end();
+ it++) {
+ resolution_list.push_back(it->second);
+ }
+ }
+ DnsHostInfo::GetHtmlTable(resolution_list,
+ "Future startups will prefetch DNS records for ", false, output);
+}
+
+// static
+Lock* PrefetchObserver::lock = NULL;
+// static
+ObservedResolutionMap* PrefetchObserver::resolutions = NULL;
+// static
+Results* PrefetchObserver::first_resolutions = NULL;
+
+//------------------------------------------------------------------------------
+// Support observer to detect opening and closing of OffTheRecord windows.
+
+class OffTheRecordObserver : public NotificationObserver {
+ public:
+ OffTheRecordObserver() : lock_(), count_off_the_record_windows_(0) { }
+
+ ~OffTheRecordObserver() { }
+
+ // Register as an observer, and rely on the NotificationSystem shutdown
+ // to unregister us (at the last possible moment).
+ void Register() {
+ NotificationService* service = NotificationService::current();
+ // TODO(tc): These notification observers are never removed.
+ service->AddObserver(this, NOTIFY_BROWSER_CLOSED,
+ NotificationService::AllSources());
+ service->AddObserver(this, NOTIFY_BROWSER_OPENED,
+ NotificationService::AllSources());
+ }
+
+ void Observe(NotificationType type, const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type) {
+ case NOTIFY_BROWSER_OPENED:
+ if (!Source<Browser>(source)->profile()->IsOffTheRecord())
+ break;
+ {
+ AutoLock lock(lock_);
+ ++count_off_the_record_windows_;
+ }
+ OnTheRecord(false);
+ break;
+
+ case NOTIFY_BROWSER_CLOSED:
+ if (!Source<Browser>(source)->profile()->IsOffTheRecord())
+ break; // Ignore ordinary windows.
+ {
+ AutoLock lock(lock_);
+ DCHECK(0 < count_off_the_record_windows_);
+ if (0 >= count_off_the_record_windows_) // Defensive coding.
+ break;
+ if (--count_off_the_record_windows_)
+ break; // Still some windows are incognito.
+ } // Release lock.
+ OnTheRecord(true);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ private:
+ Lock lock_;
+ int count_off_the_record_windows_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(OffTheRecordObserver);
+};
+
+// TODO(jar): Use static class object so that I don't have to get the
+// destruction time right (which requires unregistering just before the
+// notification-service shuts down).
+static OffTheRecordObserver off_the_record_observer;
+
+//------------------------------------------------------------------------------
+// This section supports the about:dns page.
+//------------------------------------------------------------------------------
+
+// Provide global support for the about:dns page.
+void DnsPrefetchGetHtmlInfo(std::string* output) {
+ output->append("<html><head><title>About DNS</title>"
+ // We'd like the following no-cache... but it doesn't work.
+ // "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">"
+ "</head><body>");
+ if (!dns_prefetch_enabled || NULL == dns_master) {
+ output->append("Dns Prefetching is disabled.");
+ } else {
+ if (!on_the_record_switch) {
+ output->append("Incognito mode is active in a window.");
+ } else {
+ dns_master->GetHtmlInfo(output);
+ PrefetchObserver::DnsGetFirstResolutionsHtml(output);
+ }
+ }
+ output->append("</body></html>");
+}
+
+//------------------------------------------------------------------------------
+// This section intializes and tears down global DNS prefetch services.
+//------------------------------------------------------------------------------
+
+// Note: We have explicit permission to create the following global static
+// object (in opposition to Google style rules). By making it a static, we
+// can ensure its deletion.
+static PrefetchObserver dns_resolution_observer;
+
+void InitDnsPrefetch(PrefService* user_prefs) {
+ // Use a large shutdown time so that UI tests (that instigate lookups, and
+ // then try to shutdown the browser) don't instigate the CHECK about
+ // "some slaves have not finished"
+ const TimeDelta kAllowableShutdownTime(TimeDelta::FromSeconds(10));
+ DCHECK(NULL == dns_master);
+ if (!dns_master) {
+ DnsMaster* new_master = new DnsMaster(kAllowableShutdownTime);
+ if (InterlockedCompareExchangePointer(
+ reinterpret_cast<PVOID*>(&dns_master), new_master, NULL)) {
+ delete new_master;
+ } else {
+ // We did the initialization, so we should prime the pump, and set up
+ // the DNS resolution system to run.
+ off_the_record_observer.Register();
+
+ if (user_prefs) {
+ bool enabled = user_prefs->GetBoolean(prefs::kDnsPrefetchingEnabled);
+ EnableDnsPrefetch(enabled);
+ }
+
+ DLOG(INFO) << "DNS Prefetch service started";
+
+ // Start observing real HTTP stack resolutions.
+ net::AddDnsResolutionObserver(&dns_resolution_observer);
+ }
+ }
+}
+
+void ShutdownDnsPrefetch() {
+ DCHECK(NULL != dns_master);
+ DnsMaster* master = dns_master;
+ dns_master = NULL;
+ if (master->ShutdownSlaves()) {
+ delete master;
+ } else {
+ // Leak instance if shutdown problem.
+ DCHECK(0);
+ }
+}
+
+static void DiscardAllPrefetchState() {
+ if (!dns_master)
+ return;
+ dns_master->DiscardAllResults();
+}
+
+//------------------------------------------------------------------------------
+// Functions to handle saving of hostnames from one session to the next, to
+// expedite startup times.
+
+void SaveHostNamesForNextStartup(PrefService* local_state) {
+ if (!dns_prefetch_enabled)
+ return;
+ PrefetchObserver::SaveStartupListAsPref(local_state);
+}
+
+// TODO(jar): correct typo in name change ...Pretch... to ...Prefetch...
+void DnsPretchHostNamesAtStartup(PrefService* user_prefs,
+ PrefService* local_state) {
+ NameList hostnames;
+ // Prefetch DNS for hostnames we learned about during last session.
+ // This may catch secondary hostnames, pulled in by the homepages. It will
+ // also catch more of the "primary" home pages, since that was (presumably)
+ // rendered first (and will be rendered first this time too).
+ ListValue* startup_list =
+ local_state->GetMutableList(prefs::kDnsStartupPrefetchList);
+ if (startup_list) {
+ for (ListValue::iterator it = startup_list->begin();
+ it != startup_list->end();
+ it++) {
+ std::wstring w_hostname;
+ (*it)->GetAsString(&w_hostname);
+ hostnames.push_back(WideToASCII(w_hostname));
+ }
+ }
+
+ // Prepare for any static home page(s) the user has in prefs. The user may
+ // have a LOT of tab's specified, so we may as well try to warm them all.
+ SessionStartupPref tab_start_pref =
+ SessionStartupPref::GetStartupPref(user_prefs);
+ if (SessionStartupPref::URLS == tab_start_pref.type) {
+ 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());
+ }
+ }
+
+ if (hostnames.size() > 0)
+ DnsPrefetchList(hostnames);
+ else
+ DnsPrefetch(std::string("www.google.com")); // Start a thread.
+}
+
+
+} // namespace chrome_browser_net
diff --git a/chrome/browser/net/dns_global.h b/chrome/browser/net/dns_global.h
new file mode 100644
index 0000000..9fefffe
--- /dev/null
+++ b/chrome/browser/net/dns_global.h
@@ -0,0 +1,80 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This is the global interface for the dns prefetch services. It centralizes
+// initialization, along with all the callbacks etc. to connect to the browser
+// process. This allows the more standard DNS prefetching services, such as
+// provided by DnsMaster to be left as more generally usable code, and possibly
+// be shared across multiple client projects.
+
+#ifndef CHROME_BROWSER_NET_DNS_GLOBAL_H__
+#define CHROME_BROWSER_NET_DNS_GLOBAL_H__
+
+#include "chrome/browser/net/dns_master.h"
+
+#include <string>
+
+class PrefService;
+
+namespace chrome_browser_net {
+
+// Functions to initialize our global state so that PrefetchDns() can be called.
+void InitDnsPrefetch(PrefService* user_prefs);
+void ShutdownDnsPrefetch();
+
+// Global API relating to Prefetching in browser
+void EnableDnsPrefetch(bool enable);
+void RegisterPrefs(PrefService* local_state);
+void RegisterUserPrefs(PrefService* user_prefs);
+void DnsPrefetchList(const NameList& hostnames);
+void DnsPrefetchUrlString(const url_canon::UTF16String& url_string);
+void DnsPrefetch(const std::string& hostname);
+void DnsPrefetchGetHtmlInfo(std::string* output);
+
+// Save the hostnames actually used at the start of this session to prefetch
+// during the next startup.
+void SaveHostNamesForNextStartup(PrefService* local_state);
+void DnsPretchHostNamesAtStartup(PrefService* user_prefs,
+ PrefService* local_state);
+
+// Helper class to handle global init and shutdown.
+class DnsPrefetcherInit {
+ public:
+ explicit DnsPrefetcherInit(PrefService* user_prefs) {
+ InitDnsPrefetch(user_prefs);
+ }
+ ~DnsPrefetcherInit() {ShutdownDnsPrefetch();}
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(DnsPrefetcherInit);
+};
+
+} // namespace chrome_browser_net
+
+#endif // CHROME_BROWSER_NET_DNS_GLOBAL_H__
diff --git a/chrome/browser/net/dns_host_info.cc b/chrome/browser/net/dns_host_info.cc
new file mode 100644
index 0000000..589e621
--- /dev/null
+++ b/chrome/browser/net/dns_host_info.cc
@@ -0,0 +1,361 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// See header file for description of class
+
+#include "chrome/browser/net/dns_host_info.h"
+
+#include <math.h>
+
+#include <algorithm>
+#include <string>
+
+#include "base/histogram.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+
+namespace chrome_browser_net {
+
+static bool detailed_logging_enabled = false;
+
+// Use command line switch to enable detailed logging.
+void EnableDnsDetailedLog(bool enable) {
+ detailed_logging_enabled = enable;
+}
+
+// static
+int DnsHostInfo::sequence_counter = 1;
+
+
+bool DnsHostInfo::NeedsDnsUpdate(const std::string& hostname) {
+ DCHECK(hostname == hostname_);
+ switch (state_) {
+ case PENDING: // Just now created info.
+ return true;
+
+ case QUEUED: // In queue.
+ case ASSIGNED: // Slave is working on it.
+ case ASSIGNED_BUT_MARKED: // Slave is working on it.
+ return false; // We're already working on it
+
+ case NO_SUCH_NAME: // Lookup failed.
+ case FOUND: // Lookup succeeded.
+ return !IsStillCached(); // See if DNS cache expired.
+
+ default:
+ DCHECK(false);
+ return false;
+ }
+}
+
+const TimeDelta DnsHostInfo::kNullDuration(TimeDelta::FromMilliseconds(-1));
+
+TimeDelta DnsHostInfo::kCacheExpirationDuration(TimeDelta::FromMinutes(5));
+
+const TimeDelta DnsHostInfo::kMaxNonNetworkDnsLookupDuration(
+ TimeDelta::FromMilliseconds(15));
+
+void DnsHostInfo::set_cache_expiration(TimeDelta time) {
+ kCacheExpirationDuration = time;
+}
+
+void DnsHostInfo::SetQueuedState() {
+ DCHECK(PENDING == state_ || FOUND == state_ || NO_SUCH_NAME == state_);
+ state_ = QUEUED;
+ queue_duration_ = resolve_duration_ = kNullDuration;
+ GetDuration(); // Set time_
+ DLogResultsStats("DNS Prefetch in queue");
+}
+
+void DnsHostInfo::SetAssignedState() {
+ DCHECK(QUEUED == state_);
+ state_ = ASSIGNED;
+ queue_duration_ = GetDuration();
+ DLogResultsStats("DNS Prefetch assigned");
+ DHISTOGRAM_TIMES(L"DNS.PrefetchQueue", queue_duration_);
+}
+
+void DnsHostInfo::SetPendingDeleteState() {
+ DCHECK(ASSIGNED == state_ || ASSIGNED_BUT_MARKED == state_);
+ state_ = ASSIGNED_BUT_MARKED;
+}
+
+void DnsHostInfo::SetFoundState() {
+ DCHECK(ASSIGNED == state_);
+ state_ = FOUND;
+ resolve_duration_ = GetDuration();
+ if (kMaxNonNetworkDnsLookupDuration <= resolve_duration_) {
+ UMA_HISTOGRAM_LONG_TIMES(L"DNS.PrefetchFoundNameL", resolve_duration_);
+ // Record potential beneficial time, and maybe we'll get a cache hit.
+ // We keep the maximum, as the warming we did earlier may still be
+ // helping with a cache upstream in DNS resolution.
+ benefits_remaining_ = std::max(resolve_duration_, benefits_remaining_);
+ }
+ sequence_number_ = sequence_counter++;
+ DLogResultsStats("DNS PrefetchFound");
+}
+
+void DnsHostInfo::SetNoSuchNameState() {
+ DCHECK(ASSIGNED == state_);
+ state_ = NO_SUCH_NAME;
+ resolve_duration_ = GetDuration();
+ if (kMaxNonNetworkDnsLookupDuration <= resolve_duration_) {
+ DHISTOGRAM_TIMES(L"DNS.PrefetchNotFoundName", resolve_duration_);
+ // Record potential beneficial time, and maybe we'll get a cache hit.
+ benefits_remaining_ = std::max(resolve_duration_, benefits_remaining_);
+ }
+ sequence_number_ = sequence_counter++;
+ DLogResultsStats("DNS PrefetchNotFound");
+}
+
+void DnsHostInfo::SetStartedState() {
+ DCHECK(PENDING == state_);
+ state_ = STARTED;
+ queue_duration_ = resolve_duration_ = TimeDelta(); // 0ms.
+ GetDuration(); // Set time.
+}
+
+void DnsHostInfo::SetFinishedState(bool was_resolved) {
+ DCHECK(STARTED == state_);
+ state_ = was_resolved ? FINISHED : FINISHED_UNRESOLVED;
+ resolve_duration_ = GetDuration();
+ // TODO(jar): Sequence number should be incremented in prefetched HostInfo.
+ DLogResultsStats("DNS HTTP Finished");
+}
+
+// IsStillCached() guesses if the DNS cache still has IP data,
+// or at least remembers results about "not finding host."
+bool DnsHostInfo::IsStillCached() const {
+ DCHECK(FOUND == state_ || NO_SUCH_NAME == state_);
+
+ // Default MS OS does not cache failures. Hence we could return false almost
+ // all the time for that case. However, we'd never try again to prefetch
+ // the value if we returned false that way. Hence we'll just let the lookup
+ // time out the same way as FOUND case.
+
+ if (sequence_counter - sequence_number_ > kMaxGuaranteedCacheSize)
+ return false;
+
+ TimeDelta time_since_resolution = TimeTicks::Now() - time_;
+
+ if (FOUND == state_ && resolve_duration_ < kMaxNonNetworkDnsLookupDuration) {
+ // Since cache was warm (no apparent network activity during resolution),
+ // we assume it was "really" found (via network activity) twice as long
+ // ago as when we got our FOUND result.
+ time_since_resolution *= 2;
+ }
+
+ return time_since_resolution < kCacheExpirationDuration;
+}
+
+// Compare the later results, to the previously prefetched info.
+DnsBenefit DnsHostInfo::AcruePrefetchBenefits(DnsHostInfo* later_host_info) {
+ DCHECK(FINISHED == later_host_info->state_
+ || FINISHED_UNRESOLVED == later_host_info->state_);
+ DCHECK(0 == later_host_info->hostname_.compare(hostname_.data()));
+ if ((0 == benefits_remaining_.InMilliseconds()) ||
+ (FOUND != state_ && NO_SUCH_NAME != state_)) {
+ return PREFETCH_NO_BENEFIT;
+ }
+
+ TimeDelta benefit = benefits_remaining_ - later_host_info->resolve_duration_;
+ later_host_info->benefits_remaining_ = benefits_remaining_;
+ benefits_remaining_ = TimeDelta(); // zero ms.
+
+ if (later_host_info->resolve_duration_ > kMaxNonNetworkDnsLookupDuration) {
+ // Our precache effort didn't help since HTTP stack hit the network.
+ DHISTOGRAM_TIMES(L"DNS.PrefetchCacheEviction", resolve_duration_);
+ DLogResultsStats("DNS PrefetchCacheEviction");
+ return PREFETCH_CACHE_EVICTION;
+ }
+
+ if (NO_SUCH_NAME == state_) {
+ UMA_HISTOGRAM_LONG_TIMES(L"DNS.PrefetchNegativeHitL", benefit);
+ DLogResultsStats("DNS PrefetchNegativeHit");
+ return PREFETCH_NAME_NONEXISTANT;
+ }
+
+ DCHECK_EQ(FOUND, state_);
+ UMA_HISTOGRAM_LONG_TIMES(L"DNS.PrefetchPositiveHitL", benefit);
+ DLogResultsStats("DNS PrefetchPositiveHit");
+ return PREFETCH_NAME_FOUND;
+}
+
+void DnsHostInfo::DLogResultsStats(const char* message) const {
+ if (!detailed_logging_enabled)
+ return;
+ DLOG(INFO) << "\t" << message << "\tq="
+ << queue_duration().InMilliseconds() << "ms,\tr="
+ << resolve_duration().InMilliseconds() << "ms\tp="
+ << benefits_remaining_.InMilliseconds() << "ms\tseq="
+ << sequence_number_
+ << "\t" << hostname_;
+}
+
+//------------------------------------------------------------------------------
+// This last section supports HTML output, such as seen in about:dns.
+//------------------------------------------------------------------------------
+
+// Preclude any possibility of Java Script or markup in the text, by only
+// allowing alphanumerics, ".", and whitespace.
+static std::string RemoveJs(const std::string& text) {
+ std::string output(text);
+ size_t length = output.length();
+ for (size_t i = 0; i < 1; i++) {
+ char next = output[i];
+ if (isalnum(next) || isspace(next) || '.' == next)
+ continue;
+ output[i] = '?';
+ }
+ return output;
+}
+
+class MinMaxAverage {
+ public:
+ MinMaxAverage()
+ : sum_(0), square_sum_(0), count_(0),
+ minimum_(kint64max), maximum_(kint64min) {
+ }
+
+ // Return values for use in printf formatted as "%d"
+ int sample(int64 value) {
+ sum_ += value;
+ square_sum_ += value * value;
+ count_++;
+ minimum_ = std::min(minimum_, value);
+ maximum_ = std::max(maximum_, value);
+ return static_cast<int>(value);
+ }
+ int minimum() const { return static_cast<int>(minimum_); }
+ int maximum() const { return static_cast<int>(maximum_); }
+ int average() const { return static_cast<int>(sum_/count_); }
+ int sum() const { return static_cast<int>(sum_); }
+
+ int standard_deviation() const {
+ double average = static_cast<float>(sum_) / count_;
+ double variance = static_cast<float>(square_sum_)/count_
+ - average * average;
+ return static_cast<int>(floor(sqrt(variance) + .5));
+ }
+
+ private:
+ int64 sum_;
+ int64 square_sum_;
+ int count_;
+ int64 minimum_;
+ int64 maximum_;
+
+ // DISALLOW_EVIL_CONSTRUCTORS(MinMaxAverage);
+};
+
+static std::string HoursMinutesSeconds(int seconds) {
+ std::string result;
+ int print_seconds = seconds % 60;
+ int minutes = seconds / 60;
+ int print_minutes = minutes % 60;
+ int print_hours = minutes/60;
+ if (print_hours)
+ StringAppendF(&result, "%.2d:", print_hours);
+ if (print_hours || print_minutes)
+ StringAppendF(&result, "%2.2d:", print_minutes);
+ StringAppendF(&result, "%2.2d", print_seconds);
+ return result;
+}
+
+// static
+void DnsHostInfo::GetHtmlTable(const DnsInfoTable host_infos,
+ const char* description,
+ const bool brief,
+ std::string* output) {
+ if (0 == host_infos.size())
+ return;
+ output->append(description);
+ StringAppendF(output, "%d %s", host_infos.size(),
+ (1 == host_infos.size()) ? "hostname" : "hostnames");
+
+ if (brief) {
+ output->append("<br><br>");
+ return;
+ }
+
+ char* row_format = "<tr align=right><td>%s</td>"
+ "<td>%d</td><td>%d</td><td>%s</td></tr>";
+
+ output->append("<br><table border=1>");
+ StringAppendF(output, "<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>",
+ "Host name", "Applicable Prefetch<br>Time (ms)",
+ "Recent Resolution<br>Time(ms)", "How long ago<br>(HH:MM:SS)");
+
+ // Print bulk of table, and gather stats at same time.
+ MinMaxAverage queue, resolve, preresolve, when;
+ TimeTicks current_time = TimeTicks::Now();
+ for (DnsInfoTable::const_iterator it(host_infos.begin());
+ it != host_infos.end(); it++) {
+ queue.sample((it->queue_duration_.InMilliseconds()));
+ StringAppendF(output, row_format,
+ RemoveJs(it->hostname_).c_str(),
+ preresolve.sample((it->benefits_remaining_.InMilliseconds())),
+ resolve.sample((it->resolve_duration_.InMilliseconds())),
+ HoursMinutesSeconds(when.sample(
+ (current_time - it->time_).InSeconds())).c_str());
+ }
+ // Write min, max, and average summary lines.
+ if (host_infos.size() > 2) {
+ output->append("<B>");
+ StringAppendF(output, row_format,
+ "<b>---minimum---</b>",
+ preresolve.minimum(), resolve.minimum(),
+ HoursMinutesSeconds(when.minimum()).c_str());
+ StringAppendF(output, row_format,
+ "<b>---average---</b>",
+ preresolve.average(), resolve.average(),
+ HoursMinutesSeconds(when.average()).c_str());
+ StringAppendF(output, row_format,
+ "<b>standard deviation</b>",
+ preresolve.standard_deviation(),
+ resolve.standard_deviation(), "n/a");
+ StringAppendF(output, row_format,
+ "<b>---maximum---</b>",
+ preresolve.maximum(), resolve.maximum(),
+ HoursMinutesSeconds(when.maximum()).c_str());
+ StringAppendF(output, row_format,
+ "<b>-----SUM-----</b>",
+ preresolve.sum(), resolve.sum(), "n/a");
+ }
+ output->append("</table>");
+
+#ifdef DEBUG
+ StringAppendF(output,
+ "Prefetch Queue Durations: min=%d, avg=%d, max=%d<br><br>",
+ queue.minimum(), queue.average(), queue.maximum());
+#endif
+
+ output->append("<br>");
+}
+} // namespace chrome_browser_net
diff --git a/chrome/browser/net/dns_host_info.h b/chrome/browser/net/dns_host_info.h
new file mode 100644
index 0000000..d0a05bc
--- /dev/null
+++ b/chrome/browser/net/dns_host_info.h
@@ -0,0 +1,174 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// A DnsHostInfo object is used to store status of a Dns lookup of a specific
+// hostname.
+// It includes progress, from placement in the DnsMaster's queue, to assignment
+// to a slave, to resolution by the (blocking) DNS service as either FOUND or
+// NO_SUCH_NAME.
+
+#ifndef CHROME_BROWSER_NET_DNS_HOST_INFO_H__
+#define CHROME_BROWSER_NET_DNS_HOST_INFO_H__
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/time.h"
+
+namespace chrome_browser_net {
+
+// Use command line switch to enable detailed logging.
+void EnableDnsDetailedLog(bool enable);
+
+enum DnsBenefit {
+ PREFETCH_NO_BENEFIT, // Prefetch never hit the network. Name was pre-cached.
+ PREFETCH_CACHE_EVICTION, // Prefetch used network, but so did HTTP stack.
+ PREFETCH_NAME_NONEXISTANT, // Valuable prefetch of "name not found" was used.
+ PREFETCH_NAME_FOUND, // Valuable prefetch was used.
+ PREFETCH_OBLIVIOUS // No prefetch attempt was even made.
+};
+
+class DnsHostInfo {
+ public:
+ enum DnsProcessingState {
+ // When processed by our prefetching system, there are 4 states:
+ PENDING, // Constructor has completed.
+ QUEUED, // In prefetch queue but not yet assigned to a slave.
+ ASSIGNED, // Currently being processed by a slave.
+ ASSIGNED_BUT_MARKED, // Needs to be deleted as soon as slave is done.
+ FOUND, // DNS prefetch search completed.
+ NO_SUCH_NAME, // DNS prefetch search completed.
+ // When processed by the HTTP network stack, there are 3 states:
+ STARTED, // Resolution has begun.
+ FINISHED, // Resolution has completed.
+ FINISHED_UNRESOLVED}; // No resolution found.
+ static const TimeDelta kMaxNonNetworkDnsLookupDuration;
+ // The number of OS cache entries we can guarantee(?) before cache eviction
+ // might likely take place.
+ static const int kMaxGuaranteedCacheSize = 50;
+
+ typedef std::vector<const DnsHostInfo> DnsInfoTable;
+
+ static const TimeDelta kNullDuration;
+
+ // DnsHostInfo are usually made by the default constructor during
+ // initializing of the DnsMaster's map (of info for Hostnames).
+ DnsHostInfo()
+ : state_(PENDING),
+ resolve_duration_(kNullDuration),
+ queue_duration_(kNullDuration),
+ benefits_remaining_(),
+ sequence_number_(0) {
+ }
+
+ ~DnsHostInfo() {}
+
+ // NeedDnsUpdate decides, based on our internal info,
+ // 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);
+
+ static void set_cache_expiration(TimeDelta time);
+
+ // The prefetching lifecycle.
+ void SetQueuedState();
+ void SetAssignedState();
+ void SetPendingDeleteState();
+ void SetFoundState();
+ void SetNoSuchNameState();
+ // The actual browsing resolution lifecycle.
+ void SetStartedState();
+ void SetFinishedState(bool was_resolved);
+
+ void SetHostname(const std::string& hostname) {
+ if (hostname != hostname_) {
+ DCHECK(hostname_.size() == 0); // Not yet initialized.
+ hostname_ = hostname;
+ }
+ }
+
+ bool was_found() const { return FOUND == state_; }
+ bool was_nonexistant() const { return NO_SUCH_NAME == state_; }
+ bool is_assigned() const {
+ 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_; }
+
+ bool HasHostname(const std::string& hostname) const {
+ return (hostname == hostname_);
+ }
+
+ TimeDelta resolve_duration() const { return resolve_duration_;}
+ TimeDelta queue_duration() const { return queue_duration_;}
+ TimeDelta benefits_remaining() const { return benefits_remaining_; }
+
+ DnsBenefit AcruePrefetchBenefits(DnsHostInfo* later_host_info);
+
+ void DLogResultsStats(const char* message) const;
+
+ static void GetHtmlTable(const DnsInfoTable host_infos,
+ const char* description,
+ const bool brief,
+ std::string* output);
+
+ private:
+ // The next declaration is non-const to facilitate testing.
+ static TimeDelta kCacheExpirationDuration;
+
+ DnsProcessingState state_;
+ std::string hostname_; // Hostname for this info.
+
+ TimeTicks time_; // When was last state changed (usually lookup completed).
+ TimeDelta resolve_duration_; // Time needed for DNS to resolve.
+ TimeDelta queue_duration_; // Time spent in queue.
+ TimeDelta benefits_remaining_; // Unused potential benefits of a prefetch.
+
+ int sequence_number_; // Used to calculate potential of cache eviction.
+ static int sequence_counter; // Used to allocate sequence_number_'s.
+
+ TimeDelta GetDuration() {
+ TimeTicks old_time = time_;
+ time_ = TimeTicks::Now();
+ return time_ - old_time;
+ }
+
+ // IsStillCached() guesses if the DNS cache still has IP data.
+ bool IsStillCached() const;
+
+ // We put these objects into a std::map, and hence we
+ // need some "evil" constructors.
+ // DISALLOW_EVIL_CONSTRUCTORS(DnsHostInfo);
+};
+
+} // namespace chrome_browser_net
+
+#endif // CHROME_BROWSER_NET_DNS_HOST_INFO_H__
diff --git a/chrome/browser/net/dns_host_info_unittest.cc b/chrome/browser/net/dns_host_info_unittest.cc
new file mode 100644
index 0000000..1767fc4
--- /dev/null
+++ b/chrome/browser/net/dns_host_info_unittest.cc
@@ -0,0 +1,114 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Single threaded tests of DnsHostInfo functionality.
+
+#include <time.h>
+#include <string>
+
+#include "chrome/browser/net/dns_host_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class DnsHostInfoTest : public testing::Test {
+};
+
+typedef chrome_browser_net::DnsHostInfo DnsHostInfo;
+
+TEST(DnsHostInfoTest, StateChangeTest) {
+ DnsHostInfo info_practice, info;
+ std::string hostname1("domain1.com"), hostname2("domain2.com");
+
+ // First load DLL, so that their load time won't interfere with tests.
+ // Some tests involve timing function performance, and DLL time can overwhelm
+ // test durations (which are considering network vs cache response times).
+ info_practice.SetHostname(hostname2);
+ info_practice.SetQueuedState();
+ info_practice.SetAssignedState();
+ info_practice.SetFoundState();
+ Sleep(500); // Allow time for DLLs to fully load.
+
+ // Complete the construction of real test object.
+ info.SetHostname(hostname1);
+
+ EXPECT_TRUE(info.NeedsDnsUpdate(hostname1)) << "error in construction state";
+ info.SetQueuedState();
+ EXPECT_FALSE(info.NeedsDnsUpdate(hostname1))
+ << "update needed after being queued";
+ info.SetAssignedState();
+ EXPECT_FALSE(info.NeedsDnsUpdate(hostname1))
+ << "update needed while assigned to slave";
+ info.SetFoundState();
+ EXPECT_FALSE(info.NeedsDnsUpdate(hostname1))
+ << "default expiration time is TOOOOO short";
+
+ // Note that time from ASSIGNED to FOUND was VERY short (probably 0ms), so the
+ // object should conclude that no network activity was needed. As a result,
+ // the required time till expiration will be halved (guessing that we were
+ // half way through having the cache expire when we did the lookup.
+ EXPECT_LT(info.resolve_duration().InMilliseconds(),
+ DnsHostInfo::kMaxNonNetworkDnsLookupDuration.InMilliseconds())
+ << "Non-net time is set too low";
+
+ info.set_cache_expiration(TimeDelta::FromMilliseconds(300));
+ EXPECT_FALSE(info.NeedsDnsUpdate(hostname1)) << "expiration time not honored";
+ // Note that we'll actually get an expiration (effectively) of
+ // 150ms, since there was no detected network activity time during lookup.
+ Sleep(80); // Not enough time to pass our 150ms mark.
+ EXPECT_FALSE(info.NeedsDnsUpdate(hostname1)) << "expiration time not honored";
+ Sleep(100); // Be sure we sleep (80+100) enough to pass that 150ms mark.
+ EXPECT_TRUE(info.NeedsDnsUpdate(hostname1)) << "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();
+ info.SetAssignedState();
+ EXPECT_FALSE(info.NeedsDnsUpdate(hostname1))
+ << "update needed while assigned to slave";
+ Sleep(25); // Greater than minimal expected network latency on DNS lookup.
+ info.SetNoSuchNameState();
+ EXPECT_FALSE(info.NeedsDnsUpdate(hostname1))
+ << "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.
+ Sleep(80); // Not enough time to pass our 300ms mark.
+ EXPECT_FALSE(info.NeedsDnsUpdate(hostname1)) << "expiration time not honored";
+ Sleep(80); // Still not past our 300ms mark (only about 4+2ms)
+ EXPECT_FALSE(info.NeedsDnsUpdate(hostname1)) << "expiration time not honored";
+ Sleep(150);
+ EXPECT_TRUE(info.NeedsDnsUpdate(hostname1)) << "expiration time not honored";
+}
+
+// TODO(jar): Add death test for illegal state changes, and also for setting
+// hostname when already set.
+
+} // namespace anonymous
diff --git a/chrome/browser/net/dns_master.cc b/chrome/browser/net/dns_master.cc
new file mode 100644
index 0000000..493c404
--- /dev/null
+++ b/chrome/browser/net/dns_master.cc
@@ -0,0 +1,454 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// See header file for description of class
+
+#include "chrome/browser/net/dns_master.h"
+
+#include <map>
+#include <sstream>
+#include <string>
+
+#include "base/condition_variable.h"
+#include "base/histogram.h"
+#include "base/stats_counters.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+#include "base/win_util.h"
+#include "chrome/browser/net/dns_slave.h"
+
+namespace chrome_browser_net {
+
+//------------------------------------------------------------------------------
+// This section contains methods for the DnsMaster class.
+//------------------------------------------------------------------------------
+DnsMaster::DnsMaster(TimeDelta shutdown_wait_time)
+ : slave_count_(0),
+ shutdown_(false),
+ running_slave_count_(0),
+ kShutdownWaitTime_(shutdown_wait_time),
+ slaves_have_work_(&lock_) {
+ for ( int i = 0; i < kSlaveCountMax; i++ ) {
+ thread_ids_[i] = 0;
+ thread_handles_[i] = 0;
+ slaves_[i] = NULL;
+ }
+}
+
+// Overloaded Resolve() to take a vector of names.
+void DnsMaster::ResolveList(const NameList& hostnames) {
+ {
+ AutoLock auto_lock(lock_);
+ if (shutdown_) return;
+ if (slave_count_ < kSlaveCountMin) {
+ for (int target_count = std::min(static_cast<int>(hostnames.size()),
+ kSlaveCountMin);
+ target_count > 0;
+ target_count--)
+ PreLockedCreateNewSlaveIfNeeded();
+ } else {
+ PreLockedCreateNewSlaveIfNeeded(); // Allocate one per list call.
+ }
+
+ for (NameList::const_iterator it = hostnames.begin();
+ it < hostnames.end();
+ it++) {
+ PreLockedResolve(*it);
+ }
+ }
+ slaves_have_work_.Signal();
+}
+
+// Basic Resolve() takes an invidual name, and adds it
+// to the queue.
+void DnsMaster::Resolve(const std::string& hostname) {
+ if (0 == hostname.length())
+ return;
+ {
+ AutoLock auto_lock(lock_);
+ if (shutdown_) return;
+ PreLockedCreateNewSlaveIfNeeded(); // Allocate one at a time.
+ PreLockedResolve(hostname);
+ }
+ slaves_have_work_.Signal();
+}
+
+bool DnsMaster::AcruePrefetchBenefits(DnsHostInfo* host_info) {
+ std::string hostname = host_info->hostname();
+ DnsBenefit benefit;
+ DnsHostInfo prefetched_host_info;
+ {
+ AutoLock auto_lock(lock_);
+ if (results_.find(hostname) == results_.end()) {
+ // Remain under lock to assure static HISTOGRAM constructor is safely run.
+ // Use UMA histogram to quantify potential future gains here.
+ UMA_HISTOGRAM_LONG_TIMES(L"DNS.UnexpectedResolutionL",
+ host_info->resolve_duration());
+ SIMPLE_STATS_COUNTER(L"DNS.PrefetchCacheOblivious");
+ host_info->DLogResultsStats("DNS PrefetchCacheOblivious");
+ return false;
+ }
+ benefit = results_[hostname].AcruePrefetchBenefits(host_info);
+ switch (benefit) {
+ case PREFETCH_NAME_FOUND:
+ case PREFETCH_NAME_NONEXISTANT:
+ // Remain under lock to push data.
+ cache_hits_.push_back(*host_info);
+ return true;
+
+ case PREFETCH_CACHE_EVICTION:
+ // Remain under lock to push data.
+ cache_eviction_map_[hostname] = *host_info;
+ return false;
+ case PREFETCH_NO_BENEFIT:
+ // Prefetch never hit the network. Name was pre-cached.
+ return false;
+
+ default:
+ DCHECK(false);
+ return false;
+ }
+ }
+}
+
+static char* PluralOptionalHostname(size_t count) {
+ if (count == 1)
+ return "hostname";
+ return "hostnames";
+}
+
+// Provide sort order so all .com's are together, etc.
+struct RightToLeftStringSorter {
+ bool operator()(const std::string& left, const std::string& right) const {
+ size_t left_length = left.length();
+ size_t right_length = right.length();
+ const char* left_data = left.data();
+ const char* right_data = right.data();
+ while (true) {
+ if (0 == right_length)
+ return false;
+ if (0 == left_length)
+ return true;
+ left_length--;
+ right_length--;
+ int difference = left_data[left_length] - right_data[right_length];
+ if (difference)
+ return difference < 0;
+ }
+ }
+};
+
+void DnsMaster::GetHtmlInfo(std::string* output) {
+ // Local lists for calling DnsHostInfo
+ DnsHostInfo::DnsInfoTable cache_hits;
+ DnsHostInfo::DnsInfoTable cache_evictions;
+ DnsHostInfo::DnsInfoTable name_not_found;
+ DnsHostInfo::DnsInfoTable network_hits;
+ DnsHostInfo::DnsInfoTable already_cached;
+
+ // Get copies of all useful data under protection of a lock.
+ typedef std::map<std::string, DnsHostInfo, RightToLeftStringSorter> Snapshot;
+ Snapshot snapshot;
+ {
+ AutoLock auto_lock(lock_);
+ // DnsHostInfo supports value semantics, so we can do a shallow copy.
+ for (Results::iterator it(results_.begin()); it != results_.end(); it++) {
+ snapshot[it->first] = it->second;
+ }
+ for (Results::iterator it(cache_eviction_map_.begin());
+ it != cache_eviction_map_.end();
+ it++) {
+ cache_evictions.push_back(it->second);
+ }
+ // Reverse list as we copy cache hits, so that new hits are at the top.
+ size_t index = cache_hits_.size();
+ while (index > 0) {
+ index--;
+ cache_hits.push_back(cache_hits_[index]);
+ }
+ }
+
+ // Partition the DnsHostInfo's into categories.
+ for (Snapshot::iterator it(snapshot.begin()); it != snapshot.end(); it++) {
+ if (it->second.was_nonexistant()) {
+ name_not_found.push_back(it->second);
+ continue;
+ }
+ if (!it->second.was_found())
+ continue; // Still being processed.
+ if (TimeDelta() != it->second.benefits_remaining()) {
+ network_hits.push_back(it->second); // With no benefit yet.
+ continue;
+ }
+ if (DnsHostInfo::kMaxNonNetworkDnsLookupDuration >
+ it->second.resolve_duration()) {
+ already_cached.push_back(it->second);
+ continue;
+ }
+ // Remaining case is where prefetch benefit was significant, and was used.
+ // Since we shot those cases as historical hits, we won't bother here.
+ }
+
+ bool brief = false;
+#ifdef NDEBUG
+ brief = true;
+#endif // NDEBUG
+
+ // Call for display of each table, along with title.
+ DnsHostInfo::GetHtmlTable(cache_hits,
+ "Prefetching DNS records produced benefits for ", false, output);
+ DnsHostInfo::GetHtmlTable(cache_evictions,
+ "Cache evictions negated DNS prefetching benefits for ", brief, output);
+ DnsHostInfo::GetHtmlTable(network_hits,
+ "Prefetching DNS records was not yet beneficial for ", brief, output);
+ DnsHostInfo::GetHtmlTable(already_cached,
+ "Previously cached resolutions were found for ", brief, output);
+ DnsHostInfo::GetHtmlTable(name_not_found,
+ "Prefetching DNS records revealed non-existance for ", brief, output);
+}
+
+void DnsMaster::PreLockedResolve(const std::string& hostname) {
+ // DCHECK(We have the lock);
+ DCHECK(0 != slave_count_);
+ DCHECK(0 != hostname.length());
+
+ DnsHostInfo* info = &results_[hostname];
+ info->SetHostname(hostname); // 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));
+
+ static StatsCounter count(L"DNS.PrefetchContemplated");
+ count.Increment();
+
+ if (!info->NeedsDnsUpdate(hostname)) {
+ info->DLogResultsStats("DNS PrefetchNotUpdated");
+ return;
+ }
+
+ info->SetQueuedState();
+ name_buffer_.push(hostname);
+}
+
+void DnsMaster::SetSlaveName(int slave_index) {
+ DCHECK(0 <= slave_index && kSlaveCountMax > slave_index);
+
+ std::string name = StringPrintf("Dns Prefetcher thread %d of %d",
+ slave_index + 1, kSlaveCountMax);
+
+ DLOG(INFO) << "Now Running " << name;
+ Thread::SetThreadName(name.c_str(), thread_ids_[slave_index]);
+}
+
+// GetNextAssignment() is executed on the thread associated with
+// with a prefetch slave instance.
+// Return value of false indicates slave thread termination is needed.
+// Return value of true means info argument was populated
+// with a pointer to the assigned DnsHostInfo instance.
+bool DnsMaster::GetNextAssignment(std::string* hostname) {
+ bool ask_for_help = false;
+ {
+ AutoLock auto_lock(lock_); // For map and buffer access
+ while (0 == name_buffer_.size() && !shutdown_) {
+ // No work pending, so just wait.
+ // wait on condition variable while releasing lock temporarilly.
+ slaves_have_work_.Wait();
+ }
+ if (shutdown_)
+ return false; // Tell slaves to terminate also.
+ *hostname = name_buffer_.front();
+ name_buffer_.pop();
+
+ DnsHostInfo* info = &results_[*hostname];
+ DCHECK(info->HasHostname(*hostname));
+ info->SetAssignedState();
+
+ ask_for_help = name_buffer_.size() > 0;
+ } // Release lock_
+ if (ask_for_help)
+ slaves_have_work_.Signal();
+ return true;
+}
+
+void DnsMaster::SetFoundState(const std::string hostname) {
+ AutoLock auto_lock(lock_); // For map access (changing info values).
+ DnsHostInfo* info = &results_[hostname];
+ DCHECK(info->HasHostname(hostname));
+ if (info->is_marked_to_delete())
+ results_.erase(hostname);
+ else
+ info->SetFoundState();
+}
+
+void DnsMaster::SetNoSuchNameState(const std::string hostname) {
+ AutoLock auto_lock(lock_); // For map access (changing info values).
+ DnsHostInfo* info = &results_[hostname];
+ DCHECK(info->HasHostname(hostname));
+ if (info->is_marked_to_delete())
+ results_.erase(hostname);
+ else
+ info->SetNoSuchNameState();
+}
+
+bool DnsMaster::PreLockedCreateNewSlaveIfNeeded() {
+ // Don't create more then max.
+ if (kSlaveCountMax <= slave_count_ || shutdown_)
+ return false;
+
+ DnsSlave* slave_instance = new DnsSlave(this, slave_count_);
+ DWORD thread_id;
+ size_t stack_size = 0;
+ unsigned int flags = CREATE_SUSPENDED;
+ if (win_util::GetWinVersion() >= win_util::WINVERSION_XP) {
+ // 128kb stack size.
+ stack_size = 128*1024;
+ flags |= STACK_SIZE_PARAM_IS_A_RESERVATION;
+ }
+ HANDLE handle = CreateThread(NULL, // security
+ stack_size,
+ DnsSlave::ThreadStart,
+ reinterpret_cast<void*>(slave_instance),
+ flags,
+ &thread_id);
+ DCHECK(NULL != handle);
+ if (NULL == handle)
+ return false;
+
+ // Carlos suggests it is not valuable to do a set priority.
+ // BOOL WINAPI SetThreadPriority(handle,int nPriority);
+
+ thread_ids_[slave_count_] = thread_id;
+ thread_handles_[slave_count_] = handle;
+ slaves_[slave_count_] = slave_instance;
+ slave_count_++;
+
+ ResumeThread(handle); // WINAPI call.
+ running_slave_count_++;
+
+ return true;
+}
+
+void DnsMaster::SetSlaveHasTerminated(int slave_index) {
+ DCHECK_EQ(GetCurrentThreadId(), thread_ids_[slave_index]);
+ AutoLock auto_lock(lock_);
+ running_slave_count_--;
+ DCHECK(thread_ids_[slave_index]);
+ thread_ids_[slave_index] = 0;
+}
+
+bool DnsMaster::ShutdownSlaves() {
+ int running_slave_count;
+ {
+ AutoLock auto_lock(lock_);
+ shutdown_ = true; // Block additional resolution requests.
+ // Empty the queue gracefully
+ while (name_buffer_.size() > 0) {
+ std::string hostname = name_buffer_.front();
+ name_buffer_.pop();
+ DnsHostInfo* info = &results_[hostname];
+ DCHECK(info->HasHostname(hostname));
+ // We should be in Queued state, so simulate to end of life.
+ info->SetAssignedState(); // Simulate slave assignment.
+ info->SetNoSuchNameState(); // Simulate failed lookup.
+ results_.erase(hostname);
+ }
+ running_slave_count = running_slave_count_;
+ // Release lock, so slaves can finish up.
+ }
+
+ if (running_slave_count) {
+ slaves_have_work_.Broadcast(); // Slaves need to check for termination.
+
+ DWORD result = WaitForMultipleObjects(
+ slave_count_,
+ thread_handles_,
+ TRUE, // Wait for all
+ static_cast<DWORD>(kShutdownWaitTime_.InMilliseconds()));
+
+ DCHECK(result != WAIT_TIMEOUT) << "Some slaves didn't stop";
+ if (WAIT_TIMEOUT == result)
+ return false;
+ }
+ {
+ AutoLock auto_lock(lock_);
+ while (0 < slave_count_--) {
+ if (0 == thread_ids_[slave_count_]) { // Thread terminated.
+ int result = CloseHandle(thread_handles_[slave_count_]);
+ CHECK(0 != result);
+ thread_handles_[slave_count_] = 0;
+ delete slaves_[slave_count_];
+ slaves_[slave_count_] = NULL;
+ }
+ }
+ }
+ return true;
+}
+
+void DnsMaster::DiscardAllResults() {
+ AutoLock auto_lock(lock_);
+ // Delete anything listed so far in this session that shows in about:dns.
+ cache_eviction_map_.clear();
+ cache_hits_.clear();
+
+
+ // Try to delete anything in our work queue.
+ while (!name_buffer_.empty()) {
+ // Emulate processing cycle as though host was not found.
+ std::string hostname = name_buffer_.front();
+ name_buffer_.pop();
+ DnsHostInfo* info = &results_[hostname];
+ DCHECK(info->HasHostname(hostname));
+ info->SetAssignedState();
+ info->SetNoSuchNameState();
+ }
+ // Now every result_ is either resolved, or is being worked on by a slave.
+
+ // Step through result_, recording names of all hosts that can't be erased.
+ // We can't erase anything being worked on by a slave.
+ Results assignees;
+ for (Results::iterator it = results_.begin(); results_.end() != it; ++it) {
+ std::string hostname = it->first;
+ DnsHostInfo* info = &it->second;
+ DCHECK(info->HasHostname(hostname));
+ if (info->is_assigned()) {
+ info->SetPendingDeleteState();
+ assignees[hostname] = *info;
+ }
+ }
+ DCHECK(kSlaveCountMax >= assignees.size());
+ results_.clear();
+ // Put back in the names being worked on by slaves.
+ for (Results::iterator it = assignees.begin(); assignees.end() != it; ++it) {
+ DCHECK(it->second.is_marked_to_delete());
+ results_[it->first] = it->second;
+ }
+}
+
+} // namespace chrome_browser_net
diff --git a/chrome/browser/net/dns_master.h b/chrome/browser/net/dns_master.h
new file mode 100644
index 0000000..45dd00e
--- /dev/null
+++ b/chrome/browser/net/dns_master.h
@@ -0,0 +1,201 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// A DnsMaster object is instantiated once in the browser
+// process, and delivers DNS prefetch assignments (hostnames)
+// to any of several DnsSlave objects.
+// Most hostname lists are sent out by renderer processes, and
+// involve lists of hostnames that *might* be used in the near
+// future by the browsing user. The goal of this class is to
+// cause the underlying DNS structure to lookup a hostname before
+// it is really needed, and hence reduce latency in the standard
+// lookup paths. Since some DNS lookups may take a LONG time, we
+// use several DnsSlave threads to concurrently perform the
+// lookups.
+
+#ifndef CHROME_BROWSER_NET_DNS_MASTER_H__
+#define CHROME_BROWSER_NET_DNS_MASTER_H__
+
+#include <map>
+#include <queue>
+#include <string>
+
+#include "base/condition_variable.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/net/dns_host_info.h"
+#include "chrome/common/net/dns.h"
+#include "googleurl/src/url_canon.h"
+
+namespace chrome_browser_net {
+
+class DnsSlave;
+
+typedef chrome_common_net::NameList NameList;
+typedef std::map<std::string, DnsHostInfo> Results;
+
+class DnsMaster {
+ public:
+
+ explicit DnsMaster(TimeDelta shutdown_wait_time);
+
+ ~DnsMaster() {
+ if (!shutdown_)
+ ShutdownSlaves(); // Ensure we did our cleanup.
+ }
+
+ // ShutdownSlaves() gets all spawned threads to terminate, closes
+ // their handles, and deletes their DnsSlave instances.
+ // Return value of true means all operations succeeded.
+ // Return value of false means that the threads wouldn't terminate,
+ // and that resources may leak. If this returns false, it is best
+ // to NOT delete this DnsMaster, as slave threads may still call into
+ // this object.
+ bool ShutdownSlaves();
+
+ // In some circumstances, for privacy reasons, all results should be
+ // discarded. This method gracefully handles that activity.
+ // Destroy all our internal state, which shows what names we've looked up, and
+ // how long each has taken, etc. etc. We also destroy records of suggesses
+ // (cache hits etc.).
+ void DiscardAllResults();
+
+ // Add hostname(s) to the queue for processing by slaves
+ void ResolveList(const NameList& hostnames);
+ void Resolve(const std::string& hostname);
+
+ // Get latency benefit of the prefetch that we are navigating to.
+ bool AcruePrefetchBenefits(DnsHostInfo* host_info);
+
+ void GetHtmlInfo(std::string* output);
+
+ // For testing only...
+ // Currently testing only provides a crude measure of success.
+ bool WasFound(const std::string& hostname) {
+ AutoLock auto_lock(lock_);
+ return (results_.find(hostname) != results_.end()) &&
+ results_[hostname].was_found();
+ }
+
+ // Accessor methods, used mostly for testing.
+ // Both functions return DnsHostInfo::kNullDuration if name was not yet
+ // processed enough.
+ TimeDelta GetResolutionDuration(const std::string hostname) {
+ AutoLock auto_lock(lock_);
+ if (results_.find(hostname) == results_.end())
+ return DnsHostInfo::kNullDuration;
+ return results_[hostname].resolve_duration();
+ }
+
+ TimeDelta GetQueueDuration(const std::string hostname) {
+ AutoLock auto_lock(lock_);
+ if (results_.find(hostname) == results_.end())
+ return DnsHostInfo::kNullDuration;
+ return results_[hostname].queue_duration();
+ }
+
+ int running_slave_count() {
+ AutoLock auto_lock(lock_);
+ return running_slave_count_;
+ }
+
+ //----------------------------------------------------------------------------
+ // Methods below this line should only be called by slave processes.
+
+ // Thread names can only be set after the thread has been running a bit.
+ void SetSlaveName(int slave_index);
+
+ // GetNextAssignment() gets the next hostname from queue for processing
+ // It is not meant to be public, and should only be used by the slave.
+ // GetNextAssignment() waits on a condition variable if there are no more
+ // names in queue.
+ // Return false if slave thread should terminate.
+ // Return true if slave thread should process the value.
+ bool GetNextAssignment(std::string* hostname);
+
+ // Access methods for use by slave threads to callback with state updates.
+ void SetFoundState(const std::string hostname);
+ void SetNoSuchNameState(const std::string hostname);
+
+ // Notification during ShutdownSlaves.
+ void SetSlaveHasTerminated(int slave_index);
+
+ private:
+ //----------------------------------------------------------------------------
+ // Internal helper functions
+
+ // "PreLocked" means that the caller has already Acquired lock_ in the
+ // following method names.
+ void PreLockedResolve(const std::string& hostname);
+ bool PreLockedCreateNewSlaveIfNeeded(); // Lazy slave processes creation.
+
+ // The number of slave processes that will do DNS prefetching
+ static const int kSlaveCountMax = 8;
+ // Number of slave processes started early (to help with startup prefetch).
+ static const int kSlaveCountMin = 4;
+
+ Lock lock_;
+
+ // name_buffer_ holds a list of names we need to look up.
+ std::queue<std::string> name_buffer_;
+
+ // results_ contains information progress for existing/prior prefetches.
+ Results results_;
+
+ // Signaling slaves to process elements in the queue, or to terminate,
+ // is done using ConditionVariables.
+ ConditionVariable slaves_have_work_;
+
+ int slave_count_; // Count of slave processes started.
+ int running_slave_count_; // Count of slaves process still running.
+
+ // The following arrays are only initialized as
+ // slave_count_ grows (up to the indicated max).
+ DWORD thread_ids_[kSlaveCountMax];
+ HANDLE thread_handles_[kSlaveCountMax];
+ DnsSlave* slaves_[kSlaveCountMax];
+
+ // shutdown_ is set to tell the slaves to terminate.
+ bool shutdown_;
+
+ // The following is the maximum time the ShutdownSlaves method
+ // will wait for all the slave processes to terminate.
+ const TimeDelta kShutdownWaitTime_;
+
+ // A list of successful events resulting from pre-fetching.
+ DnsHostInfo::DnsInfoTable cache_hits_;
+ // A map of hosts that were evicted from our cache (after we prefetched them)
+ // and before the HTTP stack tried to look them up.
+ Results cache_eviction_map_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(DnsMaster);
+};
+
+} // namespace chrome_browser_net
+
+#endif // CHROME_BROWSER_NET_DNS_MASTER_H__
diff --git a/chrome/browser/net/dns_master_unittest.cc b/chrome/browser/net/dns_master_unittest.cc
new file mode 100644
index 0000000..804af04
--- /dev/null
+++ b/chrome/browser/net/dns_master_unittest.cc
@@ -0,0 +1,451 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Multi-threaded tests of DnsMaster and DnsPrefetch slave functionality.
+
+#include <time.h>
+#include <ws2tcpip.h>
+#include <Wspiapi.h> // Needed for win2k compatibility
+
+#include <algorithm>
+#include <map>
+#include <sstream>
+#include <string>
+
+#include "base/spin_wait.h"
+#include "chrome/browser/net/dns_global.h"
+#include "chrome/browser/net/dns_host_info.h"
+#include "chrome/browser/net/dns_slave.h"
+#include "net/base/winsock_init.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+namespace {
+
+class DnsMasterTest : public testing::Test {
+};
+
+typedef chrome_browser_net::DnsMaster DnsMaster;
+typedef chrome_browser_net::DnsPrefetcherInit DnsPrefetcherInit;
+typedef chrome_browser_net::DnsHostInfo DnsHostInfo;
+typedef chrome_browser_net::NameList NameList;
+
+
+//------------------------------------------------------------------------------
+// Provide network function stubs to run tests offline (and avoid the variance
+// of real DNS lookups.
+//------------------------------------------------------------------------------
+
+static void __stdcall fake_free_addr_info(struct addrinfo* ai) {
+ // Kill off the dummy results.
+ EXPECT_TRUE(NULL != ai);
+ delete ai;
+}
+
+static int __stdcall fake_get_addr_info(const char* nodename,
+ const char* servname,
+ const struct addrinfo* hints,
+ struct addrinfo** result) {
+ static Lock lock;
+ int duration;
+ bool was_found;
+ std::string hostname(nodename);
+ // Dummy up *some* return results to pass along.
+ *result = new addrinfo;
+ EXPECT_TRUE(NULL != *result);
+ {
+ AutoLock autolock(lock);
+
+ static bool initialized = false;
+ typedef std::map<std::string, int> Latency;
+ static Latency latency;
+ static std::map<std::string, bool> found;
+ if (!initialized) {
+ initialized = true;
+ // List all known hostnames
+ latency["www.google.com"] = 50;
+ latency["gmail.google.com.com"] = 70;
+ latency["mail.google.com"] = 44;
+ latency["gmail.com"] = 63;
+
+ for (Latency::iterator it = latency.begin(); latency.end() != it; it++) {
+ found[it->first] = true;
+ }
+ } // End static initialization
+
+ was_found = found[hostname];
+
+ if (latency.end() != latency.find(hostname)) {
+ duration = latency[hostname];
+ } else {
+ duration = 500;
+ }
+ // Change latency to simulate cache warming (next latency will be short).
+ latency[hostname] = 1;
+ } // Release lock.
+
+ Sleep(duration);
+
+ return was_found ? 0 : WSAHOST_NOT_FOUND;
+}
+
+static void SetupNetworkInfrastructure() {
+ bool kUseFakeNetwork = true;
+ if (kUseFakeNetwork)
+ chrome_browser_net::SetAddrinfoCallbacks(fake_get_addr_info,
+ fake_free_addr_info);
+}
+
+//------------------------------------------------------------------------------
+// Provide a function to create unique (nonexistant) domains at *every* call.
+//------------------------------------------------------------------------------
+static std::string GetNonexistantDomain() {
+ static std::string postfix = ".google.com";
+ static std::string prefix = "www.";
+ static std::string mid = "datecount";
+
+ static int counter = 0; // Make sure its unique.
+ time_t number = time(NULL);
+ std::ostringstream result;
+ result << prefix << number << mid << ++counter << postfix;
+ return result.str();
+}
+
+//------------------------------------------------------------------------------
+// Use a blocking function to contrast results we get via async services.
+//------------------------------------------------------------------------------
+TimeDelta BlockingDnsLookup(const std::string& hostname) {
+ char* port = "80"; // I may need to get the real port
+ struct addrinfo* result = NULL;
+ Time start = Time::Now();
+
+ // Use the same underlying methods as dns_prefetch_slave does
+ chrome_browser_net::get_getaddrinfo()(hostname.c_str(), port,
+ NULL, &result);
+
+ TimeDelta duration = Time::Now() - start;
+
+ if (result) {
+ chrome_browser_net::get_freeaddrinfo()(result);
+ result = NULL;
+ }
+
+ return duration;
+}
+
+//------------------------------------------------------------------------------
+
+// First test to be sure the OS is caching lookups, which is the whole premise
+// of DNS prefetching.
+TEST(DnsMasterTest, OsCachesLookupsTest) {
+ SetupNetworkInfrastructure();
+ WinsockInit ws_init;
+
+ for (int i = 0; i < 5; i++) {
+ std::string badname;
+ badname = GetNonexistantDomain();
+ TimeDelta duration = BlockingDnsLookup(badname);
+ TimeDelta cached_duration = BlockingDnsLookup(badname);
+ EXPECT_TRUE(duration > cached_duration);
+ }
+}
+
+TEST(DnsMasterTest, StartupShutdownTest) {
+ DnsMaster testing_master(TimeDelta::FromMilliseconds(5000));
+
+ // With no threads, we should have no problem doing a shutdown.
+ EXPECT_TRUE(testing_master.ShutdownSlaves());
+}
+
+TEST(DnsMasterTest, BenefitLookupTest) {
+ SetupNetworkInfrastructure();
+ WinsockInit ws_init;
+ DnsPrefetcherInit dns_init(NULL); // Creates global service .
+ DnsMaster testing_master(TimeDelta::FromMilliseconds(5000));
+
+ std::string goog("www.google.com"),
+ goog2("gmail.google.com.com"),
+ goog3("mail.google.com"),
+ goog4("gmail.com");
+ DnsHostInfo goog_info, goog2_info, goog3_info, goog4_info;
+
+ // Simulate getting similar names from a network observer
+ goog_info.SetHostname(goog);
+ goog2_info.SetHostname(goog2);
+ goog3_info.SetHostname(goog3);
+ goog4_info.SetHostname(goog4);
+
+ goog_info.SetStartedState();
+ goog2_info.SetStartedState();
+ goog3_info.SetStartedState();
+ goog4_info.SetStartedState();
+
+ goog_info.SetFinishedState(true);
+ goog2_info.SetFinishedState(true);
+ goog3_info.SetFinishedState(true);
+ 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);
+
+ // First only cause a minimal set of threads to start up.
+ // Currently we actually start 4 threads when we get called with an array
+ testing_master.ResolveList(names);
+
+ // Wait for some resoultion for each google.
+ SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
+ testing_master.GetResolutionDuration(goog).InMilliseconds());
+ SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
+ testing_master.GetResolutionDuration(goog2).InMilliseconds());
+ SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
+ testing_master.GetResolutionDuration(goog3).InMilliseconds());
+ SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
+ testing_master.GetResolutionDuration(goog4).InMilliseconds());
+
+ EXPECT_EQ(std::min(names.size(),
+ 4u /* chrome_browser_net::DnsMaster::kSlaveCountMin */ ),
+ testing_master.running_slave_count());
+
+ EXPECT_TRUE(testing_master.WasFound(goog));
+ EXPECT_TRUE(testing_master.WasFound(goog2));
+ EXPECT_TRUE(testing_master.WasFound(goog3));
+ EXPECT_TRUE(testing_master.WasFound(goog4));
+
+ // 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).
+
+ // Simulate actual navigation, and acrue the benefit for "helping" the DNS
+ // part of the navigation.
+ EXPECT_TRUE(testing_master.AcruePrefetchBenefits(&goog_info));
+ EXPECT_TRUE(testing_master.AcruePrefetchBenefits(&goog2_info));
+ EXPECT_TRUE(testing_master.AcruePrefetchBenefits(&goog3_info));
+ EXPECT_TRUE(testing_master.AcruePrefetchBenefits(&goog4_info));
+
+ // Benefits can ONLY be reported once (for the first navigation).
+ EXPECT_FALSE(testing_master.AcruePrefetchBenefits(&goog_info));
+ EXPECT_FALSE(testing_master.AcruePrefetchBenefits(&goog2_info));
+ EXPECT_FALSE(testing_master.AcruePrefetchBenefits(&goog3_info));
+ EXPECT_FALSE(testing_master.AcruePrefetchBenefits(&goog4_info));
+
+ // Ensure a clean shutdown.
+ EXPECT_TRUE(testing_master.ShutdownSlaves());
+}
+
+TEST(DnsMasterTest, DISABLED_SingleSlaveLookupTest) {
+ SetupNetworkInfrastructure();
+ WinsockInit ws_init;
+ DnsPrefetcherInit dns_init(NULL); // Creates global service.
+ DnsMaster testing_master(TimeDelta::FromMilliseconds(5000));
+
+ std::string goog("www.google.com"),
+ goog2("gmail.google.com.com"),
+ goog3("mail.google.com"),
+ goog4("gmail.com");
+ std::string bad1(GetNonexistantDomain()),
+ bad2(GetNonexistantDomain());
+
+ // Warm up local OS cache.
+ BlockingDnsLookup(goog);
+
+ NameList names;
+ names.insert(names.end(), goog);
+ names.insert(names.end(), bad1);
+ names.insert(names.end(), bad2);
+
+ // First only cause a single thread to start up
+ testing_master.ResolveList(names);
+
+ // Wait for some resoultion for google.
+ SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
+ testing_master.GetResolutionDuration(goog).InMilliseconds());
+
+ EXPECT_TRUE(testing_master.WasFound(goog));
+ EXPECT_FALSE(testing_master.WasFound(bad1));
+ EXPECT_FALSE(testing_master.WasFound(bad2));
+ // Verify the reason it is not found is that it is still being proceessed.
+ // Negative time mean no resolution yet.
+ EXPECT_GT(0, testing_master.GetResolutionDuration(bad2).InMilliseconds());
+
+ // Spin long enough that we *do* find the resolution of bad2.
+ SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
+ testing_master.GetResolutionDuration(bad2).InMilliseconds());
+
+ // Verify both fictitious names are resolved by now.
+ // Typical random name takes about 20-30 ms
+ EXPECT_LT(0, testing_master.GetResolutionDuration(bad1).InMilliseconds());
+ EXPECT_LT(0, testing_master.GetResolutionDuration(bad2).InMilliseconds());
+ EXPECT_FALSE(testing_master.WasFound(bad1));
+ EXPECT_FALSE(testing_master.WasFound(bad2));
+
+ EXPECT_EQ(1, testing_master.running_slave_count());
+
+ // With just one thread (doing nothing now), ensure a clean shutdown.
+ EXPECT_TRUE(testing_master.ShutdownSlaves());
+}
+
+TEST(DnsMasterTest, DISABLED_MultiThreadedLookupTest) {
+ SetupNetworkInfrastructure();
+ WinsockInit ws_init;
+ DnsMaster testing_master(TimeDelta::FromSeconds(30));
+ DnsPrefetcherInit dns_init(NULL);
+
+ std::string goog("www.google.com"),
+ goog2("gmail.google.com.com"),
+ goog3("mail.google.com"),
+ goog4("gmail.com");
+ std::string bad1(GetNonexistantDomain()),
+ bad2(GetNonexistantDomain());
+
+ 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);
+
+ // Warm up the *OS* cache for all the goog domains.
+ BlockingDnsLookup(goog);
+ BlockingDnsLookup(goog2);
+ BlockingDnsLookup(goog3);
+ BlockingDnsLookup(goog4);
+
+ // Get all 8 threads running by calling many times before queue is handled.
+ for (int i = 0; i < 10; i++) {
+ testing_master.ResolveList(names);
+ }
+
+ Sleep(10); // Allow time for async DNS to get answers.
+
+ EXPECT_TRUE(testing_master.WasFound(goog));
+ EXPECT_TRUE(testing_master.WasFound(goog3));
+ EXPECT_TRUE(testing_master.WasFound(goog2));
+ EXPECT_TRUE(testing_master.WasFound(goog4));
+ EXPECT_FALSE(testing_master.WasFound(bad1));
+ EXPECT_FALSE(testing_master.WasFound(bad2));
+
+ EXPECT_EQ(8, testing_master.running_slave_count());
+
+ EXPECT_TRUE(testing_master.ShutdownSlaves());
+}
+
+TEST(DnsMasterTest, DISABLED_MultiThreadedSpeedupTest) {
+ SetupNetworkInfrastructure();
+ WinsockInit ws_init;
+ DnsMaster testing_master(TimeDelta::FromSeconds(30));
+ DnsPrefetcherInit dns_init(NULL);
+
+ std::string goog("www.google.com"),
+ goog2("gmail.google.com.com"),
+ goog3("mail.google.com"),
+ goog4("gmail.com");
+ std::string bad1(GetNonexistantDomain()),
+ bad2(GetNonexistantDomain()),
+ bad3(GetNonexistantDomain()),
+ bad4(GetNonexistantDomain());
+
+ NameList names;
+ names.insert(names.end(), goog);
+ names.insert(names.end(), bad1);
+ names.insert(names.end(), bad2);
+ names.insert(names.end(), goog3);
+ names.insert(names.end(), goog2);
+ names.insert(names.end(), bad3);
+ names.insert(names.end(), bad4);
+ names.insert(names.end(), goog4);
+
+ // First cause a lookup using a single thread.
+ testing_master.ResolveList(names);
+
+ // Wait for some resoultion for google.
+ SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
+ testing_master.GetResolutionDuration(goog).InMilliseconds());
+
+ EXPECT_TRUE(testing_master.WasFound(goog));
+ EXPECT_FALSE(testing_master.WasFound(bad1));
+ EXPECT_FALSE(testing_master.WasFound(bad2));
+ // ...and due to delay in geting resolution of bad names, the single slave
+ // thread won't have time to finish the list.
+ EXPECT_FALSE(testing_master.WasFound(goog3));
+ EXPECT_FALSE(testing_master.WasFound(goog2));
+ EXPECT_FALSE(testing_master.WasFound(goog4));
+
+ EXPECT_EQ(1, testing_master.running_slave_count());
+
+ // Get all 8 threads running by calling many times before queue is handled.
+ names.clear();
+ for (int i = 0; i < 10; i++)
+ testing_master.Resolve(GetNonexistantDomain());
+
+ // Wait long enough for all the goog's to be resolved.
+ // They should all take about the same time, and run in parallel.
+ SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
+ testing_master.GetResolutionDuration(goog2).InMilliseconds());
+ SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
+ testing_master.GetResolutionDuration(goog3).InMilliseconds());
+ SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
+ testing_master.GetResolutionDuration(goog4).InMilliseconds());
+
+ EXPECT_TRUE(testing_master.WasFound(goog3));
+ EXPECT_TRUE(testing_master.WasFound(goog2));
+ EXPECT_TRUE(testing_master.WasFound(goog4));
+
+ EXPECT_FALSE(testing_master.WasFound(bad1));
+ EXPECT_FALSE(testing_master.WasFound(bad2)); // Perhaps not even decided.
+
+ // Queue durations should be distinct from when 1 slave was working.
+ EXPECT_GT(testing_master.GetQueueDuration(goog3).InMilliseconds(),
+ testing_master.GetQueueDuration(goog).InMilliseconds());
+ EXPECT_GT(testing_master.GetQueueDuration(goog4).InMilliseconds(),
+ testing_master.GetQueueDuration(goog).InMilliseconds());
+
+ // Give bad names a chance to be determined as unresolved.
+ SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
+ testing_master.GetResolutionDuration(bad1).InMilliseconds());
+ SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 <=
+ testing_master.GetResolutionDuration(bad2).InMilliseconds());
+
+
+ // Well known names should resolve faster than bad names.
+ EXPECT_GE(testing_master.GetResolutionDuration(bad1).InMilliseconds(),
+ testing_master.GetResolutionDuration(goog).InMilliseconds());
+
+ EXPECT_GE(testing_master.GetResolutionDuration(bad2).InMilliseconds(),
+ testing_master.GetResolutionDuration(goog4).InMilliseconds());
+
+ EXPECT_EQ(8, testing_master.running_slave_count());
+
+ EXPECT_TRUE(testing_master.ShutdownSlaves());
+}
+
+} // namespace
diff --git a/chrome/browser/net/dns_slave.cc b/chrome/browser/net/dns_slave.cc
new file mode 100644
index 0000000..c3ec818
--- /dev/null
+++ b/chrome/browser/net/dns_slave.cc
@@ -0,0 +1,115 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// See header file for description of class
+
+#include <ws2tcpip.h>
+
+#include "chrome/browser/net/dns_slave.h"
+
+#include <Wspiapi.h> // Needed for win2k compatibility
+
+#include "base/logging.h"
+#include "chrome/browser/net/dns_host_info.h"
+#include "chrome/browser/net/dns_master.h"
+
+
+namespace chrome_browser_net {
+
+//------------------------------------------------------------------------------
+// We supply a functions for stubbing network callbacks, so that offline testing
+// can be performed.
+//------------------------------------------------------------------------------
+static GetAddrInfoFunction get_addr = getaddrinfo;
+static FreeAddrInfoFunction free_addr = freeaddrinfo;
+void SetAddrinfoCallbacks(GetAddrInfoFunction getaddrinfo,
+ FreeAddrInfoFunction freeaddrinfo) {
+ get_addr = getaddrinfo;
+ free_addr = freeaddrinfo;
+}
+
+GetAddrInfoFunction get_getaddrinfo() {return get_addr;}
+FreeAddrInfoFunction get_freeaddrinfo() {return free_addr;}
+
+
+//------------------------------------------------------------------------------
+// This is the entry method used by DnsMaster to start our thread.
+//------------------------------------------------------------------------------
+
+DWORD __stdcall DnsSlave::ThreadStart(void* pThis) {
+ DnsSlave* slave_instance = reinterpret_cast<DnsSlave*>(pThis);
+ return slave_instance->Run();
+}
+
+//------------------------------------------------------------------------------
+// The following are methods on the DnsPrefetch class.
+//------------------------------------------------------------------------------
+
+unsigned DnsSlave::Run() {
+ // We have to be running to set the thread name.
+ master_->SetSlaveName(slave_index_);
+
+ while (master_->GetNextAssignment(&hostname_)) {
+ BlockingDnsLookup();
+ }
+ // GetNextAssignment() fails when we are told to terminate.
+ master_->SetSlaveHasTerminated(slave_index_);
+ return 0;
+}
+
+void DnsSlave::BlockingDnsLookup() {
+ const char* port = "80"; // I may need to get the real port
+ addrinfo* result = NULL;
+
+ int error_code = get_addr(hostname_.c_str(), port, NULL, &result);
+
+ // Note that since info has value semantics, I need to ask
+ // master_ to set the new states atomically in its map.
+ switch (error_code) {
+ case 0:
+ master_->SetFoundState(hostname_);
+ break;
+
+ default:
+ DCHECK_EQ(0, error_code) << "surprising output" ;
+ // fall through
+
+ case WSAHOST_NOT_FOUND:
+ master_->SetNoSuchNameState(hostname_);
+ break;
+ }
+
+ // We don't save results, so lets free them...
+ if (result) {
+ free_addr(result);
+ result = NULL;
+ }
+}
+
+} // namespace chrome_browser_net
diff --git a/chrome/browser/net/dns_slave.h b/chrome/browser/net/dns_slave.h
new file mode 100644
index 0000000..12c85ef
--- /dev/null
+++ b/chrome/browser/net/dns_slave.h
@@ -0,0 +1,96 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// A DnsSlave object processes hostname lookups
+// via DNS on a single thread, waiting for that blocking
+// call to complete, and then getting its next hostname
+// from its associated DnsMaster object.
+// Since this class only is concerned with prefetching
+// to warm the underlying DNS cache, the actual IP
+// does not need to be recorded. It is necessary to record
+// when the lookup finished, so that the associated DnsMaster
+// won't (wastefully) ask for the same name in "too short a
+// period of time."
+// This class does no "de-duping," and merely slavishly services
+// items supplied by its DnsMaster.
+
+#ifndef CHROME_BROWSER_NET_DNS_SLAVE_H__
+#define CHROME_BROWSER_NET_DNS_SLAVE_H__
+
+#include <windows.h>
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace chrome_browser_net {
+
+class DnsMaster;
+class DnsSlave;
+
+// Support testing infrastructure, and allow off-line testing.
+typedef void (__stdcall *FreeAddrInfoFunction)(struct addrinfo* ai);
+typedef int (__stdcall *GetAddrInfoFunction)(const char* nodename,
+ const char* servname,
+ const struct addrinfo* hints,
+ struct addrinfo** result);
+void SetAddrinfoCallbacks(GetAddrInfoFunction getaddrinfo,
+ FreeAddrInfoFunction freeaddrinfo);
+
+GetAddrInfoFunction get_getaddrinfo();
+FreeAddrInfoFunction get_freeaddrinfo();
+
+class DnsSlave {
+ public:
+ DnsSlave(DnsMaster* master, int slave_index)
+ : slave_index_(slave_index),
+ master_(master) {
+ }
+
+ ~DnsSlave() {
+ master_ = NULL;
+ }
+
+ static DWORD WINAPI ThreadStart(void* pThis);
+
+ unsigned Run();
+
+ private:
+ std::string hostname_; // Name being looked up.
+
+ DnsMaster* master_; // Master that started us.
+ int slave_index_; // Our index into DnsMaster's array.
+
+ void BlockingDnsLookup();
+
+ DISALLOW_EVIL_CONSTRUCTORS(DnsSlave);
+};
+
+} // namespace chrome_browser_net
+
+#endif // CHROME_BROWSER_NET_DNS_SLAVE_H__