io_thread.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
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/string_number_conversions.h"
11#include "base/string_split.h"
12#include "base/string_util.h"
13#include "chrome/browser/browser_process.h"
14#include "chrome/browser/chrome_thread.h"
15#include "chrome/browser/gpu_process_host.h"
16#include "chrome/browser/net/chrome_net_log.h"
17#include "chrome/browser/net/predictor_api.h"
18#include "chrome/browser/net/passive_log_collector.h"
19#include "chrome/common/chrome_switches.h"
20#include "chrome/common/net/url_fetcher.h"
21#include "net/base/mapped_host_resolver.h"
22#include "net/base/host_cache.h"
23#include "net/base/host_resolver.h"
24#include "net/base/host_resolver_impl.h"
25#include "net/base/net_util.h"
26#include "net/http/http_auth_filter.h"
27#include "net/http/http_auth_handler_factory.h"
28
29namespace {
30
31net::HostResolver* CreateGlobalHostResolver(net::NetLog* net_log) {
32  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
33
34  size_t parallelism = net::HostResolver::kDefaultParallelism;
35
36  // Use the concurrency override from the command-line, if any.
37  if (command_line.HasSwitch(switches::kHostResolverParallelism)) {
38    std::string s =
39        command_line.GetSwitchValueASCII(switches::kHostResolverParallelism);
40
41    // Parse the switch (it should be a positive integer formatted as decimal).
42    int n;
43    if (base::StringToInt(s, &n) && n > 0) {
44      parallelism = static_cast<size_t>(n);
45    } else {
46      LOG(ERROR) << "Invalid switch for host resolver parallelism: " << s;
47    }
48  }
49
50  net::HostResolver* global_host_resolver =
51      net::CreateSystemHostResolver(parallelism, net_log);
52
53  // Determine if we should disable IPv6 support.
54  if (!command_line.HasSwitch(switches::kEnableIPv6)) {
55    if (command_line.HasSwitch(switches::kDisableIPv6)) {
56      global_host_resolver->SetDefaultAddressFamily(net::ADDRESS_FAMILY_IPV4);
57    } else {
58      net::HostResolverImpl* host_resolver_impl =
59          global_host_resolver->GetAsHostResolverImpl();
60      if (host_resolver_impl != NULL) {
61        // Use probe to decide if support is warranted.
62        host_resolver_impl->ProbeIPv6Support();
63      }
64    }
65  }
66
67  // If hostname remappings were specified on the command-line, layer these
68  // rules on top of the real host resolver. This allows forwarding all requests
69  // through a designated test server.
70  if (!command_line.HasSwitch(switches::kHostResolverRules))
71    return global_host_resolver;
72
73  net::MappedHostResolver* remapped_resolver =
74      new net::MappedHostResolver(global_host_resolver);
75  remapped_resolver->SetRulesFromString(
76      command_line.GetSwitchValueASCII(switches::kHostResolverRules));
77  return remapped_resolver;
78}
79
80class LoggingNetworkChangeObserver
81    : public net::NetworkChangeNotifier::Observer {
82 public:
83  // |net_log| must remain valid throughout our lifetime.
84  explicit LoggingNetworkChangeObserver(net::NetLog* net_log)
85      : net_log_(net_log) {
86    net::NetworkChangeNotifier::AddObserver(this);
87  }
88
89  ~LoggingNetworkChangeObserver() {
90    net::NetworkChangeNotifier::RemoveObserver(this);
91  }
92
93  virtual void OnIPAddressChanged() {
94    LOG(INFO) << "Observed a change to the network IP addresses";
95
96    net_log_->AddEntry(net::NetLog::TYPE_NETWORK_IP_ADDRESSES_CHANGED,
97                       base::TimeTicks::Now(),
98                       net::NetLog::Source(),
99                       net::NetLog::PHASE_NONE,
100                       NULL);
101  }
102
103 private:
104  net::NetLog* net_log_;
105  DISALLOW_COPY_AND_ASSIGN(LoggingNetworkChangeObserver);
106};
107
108}  // namespace
109
110// The IOThread object must outlive any tasks posted to the IO thread before the
111// Quit task.
112DISABLE_RUNNABLE_METHOD_REFCOUNT(IOThread);
113
114IOThread::IOThread()
115    : BrowserProcessSubThread(ChromeThread::IO),
116      globals_(NULL),
117      speculative_interceptor_(NULL),
118      predictor_(NULL) {}
119
120IOThread::~IOThread() {
121  // We cannot rely on our base class to stop the thread since we want our
122  // CleanUp function to run.
123  Stop();
124  DCHECK(!globals_);
125}
126
127IOThread::Globals* IOThread::globals() {
128  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
129  return globals_;
130}
131
132void IOThread::InitNetworkPredictor(
133    bool prefetching_enabled,
134    base::TimeDelta max_dns_queue_delay,
135    size_t max_concurrent,
136    const chrome_common_net::UrlList& startup_urls,
137    ListValue* referral_list,
138    bool preconnect_enabled) {
139  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
140  message_loop()->PostTask(
141      FROM_HERE,
142      NewRunnableMethod(
143          this,
144          &IOThread::InitNetworkPredictorOnIOThread,
145          prefetching_enabled, max_dns_queue_delay, max_concurrent,
146          startup_urls, referral_list, preconnect_enabled));
147}
148
149void IOThread::ChangedToOnTheRecord() {
150  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
151  message_loop()->PostTask(
152      FROM_HERE,
153      NewRunnableMethod(
154          this,
155          &IOThread::ChangedToOnTheRecordOnIOThread));
156}
157
158void IOThread::Init() {
159  BrowserProcessSubThread::Init();
160
161  DCHECK(!globals_);
162  globals_ = new Globals;
163
164  globals_->net_log.reset(new ChromeNetLog());
165
166  // Add an observer that will emit network change events to the ChromeNetLog.
167  // Assuming NetworkChangeNotifier dispatches in FIFO order, we should be
168  // logging the network change before other IO thread consumers respond to it.
169  network_change_observer_.reset(
170      new LoggingNetworkChangeObserver(globals_->net_log.get()));
171
172  globals_->host_resolver = CreateGlobalHostResolver(globals_->net_log.get());
173  globals_->http_auth_handler_factory.reset(CreateDefaultAuthHandlerFactory(
174      globals_->host_resolver));
175}
176
177void IOThread::CleanUp() {
178  // This must be reset before the ChromeNetLog is destroyed.
179  network_change_observer_.reset();
180
181  // If any child processes are still running, terminate them and
182  // and delete the BrowserChildProcessHost instances to release whatever
183  // IO thread only resources they are referencing.
184  BrowserChildProcessHost::TerminateAll();
185
186  // Not initialized in Init().  May not be initialized.
187  if (predictor_) {
188    predictor_->Shutdown();
189
190    // TODO(willchan): Stop reference counting Predictor.  It's owned by
191    // IOThread now.
192    predictor_->Release();
193    predictor_ = NULL;
194    chrome_browser_net::FreePredictorResources();
195  }
196
197  // Deletion will unregister this interceptor.
198  delete speculative_interceptor_;
199  speculative_interceptor_ = NULL;
200
201  // TODO(eroman): hack for http://crbug.com/15513
202  if (globals_->host_resolver->GetAsHostResolverImpl()) {
203    globals_->host_resolver.get()->GetAsHostResolverImpl()->Shutdown();
204  }
205
206  // We will delete the NetLog as part of CleanUpAfterMessageLoopDestruction()
207  // in case any of the message loop destruction observers try to access it.
208  deferred_net_log_to_delete_.reset(globals_->net_log.release());
209
210  delete globals_;
211  globals_ = NULL;
212
213  BrowserProcessSubThread::CleanUp();
214}
215
216void IOThread::CleanUpAfterMessageLoopDestruction() {
217  // TODO(eroman): get rid of this special case for 39723. If we could instead
218  // have a method that runs after the message loop destruction obsevers have
219  // run, but before the message loop itself is destroyed, we could safely
220  // combine the two cleanups.
221  deferred_net_log_to_delete_.reset();
222  BrowserProcessSubThread::CleanUpAfterMessageLoopDestruction();
223
224  // URLRequest instances must NOT outlive the IO thread.
225  //
226  // To allow for URLRequests to be deleted from
227  // MessageLoop::DestructionObserver this check has to happen after CleanUp
228  // (which runs before DestructionObservers).
229  base::LeakTracker<URLRequest>::CheckForLeaks();
230}
231
232net::HttpAuthHandlerFactory* IOThread::CreateDefaultAuthHandlerFactory(
233    net::HostResolver* resolver) {
234  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
235
236  // Get the whitelist information from the command line, create an
237  // HttpAuthFilterWhitelist, and attach it to the HttpAuthHandlerFactory.
238  net::HttpAuthFilterWhitelist* auth_filter_default_credentials = NULL;
239  if (command_line.HasSwitch(switches::kAuthServerWhitelist)) {
240    auth_filter_default_credentials = new net::HttpAuthFilterWhitelist(
241        command_line.GetSwitchValueASCII(switches::kAuthServerWhitelist));
242  }
243  net::HttpAuthFilterWhitelist* auth_filter_delegate = NULL;
244  if (command_line.HasSwitch(switches::kAuthNegotiateDelegateWhitelist)) {
245    auth_filter_delegate = new net::HttpAuthFilterWhitelist(
246        command_line.GetSwitchValueASCII(
247            switches::kAuthNegotiateDelegateWhitelist));
248  }
249  globals_->url_security_manager.reset(
250      net::URLSecurityManager::Create(auth_filter_default_credentials,
251                                      auth_filter_delegate));
252
253  // Determine which schemes are supported.
254  std::string csv_auth_schemes = "basic,digest,ntlm,negotiate";
255  if (command_line.HasSwitch(switches::kAuthSchemes))
256    csv_auth_schemes = StringToLowerASCII(
257        command_line.GetSwitchValueASCII(switches::kAuthSchemes));
258  std::vector<std::string> supported_schemes;
259  SplitString(csv_auth_schemes, ',', &supported_schemes);
260
261  return net::HttpAuthHandlerRegistryFactory::Create(
262      supported_schemes,
263      globals_->url_security_manager.get(),
264      resolver,
265      command_line.HasSwitch(switches::kDisableAuthNegotiateCnameLookup),
266      command_line.HasSwitch(switches::kEnableAuthNegotiatePort));
267}
268
269void IOThread::InitNetworkPredictorOnIOThread(
270    bool prefetching_enabled,
271    base::TimeDelta max_dns_queue_delay,
272    size_t max_concurrent,
273    const chrome_common_net::UrlList& startup_urls,
274    ListValue* referral_list,
275    bool preconnect_enabled) {
276  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
277  CHECK(!predictor_);
278
279  chrome_browser_net::EnablePredictor(prefetching_enabled);
280
281  predictor_ = new chrome_browser_net::Predictor(
282      globals_->host_resolver,
283      max_dns_queue_delay,
284      max_concurrent,
285      preconnect_enabled);
286  predictor_->AddRef();
287
288  // Speculative_interceptor_ is used to predict subresource usage.
289  DCHECK(!speculative_interceptor_);
290  speculative_interceptor_ = new chrome_browser_net::ConnectInterceptor;
291
292  FinalizePredictorInitialization(predictor_, startup_urls, referral_list);
293}
294
295void IOThread::ChangedToOnTheRecordOnIOThread() {
296  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
297
298  if (predictor_) {
299    // Destroy all evidence of our OTR session.
300    predictor_->Predictor::DiscardAllResults();
301  }
302
303  // Clear the host cache to avoid showing entries from the OTR session
304  // in about:net-internals.
305  if (globals_->host_resolver->GetAsHostResolverImpl()) {
306    net::HostCache* host_cache =
307        globals_->host_resolver.get()->GetAsHostResolverImpl()->cache();
308    if (host_cache)
309      host_cache->clear();
310  }
311  // Clear all of the passively logged data.
312  // TODO(eroman): this is a bit heavy handed, really all we need to do is
313  //               clear the data pertaining to off the record context.
314  globals_->net_log->passive_collector()->Clear();
315}
316