io_thread.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
1// Copyright (c) 2010 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/io_thread.h" 6#include "base/command_line.h" 7#include "base/leak_tracker.h" 8#include "base/logging.h" 9#include "chrome/browser/browser_process.h" 10#include "chrome/browser/chrome_thread.h" 11#include "chrome/browser/gpu_process_host.h" 12#include "chrome/browser/net/chrome_net_log.h" 13#include "chrome/browser/net/predictor_api.h" 14#include "chrome/browser/net/passive_log_collector.h" 15#include "chrome/common/chrome_switches.h" 16#include "chrome/common/net/url_fetcher.h" 17#include "net/base/mapped_host_resolver.h" 18#include "net/base/host_cache.h" 19#include "net/base/host_resolver.h" 20#include "net/base/host_resolver_impl.h" 21#include "net/base/net_util.h" 22#include "net/http/http_auth_filter.h" 23#include "net/http/http_auth_handler_factory.h" 24#include "net/http/http_auth_handler_negotiate.h" 25 26namespace { 27 28net::HostResolver* CreateGlobalHostResolver() { 29 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 30 31 size_t parallelism = net::HostResolver::kDefaultParallelism; 32 33 // Use the concurrency override from the command-line, if any. 34 if (command_line.HasSwitch(switches::kHostResolverParallelism)) { 35 std::string s = 36 command_line.GetSwitchValueASCII(switches::kHostResolverParallelism); 37 38 // Parse the switch (it should be a positive integer formatted as decimal). 39 int n; 40 if (StringToInt(s, &n) && n > 0) { 41 parallelism = static_cast<size_t>(n); 42 } else { 43 LOG(ERROR) << "Invalid switch for host resolver parallelism: " << s; 44 } 45 } 46 47 net::HostResolver* global_host_resolver = 48 net::CreateSystemHostResolver(parallelism); 49 50 // Determine if we should disable IPv6 support. 51 if (!command_line.HasSwitch(switches::kEnableIPv6)) { 52 if (command_line.HasSwitch(switches::kDisableIPv6)) { 53 global_host_resolver->SetDefaultAddressFamily(net::ADDRESS_FAMILY_IPV4); 54 } else { 55 net::HostResolverImpl* host_resolver_impl = 56 global_host_resolver->GetAsHostResolverImpl(); 57 if (host_resolver_impl != NULL) { 58 // (optionally) Use probe to decide if support is warranted. 59 bool use_ipv6_probe = true; 60 61#if defined(OS_WIN) 62 // Measure impact of probing to allow IPv6. 63 // Some users report confused OS handling of IPv6, leading to large 64 // latency. If we can show that IPv6 is not supported, then disabliing 65 // it will work around such problems. This is the test of the probe. 66 const FieldTrial::Probability kDivisor = 100; 67 const FieldTrial::Probability kProbability = 50; // 50% probability. 68 FieldTrial* trial = new FieldTrial("IPv6_Probe", kDivisor); 69 int skip_group = trial->AppendGroup("_IPv6_probe_skipped", 70 kProbability); 71 trial->AppendGroup("_IPv6_probe_done", 72 FieldTrial::kAllRemainingProbability); 73 use_ipv6_probe = (trial->group() != skip_group); 74#endif 75 76 if (use_ipv6_probe) 77 host_resolver_impl->ProbeIPv6Support(); 78 } 79 } 80 } 81 82 // If hostname remappings were specified on the command-line, layer these 83 // rules on top of the real host resolver. This allows forwarding all requests 84 // through a designated test server. 85 if (!command_line.HasSwitch(switches::kHostResolverRules)) 86 return global_host_resolver; 87 88 net::MappedHostResolver* remapped_resolver = 89 new net::MappedHostResolver(global_host_resolver); 90 remapped_resolver->SetRulesFromString( 91 command_line.GetSwitchValueASCII(switches::kHostResolverRules)); 92 return remapped_resolver; 93} 94 95class LoggingNetworkChangeObserver 96 : public net::NetworkChangeNotifier::Observer { 97 public: 98 // |net_log| must remain valid throughout our lifetime. 99 explicit LoggingNetworkChangeObserver(net::NetLog* net_log) 100 : net_log_(net_log) { 101 net::NetworkChangeNotifier::AddObserver(this); 102 } 103 104 ~LoggingNetworkChangeObserver() { 105 net::NetworkChangeNotifier::RemoveObserver(this); 106 } 107 108 virtual void OnIPAddressChanged() { 109 LOG(INFO) << "Observed a change to the network IP addresses"; 110 111 net::NetLog::Source global_source; 112 113 // TODO(eroman): We shouldn't need to assign an ID to this source, since 114 // conceptually it is the "global event stream". However 115 // currently the javascript does a grouping on source id, so 116 // the display will look weird if we don't give it one. 117 global_source.id = net_log_->NextID(); 118 119 net_log_->AddEntry(net::NetLog::TYPE_NETWORK_IP_ADDRESSSES_CHANGED, 120 base::TimeTicks::Now(), 121 global_source, 122 net::NetLog::PHASE_NONE, 123 NULL); 124 } 125 126 private: 127 net::NetLog* net_log_; 128 DISALLOW_COPY_AND_ASSIGN(LoggingNetworkChangeObserver); 129}; 130 131} // namespace 132 133// The IOThread object must outlive any tasks posted to the IO thread before the 134// Quit task. 135DISABLE_RUNNABLE_METHOD_REFCOUNT(IOThread); 136 137IOThread::IOThread() 138 : BrowserProcessSubThread(ChromeThread::IO), 139 globals_(NULL), 140 speculative_interceptor_(NULL), 141 prefetch_observer_(NULL), 142 predictor_(NULL) {} 143 144IOThread::~IOThread() { 145 // We cannot rely on our base class to stop the thread since we want our 146 // CleanUp function to run. 147 Stop(); 148 DCHECK(!globals_); 149} 150 151IOThread::Globals* IOThread::globals() { 152 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); 153 return globals_; 154} 155 156void IOThread::InitNetworkPredictor( 157 bool prefetching_enabled, 158 base::TimeDelta max_dns_queue_delay, 159 size_t max_concurrent, 160 const chrome_common_net::UrlList& startup_urls, 161 ListValue* referral_list, 162 bool preconnect_enabled) { 163 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); 164 message_loop()->PostTask( 165 FROM_HERE, 166 NewRunnableMethod( 167 this, 168 &IOThread::InitNetworkPredictorOnIOThread, 169 prefetching_enabled, max_dns_queue_delay, max_concurrent, 170 startup_urls, referral_list, preconnect_enabled)); 171} 172 173void IOThread::ChangedToOnTheRecord() { 174 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); 175 message_loop()->PostTask( 176 FROM_HERE, 177 NewRunnableMethod( 178 this, 179 &IOThread::ChangedToOnTheRecordOnIOThread)); 180} 181 182void IOThread::Init() { 183 BrowserProcessSubThread::Init(); 184 185 DCHECK(!globals_); 186 globals_ = new Globals; 187 188 globals_->net_log.reset(new ChromeNetLog()); 189 190 // Add an observer that will emit network change events to the ChromeNetLog. 191 // Assuming NetworkChangeNotifier dispatches in FIFO order, we should be 192 // logging the network change before other IO thread consumers respond to it. 193 network_change_observer_.reset( 194 new LoggingNetworkChangeObserver(globals_->net_log.get())); 195 196 globals_->host_resolver = CreateGlobalHostResolver(); 197 globals_->http_auth_handler_factory.reset(CreateDefaultAuthHandlerFactory( 198 globals_->host_resolver)); 199} 200 201void IOThread::CleanUp() { 202 // This must be reset before the ChromeNetLog is destroyed. 203 network_change_observer_.reset(); 204 205 // If any child processes are still running, terminate them and 206 // and delete the BrowserChildProcessHost instances to release whatever 207 // IO thread only resources they are referencing. 208 BrowserChildProcessHost::TerminateAll(); 209 210 // Not initialized in Init(). May not be initialized. 211 if (predictor_) { 212 predictor_->Shutdown(); 213 214 // TODO(willchan): Stop reference counting Predictor. It's owned by 215 // IOThread now. 216 predictor_->Release(); 217 predictor_ = NULL; 218 chrome_browser_net::FreePredictorResources(); 219 } 220 221 // Deletion will unregister this interceptor. 222 delete speculative_interceptor_; 223 speculative_interceptor_ = NULL; 224 225 // Not initialized in Init(). May not be initialized. 226 if (prefetch_observer_) { 227 globals_->host_resolver->RemoveObserver(prefetch_observer_); 228 delete prefetch_observer_; 229 prefetch_observer_ = NULL; 230 } 231 232 // TODO(eroman): hack for http://crbug.com/15513 233 if (globals_->host_resolver->GetAsHostResolverImpl()) { 234 globals_->host_resolver.get()->GetAsHostResolverImpl()->Shutdown(); 235 } 236 237 // We will delete the NetLog as part of CleanUpAfterMessageLoopDestruction() 238 // in case any of the message loop destruction observers try to access it. 239 deferred_net_log_to_delete_.reset(globals_->net_log.release()); 240 241 delete globals_; 242 globals_ = NULL; 243 244 // URLRequest instances must NOT outlive the IO thread. 245 base::LeakTracker<URLRequest>::CheckForLeaks(); 246 247 BrowserProcessSubThread::CleanUp(); 248} 249 250void IOThread::CleanUpAfterMessageLoopDestruction() { 251 // TODO(eroman): get rid of this special case for 39723. If we could instead 252 // have a method that runs after the message loop destruction obsevers have 253 // run, but before the message loop itself is destroyed, we could safely 254 // combine the two cleanups. 255 deferred_net_log_to_delete_.reset(); 256 BrowserProcessSubThread::CleanUpAfterMessageLoopDestruction(); 257} 258 259net::HttpAuthHandlerFactory* IOThread::CreateDefaultAuthHandlerFactory( 260 net::HostResolver* resolver) { 261 net::HttpAuthFilterWhitelist* auth_filter = NULL; 262 263 // Get the whitelist information from the command line, create an 264 // HttpAuthFilterWhitelist, and attach it to the HttpAuthHandlerFactory. 265 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 266 267 if (command_line.HasSwitch(switches::kAuthServerWhitelist)) { 268 std::string auth_server_whitelist = 269 command_line.GetSwitchValueASCII(switches::kAuthServerWhitelist); 270 271 // Create a whitelist filter. 272 auth_filter = new net::HttpAuthFilterWhitelist(); 273 auth_filter->SetWhitelist(auth_server_whitelist); 274 } 275 276 // Set the flag that enables or disables the Negotiate auth handler. 277 static const bool kNegotiateAuthEnabledDefault = true; 278 279 bool negotiate_auth_enabled = kNegotiateAuthEnabledDefault; 280 if (command_line.HasSwitch(switches::kExperimentalEnableNegotiateAuth)) { 281 std::string enable_negotiate_auth = command_line.GetSwitchValueASCII( 282 switches::kExperimentalEnableNegotiateAuth); 283 // Enabled if no value, or value is 'true'. Disabled otherwise. 284 negotiate_auth_enabled = 285 enable_negotiate_auth.empty() || 286 (StringToLowerASCII(enable_negotiate_auth) == "true"); 287 } 288 289 net::HttpAuthHandlerRegistryFactory* registry_factory = 290 net::HttpAuthHandlerFactory::CreateDefault(); 291 292 globals_->url_security_manager.reset( 293 net::URLSecurityManager::Create(auth_filter)); 294 295 // Add the security manager to the auth factories that need it. 296 registry_factory->SetURLSecurityManager("ntlm", 297 globals_->url_security_manager.get()); 298 registry_factory->SetURLSecurityManager("negotiate", 299 globals_->url_security_manager.get()); 300 if (negotiate_auth_enabled) { 301 // Configure the Negotiate settings for the Kerberos SPN. 302 // TODO(cbentzel): Read the related IE registry settings on Windows builds. 303 // TODO(cbentzel): Ugly use of static_cast here. 304 net::HttpAuthHandlerNegotiate::Factory* negotiate_factory = 305 static_cast<net::HttpAuthHandlerNegotiate::Factory*>( 306 registry_factory->GetSchemeFactory("negotiate")); 307 DCHECK(negotiate_factory); 308 negotiate_factory->set_host_resolver(resolver); 309 if (command_line.HasSwitch(switches::kDisableAuthNegotiateCnameLookup)) 310 negotiate_factory->set_disable_cname_lookup(true); 311 if (command_line.HasSwitch(switches::kEnableAuthNegotiatePort)) 312 negotiate_factory->set_use_port(true); 313 } else { 314 // Disable the Negotiate authentication handler. 315 registry_factory->RegisterSchemeFactory("negotiate", NULL); 316 } 317 return registry_factory; 318} 319 320void IOThread::InitNetworkPredictorOnIOThread( 321 bool prefetching_enabled, 322 base::TimeDelta max_dns_queue_delay, 323 size_t max_concurrent, 324 const chrome_common_net::UrlList& startup_urls, 325 ListValue* referral_list, 326 bool preconnect_enabled) { 327 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); 328 CHECK(!predictor_); 329 330 chrome_browser_net::EnablePredictor(prefetching_enabled); 331 332 predictor_ = new chrome_browser_net::Predictor( 333 globals_->host_resolver, 334 max_dns_queue_delay, 335 max_concurrent, 336 preconnect_enabled); 337 predictor_->AddRef(); 338 339 // TODO(jar): Until connection notification and DNS observation handling are 340 // properly combined into a learning model, we'll only use one observation 341 // mechanism or the other. 342 if (preconnect_enabled) { 343 DCHECK(!speculative_interceptor_); 344 speculative_interceptor_ = new chrome_browser_net::ConnectInterceptor; 345 } else { 346 DCHECK(!prefetch_observer_); 347 prefetch_observer_ = chrome_browser_net::CreateResolverObserver(); 348 globals_->host_resolver->AddObserver(prefetch_observer_); 349 } 350 351 FinalizePredictorInitialization( 352 predictor_, prefetch_observer_, startup_urls, referral_list); 353} 354 355void IOThread::ChangedToOnTheRecordOnIOThread() { 356 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); 357 358 if (predictor_) { 359 // Destroy all evidence of our OTR session. 360 predictor_->Predictor::DiscardAllResults(); 361 } 362 363 // Clear the host cache to avoid showing entries from the OTR session 364 // in about:net-internals. 365 if (globals_->host_resolver->GetAsHostResolverImpl()) { 366 net::HostCache* host_cache = 367 globals_->host_resolver.get()->GetAsHostResolverImpl()->cache(); 368 if (host_cache) 369 host_cache->clear(); 370 } 371 // Clear all of the passively logged data. 372 // TODO(eroman): this is a bit heavy handed, really all we need to do is 373 // clear the data pertaining to off the record context. 374 globals_->net_log->passive_collector()->Clear(); 375} 376