diff options
-rw-r--r-- | chrome/browser/io_thread.cc | 61 | ||||
-rw-r--r-- | net/base/host_resolver_impl.cc | 132 | ||||
-rw-r--r-- | net/base/host_resolver_impl.h | 24 | ||||
-rw-r--r-- | net/base/net_util.cc | 70 |
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 |