summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/io_thread.cc61
-rw-r--r--net/base/host_resolver_impl.cc132
-rw-r--r--net/base/host_resolver_impl.h24
-rw-r--r--net/base/net_util.cc70
4 files changed, 238 insertions, 49 deletions
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc
index 2ea8195..d2ce04e 100644
--- a/chrome/browser/io_thread.cc
+++ b/chrome/browser/io_thread.cc
@@ -27,44 +27,49 @@ namespace {
net::HostResolver* CreateGlobalHostResolver(
net::NetworkChangeNotifier* network_change_notifier) {
- net::HostResolver* global_host_resolver = NULL;
-
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
-
- global_host_resolver =
+ net::HostResolver* global_host_resolver =
net::CreateSystemHostResolver(network_change_notifier);
+ // Determine if we should disable IPv6 support.
if (!command_line.HasSwitch(switches::kEnableIPv6)) {
- // Measure impact of allowing IPv6 support without probing.
- const FieldTrial::Probability kDivisor = 100;
- const FieldTrial::Probability kProbability = 50; // 50% probability.
- FieldTrial* trial = new FieldTrial("IPv6_Probe", kDivisor);
- int skip_group = trial->AppendGroup("_IPv6_probe_skipped", kProbability);
- trial->AppendGroup("_IPv6_probe_done",
- FieldTrial::kAllRemainingProbability);
- bool use_ipv6_probe = (trial->group() != skip_group);
-
- // Perform probe, and then optionally use result to disable IPv6.
- // Some users report confused OS handling of IPv6, leading to large
- // latency. If we can show that IPv6 is not supported, then disabliing it
- // will work around such problems.
- if ((!net::IPv6Supported() && use_ipv6_probe) ||
- command_line.HasSwitch(switches::kDisableIPv6))
+ if (command_line.HasSwitch(switches::kDisableIPv6)) {
global_host_resolver->SetDefaultAddressFamily(net::ADDRESS_FAMILY_IPV4);
+ } else {
+ net::HostResolverImpl* host_resolver_impl =
+ global_host_resolver->GetAsHostResolverImpl();
+ if (host_resolver_impl != NULL) {
+ // (optionally) Use probe to decide if support is warranted.
+
+ // Measure impact of probing to allow IPv6.
+ // Some users report confused OS handling of IPv6, leading to large
+ // latency. If we can show that IPv6 is not supported, then disabliing
+ // it will work around such problems.
+ const FieldTrial::Probability kDivisor = 100;
+ const FieldTrial::Probability kProbability = 50; // 50% probability.
+ FieldTrial* trial = new FieldTrial("IPv6_Probe", kDivisor);
+ int skip_group = trial->AppendGroup("_IPv6_probe_skipped",
+ kProbability);
+ trial->AppendGroup("_IPv6_probe_done",
+ FieldTrial::kAllRemainingProbability);
+ bool use_ipv6_probe = (trial->group() != skip_group);
+ if (use_ipv6_probe)
+ host_resolver_impl->ProbeIPv6Support();
+ }
+ }
}
// If hostname remappings were specified on the command-line, layer these
// rules on top of the real host resolver. This allows forwarding all requests
// through a designated test server.
- if (command_line.HasSwitch(switches::kHostResolverRules)) {
- net::MappedHostResolver* remapped_resolver =
- new net::MappedHostResolver(global_host_resolver);
- global_host_resolver = remapped_resolver;
- remapped_resolver->SetRulesFromString(
- command_line.GetSwitchValueASCII(switches::kHostResolverRules));
- }
-
- return global_host_resolver;
+ if (!command_line.HasSwitch(switches::kHostResolverRules))
+ return global_host_resolver;
+
+ net::MappedHostResolver* remapped_resolver =
+ new net::MappedHostResolver(global_host_resolver);
+ remapped_resolver->SetRulesFromString(
+ command_line.GetSwitchValueASCII(switches::kHostResolverRules));
+ return remapped_resolver;
}
} // namespace
diff --git a/net/base/host_resolver_impl.cc b/net/base/host_resolver_impl.cc
index d5dce2f..2120332 100644
--- a/net/base/host_resolver_impl.cc
+++ b/net/base/host_resolver_impl.cc
@@ -21,6 +21,7 @@
#include "net/base/host_resolver_proc.h"
#include "net/base/net_log.h"
#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
#include "net/base/network_change_notifier.h"
#if defined(OS_WIN)
@@ -192,7 +193,8 @@ class HostResolverImpl::Job
public:
Job(int id, HostResolverImpl* resolver, const Key& key,
RequestsTrace* requests_trace)
- : id_(id), key_(key),
+ : id_(id),
+ key_(key),
resolver_(resolver),
origin_loop_(MessageLoop::current()),
resolver_proc_(resolver->effective_resolver_proc()),
@@ -421,6 +423,91 @@ class HostResolverImpl::Job
//-----------------------------------------------------------------------------
+// This class represents a request to the worker pool for a "probe for IPv6
+// support" call.
+class HostResolverImpl::IPv6ProbeJob
+ : public base::RefCountedThreadSafe<HostResolverImpl::IPv6ProbeJob> {
+ public:
+ explicit IPv6ProbeJob(HostResolverImpl* resolver)
+ : resolver_(resolver),
+ origin_loop_(MessageLoop::current()) {
+ }
+
+ void Start() {
+ DCHECK(IsOnOriginThread());
+ const bool IS_SLOW = true;
+ WorkerPool::PostTask(
+ FROM_HERE, NewRunnableMethod(this, &IPv6ProbeJob::DoProbe), IS_SLOW);
+ }
+
+ // Cancels the current job.
+ void Cancel() {
+ DCHECK(IsOnOriginThread());
+ resolver_ = NULL; // Read/write ONLY on origin thread.
+ {
+ AutoLock locked(origin_loop_lock_);
+ // Origin loop may be destroyed before we can use it!
+ origin_loop_ = NULL;
+ }
+ }
+
+ bool was_cancelled() const {
+ DCHECK(IsOnOriginThread());
+ return resolver_ == NULL;
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<HostResolverImpl::IPv6ProbeJob>;
+
+ ~IPv6ProbeJob() {
+ }
+
+ // Run on worker thread.
+ void DoProbe() {
+ // Do actual testing on this thread, as it takes 40-100ms.
+ AddressFamily family = IPv6Supported() ? ADDRESS_FAMILY_UNSPECIFIED
+ : ADDRESS_FAMILY_IPV4;
+
+ Task* reply = NewRunnableMethod(this, &IPv6ProbeJob::OnProbeComplete,
+ family);
+
+ // The origin loop could go away while we are trying to post to it, so we
+ // need to call its PostTask method inside a lock. See ~HostResolver.
+ {
+ AutoLock locked(origin_loop_lock_);
+ if (origin_loop_) {
+ origin_loop_->PostTask(FROM_HERE, reply);
+ return;
+ }
+ }
+
+ // We didn't post, so delete the reply.
+ delete reply;
+ }
+
+ // Callback for when DoProbe() completes (runs on origin thread).
+ void OnProbeComplete(AddressFamily address_family) {
+ DCHECK(IsOnOriginThread());
+ if (!was_cancelled())
+ resolver_->IPv6ProbeSetDefaultAddressFamily(address_family);
+ }
+
+ bool IsOnOriginThread() const {
+ return !MessageLoop::current() || origin_loop_ == MessageLoop::current();
+ }
+
+ // Used/set only on origin thread.
+ HostResolverImpl* resolver_;
+
+ // Used to post ourselves onto the origin thread.
+ Lock origin_loop_lock_;
+ MessageLoop* origin_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(IPv6ProbeJob);
+};
+
+//-----------------------------------------------------------------------------
+
// We rely on the priority enum values being sequential having starting at 0,
// and increasing for lower priorities.
COMPILE_ASSERT(HIGHEST == 0u &&
@@ -581,7 +668,8 @@ HostResolverImpl::HostResolverImpl(
resolver_proc_(resolver_proc),
default_address_family_(ADDRESS_FAMILY_UNSPECIFIED),
shutdown_(false),
- network_change_notifier_(network_change_notifier) {
+ network_change_notifier_(network_change_notifier),
+ ipv6_probe_monitoring_(false) {
DCHECK_GT(max_jobs, 0u);
// It is cumbersome to expose all of the constraints in the constructor,
@@ -598,6 +686,8 @@ HostResolverImpl::HostResolverImpl(
HostResolverImpl::~HostResolverImpl() {
// Cancel the outstanding jobs. Those jobs may contain several attached
// requests, which will also be cancelled.
+ DiscardIPv6ProbeJob();
+
for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
it->second->Cancel();
@@ -742,6 +832,18 @@ void HostResolverImpl::RemoveObserver(HostResolver::Observer* observer) {
observers_.erase(it);
}
+void HostResolverImpl::SetDefaultAddressFamily(AddressFamily address_family) {
+ ipv6_probe_monitoring_ = false;
+ DiscardIPv6ProbeJob();
+ default_address_family_ = address_family;
+}
+
+void HostResolverImpl::ProbeIPv6Support() {
+ DCHECK(!ipv6_probe_monitoring_);
+ ipv6_probe_monitoring_ = true;
+ OnIPAddressChanged(); // Give initial setup call.
+}
+
void HostResolverImpl::Shutdown() {
shutdown_ = true;
@@ -975,6 +1077,32 @@ void HostResolverImpl::OnCancelRequest(const BoundNetLog& net_log,
void HostResolverImpl::OnIPAddressChanged() {
if (cache_.get())
cache_->clear();
+ if (ipv6_probe_monitoring_) {
+ DiscardIPv6ProbeJob();
+ ipv6_probe_job_ = new IPv6ProbeJob(this);
+ ipv6_probe_job_->Start();
+ }
+}
+
+void HostResolverImpl::DiscardIPv6ProbeJob() {
+ if (ipv6_probe_job_.get()) {
+ ipv6_probe_job_->Cancel();
+ ipv6_probe_job_ = NULL;
+ }
+}
+
+void HostResolverImpl::IPv6ProbeSetDefaultAddressFamily(
+ AddressFamily address_family) {
+ DCHECK(address_family == ADDRESS_FAMILY_UNSPECIFIED ||
+ address_family == ADDRESS_FAMILY_IPV4);
+ if (default_address_family_ != address_family)
+ LOG(INFO) << "IPv6Probe forced AddressFamily setting to "
+ << ((address_family == ADDRESS_FAMILY_UNSPECIFIED)
+ ? "ADDRESS_FAMILY_UNSPECIFIED"
+ : "ADDRESS_FAMILY_IPV4");
+ default_address_family_ = address_family;
+ // Drop reference since the job has called us back.
+ DiscardIPv6ProbeJob();
}
// static
diff --git a/net/base/host_resolver_impl.h b/net/base/host_resolver_impl.h
index de2cc14..9652072 100644
--- a/net/base/host_resolver_impl.h
+++ b/net/base/host_resolver_impl.h
@@ -89,9 +89,12 @@ class HostResolverImpl : public HostResolver,
virtual void AddObserver(HostResolver::Observer* observer);
virtual void RemoveObserver(HostResolver::Observer* observer);
- virtual void SetDefaultAddressFamily(AddressFamily address_family) {
- default_address_family_ = address_family;
- }
+ // Set address family, and disable IPv6 probe support.
+ virtual void SetDefaultAddressFamily(AddressFamily address_family);
+
+ // Continuously observe whether IPv6 is supported, and set the allowable
+ // address family to IPv4 iff IPv6 is not supported.
+ void ProbeIPv6Support();
virtual HostResolverImpl* GetAsHostResolverImpl() { return this; }
@@ -130,6 +133,7 @@ class HostResolverImpl : public HostResolver,
private:
class Job;
class JobPool;
+ class IPv6ProbeJob;
class Request;
class RequestsTrace;
typedef std::vector<Request*> RequestsList;
@@ -179,6 +183,12 @@ class HostResolverImpl : public HostResolver,
// NetworkChangeNotifier::Observer methods:
virtual void OnIPAddressChanged();
+ // Notify IPv6ProbeJob not to call back, and discard reference to the job.
+ void DiscardIPv6ProbeJob();
+
+ // Callback from IPv6 probe activity.
+ void IPv6ProbeSetDefaultAddressFamily(AddressFamily address_family);
+
// Returns true if the constraints for |pool| are met, and a new job can be
// created for this pool.
bool CanCreateJobForPool(const JobPool& pool) const;
@@ -247,6 +257,14 @@ class HostResolverImpl : public HostResolver,
scoped_refptr<RequestsTrace> requests_trace_;
+ // Indicate if probing is done after each network change event to set address
+ // family.
+ // When false, explicit setting of address family is used.
+ bool ipv6_probe_monitoring_;
+
+ // The last un-cancelled IPv6ProbeJob (if any).
+ scoped_refptr<IPv6ProbeJob> ipv6_probe_job_;
+
DISALLOW_COPY_AND_ASSIGN(HostResolverImpl);
};
diff --git a/net/base/net_util.cc b/net/base/net_util.cc
index 843c80a..29d804e 100644
--- a/net/base/net_util.cc
+++ b/net/base/net_util.cc
@@ -21,9 +21,11 @@
#include <ws2tcpip.h>
#include <wspiapi.h> // Needed for Win2k compat.
#elif defined(OS_POSIX)
+#include <fcntl.h>
+#include <ifaddrs.h>
#include <netdb.h>
+#include <net/if.h>
#include <sys/socket.h>
-#include <fcntl.h>
#endif
#include "base/base64.h"
@@ -258,7 +260,7 @@ bool DecodeBQEncoding(const std::string& part, RFC2047EncodingType enc_type,
bool DecodeWord(const std::string& encoded_word,
const std::string& referrer_charset,
- bool *is_rfc2047,
+ bool* is_rfc2047,
std::string* output) {
if (!IsStringASCII(encoded_word)) {
// Try UTF-8, referrer_charset and the native OS default charset in turn.
@@ -1552,15 +1554,21 @@ void SetExplicitlyAllowedPorts(const std::wstring& allowed_ports) {
enum IPv6SupportStatus {
IPV6_CANNOT_CREATE_SOCKETS,
IPV6_CAN_CREATE_SOCKETS,
+ IPV6_GETIFADDRS_FAILED,
+ IPV6_GLOBAL_ADDRESS_MISSING,
+ IPV6_GLOBAL_ADDRESS_PRESENT,
IPV6_SUPPORT_MAX // Bounding values for enumeration.
};
static void IPv6SupportResults(IPv6SupportStatus result) {
static bool run_once = false;
- if (run_once)
- return;
- run_once = true;
- UMA_HISTOGRAM_ENUMERATION("Net.IPv6Status", result, IPV6_SUPPORT_MAX);
+ if (!run_once) {
+ run_once = true;
+ UMA_HISTOGRAM_ENUMERATION("Net.IPv6Status", result, IPV6_SUPPORT_MAX);
+ } else {
+ UMA_HISTOGRAM_ENUMERATION("Net.IPv6Status_retest", result,
+ IPV6_SUPPORT_MAX);
+ }
}
// TODO(jar): The following is a simple estimate of IPv6 support. We may need
@@ -1568,27 +1576,58 @@ static void IPv6SupportResults(IPv6SupportStatus result) {
// static
bool IPv6Supported() {
#if defined(OS_POSIX)
- int test_socket;
-
- test_socket = socket(AF_INET6, SOCK_STREAM, 0);
+ int test_socket = socket(AF_INET6, SOCK_STREAM, 0);
if (test_socket == -1) {
IPv6SupportResults(IPV6_CANNOT_CREATE_SOCKETS);
return false;
}
-
close(test_socket);
- IPv6SupportResults(IPV6_CAN_CREATE_SOCKETS);
+
+ // Check to see if any interface has a IPv6 address.
+ struct ifaddrs* interface_addr = NULL;
+ int rv = getifaddrs(&interface_addr);
+ if (rv != 0) {
+ IPv6SupportResults(IPV6_GETIFADDRS_FAILED);
+ return true; // Don't yet block IPv6.
+ }
+
+ bool found_ipv6 = false;
+ for (struct ifaddrs* interface = interface_addr;
+ interface != NULL;
+ interface = interface->ifa_next) {
+ if (!(IFF_UP & interface->ifa_flags))
+ continue;
+ if (IFF_LOOPBACK & interface->ifa_flags)
+ continue;
+ struct sockaddr* addr = interface->ifa_addr;
+ if (!addr)
+ continue;
+ if (addr->sa_family != AF_INET6)
+ continue;
+ // Safe cast since this is AF_INET6.
+ struct sockaddr_in6* addr_in6 =
+ reinterpret_cast<struct sockaddr_in6*>(addr);
+ struct in6_addr* sin6_addr = &addr_in6->sin6_addr;
+ if (IN6_IS_ADDR_LOOPBACK(sin6_addr) || IN6_IS_ADDR_LINKLOCAL(sin6_addr))
+ continue;
+ found_ipv6 = true;
+ break;
+ }
+ freeifaddrs(interface_addr);
+ if (!found_ipv6) {
+ IPv6SupportResults(IPV6_GLOBAL_ADDRESS_MISSING);
+ return false;
+ }
+
+ IPv6SupportResults(IPV6_GLOBAL_ADDRESS_PRESENT);
return true;
#elif defined(OS_WIN)
EnsureWinsockInit();
- SOCKET test_socket;
-
- test_socket = socket(AF_INET6, SOCK_STREAM, 0);
+ SOCKET test_socket = socket(AF_INET6, SOCK_STREAM, 0);
if (test_socket == INVALID_SOCKET) {
IPv6SupportResults(IPV6_CANNOT_CREATE_SOCKETS);
return false;
}
-
closesocket(test_socket);
IPv6SupportResults(IPV6_CAN_CREATE_SOCKETS);
return true;
@@ -1598,5 +1637,4 @@ bool IPv6Supported() {
#endif // defined(various platforms)
}
-
} // namespace net