blob: c468a91b393b7345d05510398a591a2218a4f6e7 [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/io_thread.h"
#include <vector>
#include "base/command_line.h"
#include "base/debug/leak_tracker.h"
#include "base/logging.h"
#include "base/metrics/field_trial.h"
#include "base/stl_util-inl.h"
#include "base/string_number_conversions.h"
#include "base/string_split.h"
#include "base/string_util.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/browser/gpu_process_host.h"
#include "chrome/browser/net/chrome_net_log.h"
#include "chrome/browser/net/chrome_url_request_context.h"
#include "chrome/browser/net/connect_interceptor.h"
#include "chrome/browser/net/passive_log_collector.h"
#include "chrome/browser/net/predictor_api.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/net/raw_host_resolver_proc.h"
#include "chrome/common/net/url_fetcher.h"
#include "chrome/common/pref_names.h"
#include "net/base/cert_verifier.h"
#include "net/base/dnsrr_resolver.h"
#include "net/base/host_cache.h"
#include "net/base/host_resolver.h"
#include "net/base/host_resolver_impl.h"
#include "net/base/mapped_host_resolver.h"
#include "net/base/net_util.h"
#include "net/http/http_auth_filter.h"
#include "net/http/http_auth_handler_factory.h"
#include "net/http/http_network_layer.h"
#if defined(USE_NSS)
#include "net/ocsp/nss_ocsp.h"
#endif // defined(USE_NSS)
#include "net/proxy/proxy_script_fetcher_impl.h"
#include "net/socket/client_socket_factory.h"
#include "net/spdy/spdy_session_pool.h"
namespace {
net::HostResolver* CreateGlobalHostResolver(net::NetLog* net_log) {
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
size_t parallelism = net::HostResolver::kDefaultParallelism;
// Use the concurrency override from the command-line, if any.
if (command_line.HasSwitch(switches::kHostResolverParallelism)) {
std::string s =
command_line.GetSwitchValueASCII(switches::kHostResolverParallelism);
// Parse the switch (it should be a positive integer formatted as decimal).
int n;
if (base::StringToInt(s, &n) && n > 0) {
parallelism = static_cast<size_t>(n);
} else {
LOG(ERROR) << "Invalid switch for host resolver parallelism: " << s;
}
} else {
// Set up a field trial to see what impact the total number of concurrent
// resolutions have on DNS resolutions.
base::FieldTrial::Probability kDivisor = 1000;
// For each option (i.e., non-default), we have a fixed probability.
base::FieldTrial::Probability kProbabilityPerGroup = 100; // 10%.
// After June 30, 2011 builds, it will always be in default group
// (parallel_default).
scoped_refptr<base::FieldTrial> trial(
new base::FieldTrial(
"DnsParallelism", kDivisor, "parallel_default", 2011, 6, 30));
// List options with different counts.
// Firefox limits total to 8 in parallel, and default is currently 50.
int parallel_6 = trial->AppendGroup("parallel_6", kProbabilityPerGroup);
int parallel_7 = trial->AppendGroup("parallel_7", kProbabilityPerGroup);
int parallel_8 = trial->AppendGroup("parallel_8", kProbabilityPerGroup);
int parallel_9 = trial->AppendGroup("parallel_9", kProbabilityPerGroup);
int parallel_10 = trial->AppendGroup("parallel_10", kProbabilityPerGroup);
int parallel_14 = trial->AppendGroup("parallel_14", kProbabilityPerGroup);
int parallel_20 = trial->AppendGroup("parallel_20", kProbabilityPerGroup);
if (trial->group() == parallel_6)
parallelism = 6;
else if (trial->group() == parallel_7)
parallelism = 7;
else if (trial->group() == parallel_8)
parallelism = 8;
else if (trial->group() == parallel_9)
parallelism = 9;
else if (trial->group() == parallel_10)
parallelism = 10;
else if (trial->group() == parallel_14)
parallelism = 14;
else if (trial->group() == parallel_20)
parallelism = 20;
}
// Use the specified DNS server for doing raw resolutions if requested
// from the command-line.
scoped_refptr<net::HostResolverProc> resolver_proc;
if (command_line.HasSwitch(switches::kDnsServer)) {
std::string dns_ip_string =
command_line.GetSwitchValueASCII(switches::kDnsServer);
net::IPAddressNumber dns_ip_number;
if (net::ParseIPLiteralToNumber(dns_ip_string, &dns_ip_number)) {
resolver_proc =
new chrome_common_net::RawHostResolverProc(dns_ip_number, NULL);
} else {
LOG(ERROR) << "Invalid IP address specified for --dns-server: "
<< dns_ip_string;
}
}
net::HostResolver* global_host_resolver =
net::CreateSystemHostResolver(parallelism, resolver_proc.get(), net_log);
// Determine if we should disable IPv6 support.
if (!command_line.HasSwitch(switches::kEnableIPv6)) {
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) {
// Use probe to decide if support is warranted.
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))
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;
}
class LoggingNetworkChangeObserver
: public net::NetworkChangeNotifier::Observer {
public:
// |net_log| must remain valid throughout our lifetime.
explicit LoggingNetworkChangeObserver(net::NetLog* net_log)
: net_log_(net_log) {
net::NetworkChangeNotifier::AddObserver(this);
}
~LoggingNetworkChangeObserver() {
net::NetworkChangeNotifier::RemoveObserver(this);
}
virtual void OnIPAddressChanged() {
VLOG(1) << "Observed a change to the network IP addresses";
net_log_->AddEntry(net::NetLog::TYPE_NETWORK_IP_ADDRESSES_CHANGED,
base::TimeTicks::Now(),
net::NetLog::Source(),
net::NetLog::PHASE_NONE,
NULL);
}
private:
net::NetLog* net_log_;
DISALLOW_COPY_AND_ASSIGN(LoggingNetworkChangeObserver);
};
scoped_refptr<net::URLRequestContext>
ConstructProxyScriptFetcherContext(IOThread::Globals* globals,
net::NetLog* net_log) {
scoped_refptr<net::URLRequestContext> context(new net::URLRequestContext);
context->set_net_log(net_log);
context->set_host_resolver(globals->host_resolver.get());
context->set_cert_verifier(globals->cert_verifier.get());
context->set_dnsrr_resolver(globals->dnsrr_resolver.get());
context->set_http_auth_handler_factory(
globals->http_auth_handler_factory.get());
context->set_proxy_service(globals->proxy_script_fetcher_proxy_service.get());
context->set_http_transaction_factory(
globals->proxy_script_fetcher_http_transaction_factory.get());
// In-memory cookie store.
context->set_cookie_store(new net::CookieMonster(NULL, NULL));
return context;
}
} // namespace
// The IOThread object must outlive any tasks posted to the IO thread before the
// Quit task.
DISABLE_RUNNABLE_METHOD_REFCOUNT(IOThread);
IOThread::Globals::Globals() {}
IOThread::Globals::~Globals() {}
// |local_state| is passed in explicitly in order to (1) reduce implicit
// dependencies and (2) make IOThread more flexible for testing.
IOThread::IOThread(PrefService* local_state, ChromeNetLog* net_log)
: BrowserProcessSubThread(BrowserThread::IO),
net_log_(net_log),
globals_(NULL),
speculative_interceptor_(NULL),
predictor_(NULL) {
// We call RegisterPrefs() here (instead of inside browser_prefs.cc) to make
// sure that everything is initialized in the right order.
RegisterPrefs(local_state);
auth_schemes_ = local_state->GetString(prefs::kAuthSchemes);
negotiate_disable_cname_lookup_ = local_state->GetBoolean(
prefs::kDisableAuthNegotiateCnameLookup);
negotiate_enable_port_ = local_state->GetBoolean(
prefs::kEnableAuthNegotiatePort);
auth_server_whitelist_ = local_state->GetString(prefs::kAuthServerWhitelist);
auth_delegate_whitelist_ = local_state->GetString(
prefs::kAuthNegotiateDelegateWhitelist);
gssapi_library_name_ = local_state->GetString(prefs::kGSSAPILibraryName);
}
IOThread::~IOThread() {
// We cannot rely on our base class to stop the thread since we want our
// CleanUp function to run.
Stop();
DCHECK(!globals_);
}
IOThread::Globals* IOThread::globals() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
return globals_;
}
ChromeNetLog* IOThread::net_log() {
return net_log_;
}
void IOThread::InitNetworkPredictor(
bool prefetching_enabled,
base::TimeDelta max_dns_queue_delay,
size_t max_speculative_parallel_resolves,
const chrome_common_net::UrlList& startup_urls,
ListValue* referral_list,
bool preconnect_enabled) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
message_loop()->PostTask(
FROM_HERE,
NewRunnableMethod(
this,
&IOThread::InitNetworkPredictorOnIOThread,
prefetching_enabled, max_dns_queue_delay,
max_speculative_parallel_resolves,
startup_urls, referral_list, preconnect_enabled));
}
void IOThread::RegisterURLRequestContextGetter(
ChromeURLRequestContextGetter* url_request_context_getter) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
std::list<ChromeURLRequestContextGetter*>::const_iterator it =
std::find(url_request_context_getters_.begin(),
url_request_context_getters_.end(),
url_request_context_getter);
DCHECK(it == url_request_context_getters_.end());
url_request_context_getters_.push_back(url_request_context_getter);
}
void IOThread::UnregisterURLRequestContextGetter(
ChromeURLRequestContextGetter* url_request_context_getter) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
std::list<ChromeURLRequestContextGetter*>::iterator it =
std::find(url_request_context_getters_.begin(),
url_request_context_getters_.end(),
url_request_context_getter);
DCHECK(it != url_request_context_getters_.end());
// This does not scale, but we shouldn't have many URLRequestContextGetters in
// the first place, so this should be fine.
url_request_context_getters_.erase(it);
}
void IOThread::ChangedToOnTheRecord() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
message_loop()->PostTask(
FROM_HERE,
NewRunnableMethod(
this,
&IOThread::ChangedToOnTheRecordOnIOThread));
}
void IOThread::Init() {
#if !defined(OS_CHROMEOS)
// TODO(evan): test and enable this on all platforms.
// Though this thread is called the "IO" thread, it actually just routes
// messages around; it shouldn't be allowed to perform any blocking disk I/O.
base::ThreadRestrictions::SetIOAllowed(false);
#endif
BrowserProcessSubThread::Init();
DCHECK_EQ(MessageLoop::TYPE_IO, message_loop()->type());
#if defined(USE_NSS)
net::SetMessageLoopForOCSP();
#endif // defined(USE_NSS)
DCHECK(!globals_);
globals_ = new Globals;
// Add an observer that will emit network change events to the ChromeNetLog.
// Assuming NetworkChangeNotifier dispatches in FIFO order, we should be
// logging the network change before other IO thread consumers respond to it.
network_change_observer_.reset(
new LoggingNetworkChangeObserver(net_log_));
globals_->client_socket_factory =
net::ClientSocketFactory::GetDefaultFactory();
globals_->host_resolver.reset(
CreateGlobalHostResolver(net_log_));
globals_->cert_verifier.reset(new net::CertVerifier);
globals_->dnsrr_resolver.reset(new net::DnsRRResolver);
// TODO(willchan): Use the real SSLConfigService.
globals_->ssl_config_service =
net::SSLConfigService::CreateSystemSSLConfigService();
globals_->http_auth_handler_factory.reset(CreateDefaultAuthHandlerFactory(
globals_->host_resolver.get()));
// For the ProxyScriptFetcher, we use a direct ProxyService.
globals_->proxy_script_fetcher_proxy_service =
net::ProxyService::CreateDirectWithNetLog(net_log_);
globals_->proxy_script_fetcher_http_transaction_factory.reset(
new net::HttpNetworkLayer(
globals_->client_socket_factory,
globals_->host_resolver.get(),
globals_->cert_verifier.get(),
globals_->dnsrr_resolver.get(),
NULL /* dns_cert_checker */,
NULL /* ssl_host_info_factory */,
globals_->proxy_script_fetcher_proxy_service.get(),
globals_->ssl_config_service.get(),
new net::SpdySessionPool(globals_->ssl_config_service.get()),
globals_->http_auth_handler_factory.get(),
&globals_->network_delegate,
net_log_));
scoped_refptr<net::URLRequestContext> proxy_script_fetcher_context =
ConstructProxyScriptFetcherContext(globals_, net_log_);
globals_->proxy_script_fetcher_context = proxy_script_fetcher_context;
}
void IOThread::CleanUp() {
// Step 1: Kill all things that might be holding onto
// net::URLRequest/net::URLRequestContexts.
#if defined(USE_NSS)
net::ShutdownOCSP();
#endif // defined(USE_NSS)
// Destroy all URLRequests started by URLFetchers.
URLFetcher::CancelAll();
// If any child processes are still running, terminate them and
// and delete the BrowserChildProcessHost instances to release whatever
// IO thread only resources they are referencing.
BrowserChildProcessHost::TerminateAll();
std::list<ChromeURLRequestContextGetter*> url_request_context_getters;
url_request_context_getters.swap(url_request_context_getters_);
for (std::list<ChromeURLRequestContextGetter*>::iterator it =
url_request_context_getters.begin();
it != url_request_context_getters.end(); ++it) {
ChromeURLRequestContextGetter* getter = *it;
// Stop all pending certificate provenance check uploads
net::DnsCertProvenanceChecker* checker =
getter->GetURLRequestContext()->dns_cert_checker();
if (checker)
checker->Shutdown();
getter->ReleaseURLRequestContext();
}
// Step 2: Release objects that the net::URLRequestContext could have been
// pointing to.
// This must be reset before the ChromeNetLog is destroyed.
network_change_observer_.reset();
// Not initialized in Init(). May not be initialized.
if (predictor_) {
predictor_->Shutdown();
// TODO(willchan): Stop reference counting Predictor. It's owned by
// IOThread now.
predictor_->Release();
predictor_ = NULL;
chrome_browser_net::FreePredictorResources();
}
// Deletion will unregister this interceptor.
delete speculative_interceptor_;
speculative_interceptor_ = NULL;
// TODO(eroman): hack for http://crbug.com/15513
if (globals_->host_resolver->GetAsHostResolverImpl()) {
globals_->host_resolver.get()->GetAsHostResolverImpl()->Shutdown();
}
delete globals_;
globals_ = NULL;
BrowserProcessSubThread::CleanUp();
}
void IOThread::CleanUpAfterMessageLoopDestruction() {
// This will delete the |notification_service_|. Make sure it's done after
// anything else can reference it.
BrowserProcessSubThread::CleanUpAfterMessageLoopDestruction();
// net::URLRequest instances must NOT outlive the IO thread.
//
// To allow for URLRequests to be deleted from
// MessageLoop::DestructionObserver this check has to happen after CleanUp
// (which runs before DestructionObservers).
base::debug::LeakTracker<net::URLRequest>::CheckForLeaks();
}
// static
void IOThread::RegisterPrefs(PrefService* local_state) {
local_state->RegisterStringPref(prefs::kAuthSchemes,
"basic,digest,ntlm,negotiate");
local_state->RegisterBooleanPref(prefs::kDisableAuthNegotiateCnameLookup,
false);
local_state->RegisterBooleanPref(prefs::kEnableAuthNegotiatePort, false);
local_state->RegisterStringPref(prefs::kAuthServerWhitelist, "");
local_state->RegisterStringPref(prefs::kAuthNegotiateDelegateWhitelist, "");
local_state->RegisterStringPref(prefs::kGSSAPILibraryName, "");
}
net::HttpAuthHandlerFactory* IOThread::CreateDefaultAuthHandlerFactory(
net::HostResolver* resolver) {
net::HttpAuthFilterWhitelist* auth_filter_default_credentials = NULL;
if (!auth_server_whitelist_.empty()) {
auth_filter_default_credentials =
new net::HttpAuthFilterWhitelist(auth_server_whitelist_);
}
net::HttpAuthFilterWhitelist* auth_filter_delegate = NULL;
if (!auth_delegate_whitelist_.empty()) {
auth_filter_delegate =
new net::HttpAuthFilterWhitelist(auth_delegate_whitelist_);
}
globals_->url_security_manager.reset(
net::URLSecurityManager::Create(auth_filter_default_credentials,
auth_filter_delegate));
std::vector<std::string> supported_schemes;
base::SplitString(auth_schemes_, ',', &supported_schemes);
return net::HttpAuthHandlerRegistryFactory::Create(
supported_schemes,
globals_->url_security_manager.get(),
resolver,
gssapi_library_name_,
negotiate_disable_cname_lookup_,
negotiate_enable_port_);
}
void IOThread::InitNetworkPredictorOnIOThread(
bool prefetching_enabled,
base::TimeDelta max_dns_queue_delay,
size_t max_speculative_parallel_resolves,
const chrome_common_net::UrlList& startup_urls,
ListValue* referral_list,
bool preconnect_enabled) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
CHECK(!predictor_);
chrome_browser_net::EnablePredictor(prefetching_enabled);
predictor_ = new chrome_browser_net::Predictor(
globals_->host_resolver.get(),
max_dns_queue_delay,
max_speculative_parallel_resolves,
preconnect_enabled);
predictor_->AddRef();
// Speculative_interceptor_ is used to predict subresource usage.
DCHECK(!speculative_interceptor_);
speculative_interceptor_ = new chrome_browser_net::ConnectInterceptor;
FinalizePredictorInitialization(predictor_, startup_urls, referral_list);
}
void IOThread::ChangedToOnTheRecordOnIOThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (predictor_) {
// Destroy all evidence of our OTR session.
predictor_->Predictor::DiscardAllResults();
}
// Clear the host cache to avoid showing entries from the OTR session
// in about:net-internals.
if (globals_->host_resolver->GetAsHostResolverImpl()) {
net::HostCache* host_cache =
globals_->host_resolver.get()->GetAsHostResolverImpl()->cache();
if (host_cache)
host_cache->clear();
}
// Clear all of the passively logged data.
// TODO(eroman): this is a bit heavy handed, really all we need to do is
// clear the data pertaining to off the record context.
net_log_->ClearAllPassivelyCapturedEvents();
}