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