io_thread.cc revision 731df977c0511bca2206b5f333555b1205ff1f43
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 7#include "base/command_line.h" 8#include "base/leak_tracker.h" 9#include "base/logging.h" 10#include "base/metrics/field_trial.h" 11#include "base/stl_util-inl.h" 12#include "base/string_number_conversions.h" 13#include "base/string_split.h" 14#include "base/string_util.h" 15#include "chrome/browser/browser_process.h" 16#include "chrome/browser/browser_thread.h" 17#include "chrome/browser/gpu_process_host.h" 18#include "chrome/browser/net/chrome_net_log.h" 19#include "chrome/browser/net/connect_interceptor.h" 20#include "chrome/browser/net/passive_log_collector.h" 21#include "chrome/browser/net/predictor_api.h" 22#include "chrome/common/chrome_switches.h" 23#include "chrome/common/net/url_fetcher.h" 24#include "net/base/dnsrr_resolver.h" 25#include "net/base/host_cache.h" 26#include "net/base/host_resolver.h" 27#include "net/base/host_resolver_impl.h" 28#include "net/base/mapped_host_resolver.h" 29#include "net/base/net_util.h" 30#include "net/http/http_auth_filter.h" 31#include "net/http/http_auth_handler_factory.h" 32#if defined(USE_NSS) 33#include "net/ocsp/nss_ocsp.h" 34#endif // defined(USE_NSS) 35#include "net/proxy/proxy_script_fetcher_impl.h" 36 37namespace { 38 39net::HostResolver* CreateGlobalHostResolver(net::NetLog* net_log) { 40 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 41 42 size_t parallelism = net::HostResolver::kDefaultParallelism; 43 44 // Use the concurrency override from the command-line, if any. 45 if (command_line.HasSwitch(switches::kHostResolverParallelism)) { 46 std::string s = 47 command_line.GetSwitchValueASCII(switches::kHostResolverParallelism); 48 49 // Parse the switch (it should be a positive integer formatted as decimal). 50 int n; 51 if (base::StringToInt(s, &n) && n > 0) { 52 parallelism = static_cast<size_t>(n); 53 } else { 54 LOG(ERROR) << "Invalid switch for host resolver parallelism: " << s; 55 } 56 } else { 57 // Set up a field trial to see what impact the total number of concurrent 58 // resolutions have on DNS resolutions. 59 base::FieldTrial::Probability kDivisor = 1000; 60 // For each option (i.e., non-default), we have a fixed probability. 61 base::FieldTrial::Probability kProbabilityPerGroup = 100; // 10%. 62 63 scoped_refptr<base::FieldTrial> trial = 64 new base::FieldTrial("DnsParallelism", kDivisor); 65 66 // List options with different counts. 67 // Firefox limits total to 8 in parallel, and default is currently 50. 68 int parallel_6 = trial->AppendGroup("parallel_6", kProbabilityPerGroup); 69 int parallel_8 = trial->AppendGroup("parallel_8", kProbabilityPerGroup); 70 int parallel_10 = trial->AppendGroup("parallel_10", kProbabilityPerGroup); 71 int parallel_14 = trial->AppendGroup("parallel_14", kProbabilityPerGroup); 72 int parallel_20 = trial->AppendGroup("parallel_20", kProbabilityPerGroup); 73 74 trial->AppendGroup("parallel_default", 75 base::FieldTrial::kAllRemainingProbability); 76 77 if (trial->group() == parallel_6) 78 parallelism = 6; 79 else if (trial->group() == parallel_8) 80 parallelism = 8; 81 else if (trial->group() == parallel_10) 82 parallelism = 10; 83 else if (trial->group() == parallel_14) 84 parallelism = 14; 85 else if (trial->group() == parallel_20) 86 parallelism = 20; 87 } 88 89 net::HostResolver* global_host_resolver = 90 net::CreateSystemHostResolver(parallelism, net_log); 91 92 // Determine if we should disable IPv6 support. 93 if (!command_line.HasSwitch(switches::kEnableIPv6)) { 94 if (command_line.HasSwitch(switches::kDisableIPv6)) { 95 global_host_resolver->SetDefaultAddressFamily(net::ADDRESS_FAMILY_IPV4); 96 } else { 97 net::HostResolverImpl* host_resolver_impl = 98 global_host_resolver->GetAsHostResolverImpl(); 99 if (host_resolver_impl != NULL) { 100 // Use probe to decide if support is warranted. 101 host_resolver_impl->ProbeIPv6Support(); 102 } 103 } 104 } 105 106 // If hostname remappings were specified on the command-line, layer these 107 // rules on top of the real host resolver. This allows forwarding all requests 108 // through a designated test server. 109 if (!command_line.HasSwitch(switches::kHostResolverRules)) 110 return global_host_resolver; 111 112 net::MappedHostResolver* remapped_resolver = 113 new net::MappedHostResolver(global_host_resolver); 114 remapped_resolver->SetRulesFromString( 115 command_line.GetSwitchValueASCII(switches::kHostResolverRules)); 116 return remapped_resolver; 117} 118 119class LoggingNetworkChangeObserver 120 : public net::NetworkChangeNotifier::Observer { 121 public: 122 // |net_log| must remain valid throughout our lifetime. 123 explicit LoggingNetworkChangeObserver(net::NetLog* net_log) 124 : net_log_(net_log) { 125 net::NetworkChangeNotifier::AddObserver(this); 126 } 127 128 ~LoggingNetworkChangeObserver() { 129 net::NetworkChangeNotifier::RemoveObserver(this); 130 } 131 132 virtual void OnIPAddressChanged() { 133 VLOG(1) << "Observed a change to the network IP addresses"; 134 135 net_log_->AddEntry(net::NetLog::TYPE_NETWORK_IP_ADDRESSES_CHANGED, 136 base::TimeTicks::Now(), 137 net::NetLog::Source(), 138 net::NetLog::PHASE_NONE, 139 NULL); 140 } 141 142 private: 143 net::NetLog* net_log_; 144 DISALLOW_COPY_AND_ASSIGN(LoggingNetworkChangeObserver); 145}; 146 147} // namespace 148 149// This is a wrapper class around ProxyScriptFetcherImpl that will 150// keep track of live instances. 151class IOThread::ManagedProxyScriptFetcher 152 : public net::ProxyScriptFetcherImpl { 153 public: 154 ManagedProxyScriptFetcher(URLRequestContext* context, 155 IOThread* io_thread) 156 : net::ProxyScriptFetcherImpl(context), 157 io_thread_(io_thread) { 158 DCHECK(!ContainsKey(*fetchers(), this)); 159 fetchers()->insert(this); 160 } 161 162 virtual ~ManagedProxyScriptFetcher() { 163 DCHECK(ContainsKey(*fetchers(), this)); 164 fetchers()->erase(this); 165 } 166 167 private: 168 ProxyScriptFetchers* fetchers() { 169 return &io_thread_->fetchers_; 170 } 171 172 IOThread* io_thread_; 173 174 DISALLOW_COPY_AND_ASSIGN(ManagedProxyScriptFetcher); 175}; 176 177// The IOThread object must outlive any tasks posted to the IO thread before the 178// Quit task. 179DISABLE_RUNNABLE_METHOD_REFCOUNT(IOThread); 180 181IOThread::Globals::Globals() {} 182 183IOThread::Globals::~Globals() {} 184 185IOThread::IOThread() 186 : BrowserProcessSubThread(BrowserThread::IO), 187 globals_(NULL), 188 speculative_interceptor_(NULL), 189 predictor_(NULL) {} 190 191IOThread::~IOThread() { 192 // We cannot rely on our base class to stop the thread since we want our 193 // CleanUp function to run. 194 Stop(); 195 DCHECK(!globals_); 196} 197 198IOThread::Globals* IOThread::globals() { 199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 200 return globals_; 201} 202 203void IOThread::InitNetworkPredictor( 204 bool prefetching_enabled, 205 base::TimeDelta max_dns_queue_delay, 206 size_t max_concurrent, 207 const chrome_common_net::UrlList& startup_urls, 208 ListValue* referral_list, 209 bool preconnect_enabled) { 210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 211 message_loop()->PostTask( 212 FROM_HERE, 213 NewRunnableMethod( 214 this, 215 &IOThread::InitNetworkPredictorOnIOThread, 216 prefetching_enabled, max_dns_queue_delay, max_concurrent, 217 startup_urls, referral_list, preconnect_enabled)); 218} 219 220void IOThread::ChangedToOnTheRecord() { 221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 222 message_loop()->PostTask( 223 FROM_HERE, 224 NewRunnableMethod( 225 this, 226 &IOThread::ChangedToOnTheRecordOnIOThread)); 227} 228 229net::ProxyScriptFetcher* IOThread::CreateAndRegisterProxyScriptFetcher( 230 URLRequestContext* url_request_context) { 231 return new ManagedProxyScriptFetcher(url_request_context, this); 232} 233 234void IOThread::Init() { 235 BrowserProcessSubThread::Init(); 236 237 DCHECK_EQ(MessageLoop::TYPE_IO, message_loop()->type()); 238 239#if defined(USE_NSS) 240 net::SetMessageLoopForOCSP(); 241#endif // defined(USE_NSS) 242 243 DCHECK(!globals_); 244 globals_ = new Globals; 245 246 globals_->net_log.reset(new ChromeNetLog()); 247 248 // Add an observer that will emit network change events to the ChromeNetLog. 249 // Assuming NetworkChangeNotifier dispatches in FIFO order, we should be 250 // logging the network change before other IO thread consumers respond to it. 251 network_change_observer_.reset( 252 new LoggingNetworkChangeObserver(globals_->net_log.get())); 253 254 globals_->host_resolver.reset( 255 CreateGlobalHostResolver(globals_->net_log.get())); 256 globals_->dnsrr_resolver.reset(new net::DnsRRResolver); 257 globals_->http_auth_handler_factory.reset(CreateDefaultAuthHandlerFactory( 258 globals_->host_resolver.get())); 259} 260 261void IOThread::CleanUp() { 262#if defined(USE_NSS) 263 net::ShutdownOCSP(); 264#endif // defined(USE_NSS) 265 266 // Destroy all URLRequests started by URLFetchers. 267 URLFetcher::CancelAll(); 268 269 // This must be reset before the ChromeNetLog is destroyed. 270 network_change_observer_.reset(); 271 272 // If any child processes are still running, terminate them and 273 // and delete the BrowserChildProcessHost instances to release whatever 274 // IO thread only resources they are referencing. 275 BrowserChildProcessHost::TerminateAll(); 276 277 // Not initialized in Init(). May not be initialized. 278 if (predictor_) { 279 predictor_->Shutdown(); 280 281 // TODO(willchan): Stop reference counting Predictor. It's owned by 282 // IOThread now. 283 predictor_->Release(); 284 predictor_ = NULL; 285 chrome_browser_net::FreePredictorResources(); 286 } 287 288 // Deletion will unregister this interceptor. 289 delete speculative_interceptor_; 290 speculative_interceptor_ = NULL; 291 292 // TODO(eroman): hack for http://crbug.com/15513 293 if (globals_->host_resolver->GetAsHostResolverImpl()) { 294 globals_->host_resolver.get()->GetAsHostResolverImpl()->Shutdown(); 295 } 296 297 // Break any cycles between the ProxyScriptFetcher and URLRequestContext. 298 for (ProxyScriptFetchers::const_iterator it = fetchers_.begin(); 299 it != fetchers_.end(); ++it) { 300 (*it)->Cancel(); 301 } 302 303 // We will delete the NetLog as part of CleanUpAfterMessageLoopDestruction() 304 // in case any of the message loop destruction observers try to access it. 305 deferred_net_log_to_delete_.reset(globals_->net_log.release()); 306 307 delete globals_; 308 globals_ = NULL; 309 310 BrowserProcessSubThread::CleanUp(); 311} 312 313void IOThread::CleanUpAfterMessageLoopDestruction() { 314 // TODO(eroman): get rid of this special case for 39723. If we could instead 315 // have a method that runs after the message loop destruction obsevers have 316 // run, but before the message loop itself is destroyed, we could safely 317 // combine the two cleanups. 318 deferred_net_log_to_delete_.reset(); 319 BrowserProcessSubThread::CleanUpAfterMessageLoopDestruction(); 320 321 // URLRequest instances must NOT outlive the IO thread. 322 // 323 // To allow for URLRequests to be deleted from 324 // MessageLoop::DestructionObserver this check has to happen after CleanUp 325 // (which runs before DestructionObservers). 326 base::LeakTracker<URLRequest>::CheckForLeaks(); 327} 328 329net::HttpAuthHandlerFactory* IOThread::CreateDefaultAuthHandlerFactory( 330 net::HostResolver* resolver) { 331 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 332 333 // Get the whitelist information from the command line, create an 334 // HttpAuthFilterWhitelist, and attach it to the HttpAuthHandlerFactory. 335 net::HttpAuthFilterWhitelist* auth_filter_default_credentials = NULL; 336 if (command_line.HasSwitch(switches::kAuthServerWhitelist)) { 337 auth_filter_default_credentials = new net::HttpAuthFilterWhitelist( 338 command_line.GetSwitchValueASCII(switches::kAuthServerWhitelist)); 339 } 340 net::HttpAuthFilterWhitelist* auth_filter_delegate = NULL; 341 if (command_line.HasSwitch(switches::kAuthNegotiateDelegateWhitelist)) { 342 auth_filter_delegate = new net::HttpAuthFilterWhitelist( 343 command_line.GetSwitchValueASCII( 344 switches::kAuthNegotiateDelegateWhitelist)); 345 } 346 globals_->url_security_manager.reset( 347 net::URLSecurityManager::Create(auth_filter_default_credentials, 348 auth_filter_delegate)); 349 350 // Determine which schemes are supported. 351 std::string csv_auth_schemes = "basic,digest,ntlm,negotiate"; 352 if (command_line.HasSwitch(switches::kAuthSchemes)) 353 csv_auth_schemes = StringToLowerASCII( 354 command_line.GetSwitchValueASCII(switches::kAuthSchemes)); 355 std::vector<std::string> supported_schemes; 356 base::SplitString(csv_auth_schemes, ',', &supported_schemes); 357 358 return net::HttpAuthHandlerRegistryFactory::Create( 359 supported_schemes, 360 globals_->url_security_manager.get(), 361 resolver, 362 command_line.HasSwitch(switches::kDisableAuthNegotiateCnameLookup), 363 command_line.HasSwitch(switches::kEnableAuthNegotiatePort)); 364} 365 366void IOThread::InitNetworkPredictorOnIOThread( 367 bool prefetching_enabled, 368 base::TimeDelta max_dns_queue_delay, 369 size_t max_concurrent, 370 const chrome_common_net::UrlList& startup_urls, 371 ListValue* referral_list, 372 bool preconnect_enabled) { 373 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 374 CHECK(!predictor_); 375 376 chrome_browser_net::EnablePredictor(prefetching_enabled); 377 378 predictor_ = new chrome_browser_net::Predictor( 379 globals_->host_resolver.get(), 380 max_dns_queue_delay, 381 max_concurrent, 382 preconnect_enabled); 383 predictor_->AddRef(); 384 385 // Speculative_interceptor_ is used to predict subresource usage. 386 DCHECK(!speculative_interceptor_); 387 speculative_interceptor_ = new chrome_browser_net::ConnectInterceptor; 388 389 FinalizePredictorInitialization(predictor_, startup_urls, referral_list); 390} 391 392void IOThread::ChangedToOnTheRecordOnIOThread() { 393 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 394 395 if (predictor_) { 396 // Destroy all evidence of our OTR session. 397 predictor_->Predictor::DiscardAllResults(); 398 } 399 400 // Clear the host cache to avoid showing entries from the OTR session 401 // in about:net-internals. 402 if (globals_->host_resolver->GetAsHostResolverImpl()) { 403 net::HostCache* host_cache = 404 globals_->host_resolver.get()->GetAsHostResolverImpl()->cache(); 405 if (host_cache) 406 host_cache->clear(); 407 } 408 // Clear all of the passively logged data. 409 // TODO(eroman): this is a bit heavy handed, really all we need to do is 410 // clear the data pertaining to off the record context. 411 globals_->net_log->passive_collector()->Clear(); 412} 413