0e2aa1864d15c940634063dec9a2ea23875e6de0
[platform/framework/web/crosswalk.git] / src / chrome / browser / intranet_redirect_detector.cc
1 // Copyright (c) 2012 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/intranet_redirect_detector.h"
6
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/prefs/pref_registry_simple.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/rand_util.h"
12 #include "base/stl_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/common/chrome_switches.h"
16 #include "chrome/common/pref_names.h"
17 #include "net/base/load_flags.h"
18 #include "net/base/net_errors.h"
19 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
20 #include "net/url_request/url_fetcher.h"
21 #include "net/url_request/url_request_context_getter.h"
22 #include "net/url_request/url_request_status.h"
23
24 const size_t IntranetRedirectDetector::kNumCharsInHostnames = 10;
25
26 IntranetRedirectDetector::IntranetRedirectDetector()
27     : redirect_origin_(g_browser_process->local_state()->GetString(
28           prefs::kLastKnownIntranetRedirectOrigin)),
29       in_sleep_(true),
30       weak_ptr_factory_(this) {
31   // Because this function can be called during startup, when kicking off a URL
32   // fetch can eat up 20 ms of time, we delay seven seconds, which is hopefully
33   // long enough to be after startup, but still get results back quickly.
34   // Ideally, instead of this timer, we'd do something like "check if the
35   // browser is starting up, and if so, come back later", but there is currently
36   // no function to do this.
37   static const int kStartFetchDelaySeconds = 7;
38   base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
39       base::Bind(&IntranetRedirectDetector::FinishSleep,
40                  weak_ptr_factory_.GetWeakPtr()),
41       base::TimeDelta::FromSeconds(kStartFetchDelaySeconds));
42
43   net::NetworkChangeNotifier::AddIPAddressObserver(this);
44 }
45
46 IntranetRedirectDetector::~IntranetRedirectDetector() {
47   net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
48   STLDeleteElements(&fetchers_);
49 }
50
51 // static
52 GURL IntranetRedirectDetector::RedirectOrigin() {
53   const IntranetRedirectDetector* const detector =
54       g_browser_process->intranet_redirect_detector();
55   return detector ? detector->redirect_origin_ : GURL();
56 }
57
58 // static
59 void IntranetRedirectDetector::RegisterPrefs(PrefRegistrySimple* registry) {
60   registry->RegisterStringPref(prefs::kLastKnownIntranetRedirectOrigin,
61                                std::string());
62 }
63
64 void IntranetRedirectDetector::FinishSleep() {
65   in_sleep_ = false;
66
67   // If another fetch operation is still running, cancel it.
68   STLDeleteElements(&fetchers_);
69   resulting_origins_.clear();
70
71   const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
72   if (cmd_line->HasSwitch(switches::kDisableBackgroundNetworking))
73     return;
74
75   DCHECK(fetchers_.empty() && resulting_origins_.empty());
76
77   // Start three fetchers on random hostnames.
78   for (size_t i = 0; i < 3; ++i) {
79     std::string url_string("http://");
80     for (size_t j = 0; j < kNumCharsInHostnames; ++j)
81       url_string += ('a' + base::RandInt(0, 'z' - 'a'));
82     GURL random_url(url_string + '/');
83     net::URLFetcher* fetcher = net::URLFetcher::Create(
84         random_url, net::URLFetcher::HEAD, this);
85     // We don't want these fetches to affect existing state in the profile.
86     fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE |
87                           net::LOAD_DO_NOT_SAVE_COOKIES |
88                           net::LOAD_DO_NOT_SEND_COOKIES);
89     fetcher->SetRequestContext(g_browser_process->system_request_context());
90     fetcher->Start();
91     fetchers_.insert(fetcher);
92   }
93 }
94
95 void IntranetRedirectDetector::OnURLFetchComplete(
96     const net::URLFetcher* source) {
97   // Delete the fetcher on this function's exit.
98   Fetchers::iterator fetcher = fetchers_.find(
99       const_cast<net::URLFetcher*>(source));
100   DCHECK(fetcher != fetchers_.end());
101   scoped_ptr<net::URLFetcher> clean_up_fetcher(*fetcher);
102   fetchers_.erase(fetcher);
103
104   // If any two fetches result in the same domain/host, we set the redirect
105   // origin to that; otherwise we set it to nothing.
106   if (!source->GetStatus().is_success() || (source->GetResponseCode() != 200)) {
107     if ((resulting_origins_.empty()) ||
108         ((resulting_origins_.size() == 1) &&
109          resulting_origins_.front().is_valid())) {
110       resulting_origins_.push_back(GURL());
111       return;
112     }
113     redirect_origin_ = GURL();
114   } else {
115     DCHECK(source->GetURL().is_valid());
116     GURL origin(source->GetURL().GetOrigin());
117     if (resulting_origins_.empty()) {
118       resulting_origins_.push_back(origin);
119       return;
120     }
121     if (net::registry_controlled_domains::SameDomainOrHost(
122         resulting_origins_.front(),
123         origin,
124         net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES)) {
125       redirect_origin_ = origin;
126       if (!fetchers_.empty()) {
127         // Cancel remaining fetch, we don't need it.
128         DCHECK(fetchers_.size() == 1);
129         delete (*fetchers_.begin());
130         fetchers_.clear();
131       }
132     }
133     if (resulting_origins_.size() == 1) {
134       resulting_origins_.push_back(origin);
135       return;
136     }
137     DCHECK(resulting_origins_.size() == 2);
138     const bool same_domain_or_host =
139         net::registry_controlled_domains::SameDomainOrHost(
140             resulting_origins_.back(),
141             origin,
142             net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
143     redirect_origin_ = same_domain_or_host ? origin : GURL();
144   }
145
146   g_browser_process->local_state()->SetString(
147       prefs::kLastKnownIntranetRedirectOrigin, redirect_origin_.is_valid() ?
148           redirect_origin_.spec() : std::string());
149 }
150
151 void IntranetRedirectDetector::OnIPAddressChanged() {
152   // If a request is already scheduled, do not scheduled yet another one.
153   if (in_sleep_)
154     return;
155
156   // Since presumably many programs open connections after network changes,
157   // delay this a little bit.
158   in_sleep_ = true;
159   static const int kNetworkSwitchDelayMS = 1000;
160   base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
161       base::Bind(&IntranetRedirectDetector::FinishSleep,
162                  weak_ptr_factory_.GetWeakPtr()),
163       base::TimeDelta::FromMilliseconds(kNetworkSwitchDelayMS));
164 }
165
166 IntranetRedirectHostResolverProc::IntranetRedirectHostResolverProc(
167     net::HostResolverProc* previous)
168     : net::HostResolverProc(previous) {
169 }
170
171 int IntranetRedirectHostResolverProc::Resolve(
172     const std::string& host,
173     net::AddressFamily address_family,
174     net::HostResolverFlags host_resolver_flags,
175     net::AddressList* addrlist,
176     int* os_error) {
177   // We'd love to just ask the IntranetRedirectDetector, but we may not be on
178   // the same thread.  So just use the heuristic that any all-lowercase a-z
179   // hostname with the right number of characters is likely from the detector
180   // (and thus should be blocked).
181   return ((host.length() == IntranetRedirectDetector::kNumCharsInHostnames) &&
182       (host.find_first_not_of("abcdefghijklmnopqrstuvwxyz") ==
183           std::string::npos)) ?
184       net::ERR_NAME_NOT_RESOLVED :
185       ResolveUsingPrevious(host, address_family, host_resolver_flags, addrlist,
186                            os_error);
187 }