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