1 // Copyright (c) 2011 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.
5 #include "components/proxy_config/pref_proxy_config_tracker_impl.h"
8 #include "base/location.h"
9 #include "base/metrics/histogram_macros.h"
10 #include "base/prefs/pref_registry_simple.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/strings/string_util.h"
14 #include "base/values.h"
15 #include "components/pref_registry/pref_registry_syncable.h"
16 #include "components/proxy_config/proxy_config_dictionary.h"
17 #include "components/proxy_config/proxy_config_pref_names.h"
18 #include "net/proxy/proxy_list.h"
19 #include "net/proxy/proxy_server.h"
24 // Determine if |proxy| is of the form "*.googlezip.net".
25 bool IsGooglezipDataReductionProxy(const net::ProxyServer
& proxy
) {
26 return proxy
.is_valid() && !proxy
.is_direct() &&
27 base::EndsWith(proxy
.host_port_pair().host(), ".googlezip.net",
28 base::CompareCase::SENSITIVE
);
31 // Removes any Data Reduction Proxies like *.googlezip.net from |proxy_list|.
32 // Returns the number of proxies that were removed from |proxy_list|.
33 size_t RemoveGooglezipDataReductionProxiesFromList(net::ProxyList
* proxy_list
) {
34 bool found_googlezip_proxy
= false;
35 for (const net::ProxyServer
& proxy
: proxy_list
->GetAll()) {
36 if (IsGooglezipDataReductionProxy(proxy
)) {
37 found_googlezip_proxy
= true;
41 if (!found_googlezip_proxy
)
44 size_t num_removed_proxies
= 0;
45 net::ProxyList replacement_list
;
46 for (const net::ProxyServer
& proxy
: proxy_list
->GetAll()) {
47 if (!IsGooglezipDataReductionProxy(proxy
))
48 replacement_list
.AddProxyServer(proxy
);
50 ++num_removed_proxies
;
53 if (replacement_list
.IsEmpty())
54 replacement_list
.AddProxyServer(net::ProxyServer::Direct());
55 *proxy_list
= replacement_list
;
56 return num_removed_proxies
;
59 // Remove any Data Reduction Proxies like *.googlezip.net from |proxy_rules|.
60 // This is to prevent a Data Reduction Proxy from being activated in an
61 // unsupported way, such as from a proxy pref, which could cause Chrome to use
62 // the Data Reduction Proxy without adding any of the necessary authentication
63 // headers or applying the Data Reduction Proxy bypass logic. See
64 // http://crbug.com/476610.
65 // TODO(sclittle): This method should be removed once the UMA indicates that
66 // *.googlezip.net proxies are no longer present in the |proxy_rules|.
67 void RemoveGooglezipDataReductionProxies(
68 net::ProxyConfig::ProxyRules
* proxy_rules
) {
69 size_t num_removed_proxies
=
70 RemoveGooglezipDataReductionProxiesFromList(
71 &proxy_rules
->fallback_proxies
) +
72 RemoveGooglezipDataReductionProxiesFromList(
73 &proxy_rules
->proxies_for_ftp
) +
74 RemoveGooglezipDataReductionProxiesFromList(
75 &proxy_rules
->proxies_for_http
) +
76 RemoveGooglezipDataReductionProxiesFromList(
77 &proxy_rules
->proxies_for_https
) +
78 RemoveGooglezipDataReductionProxiesFromList(&proxy_rules
->single_proxies
);
80 UMA_HISTOGRAM_COUNTS_100("Net.PrefProxyConfig.GooglezipProxyRemovalCount",
86 //============================= ProxyConfigServiceImpl =======================
88 ProxyConfigServiceImpl::ProxyConfigServiceImpl(
89 net::ProxyConfigService
* base_service
)
90 : base_service_(base_service
),
91 pref_config_state_(ProxyPrefs::CONFIG_UNSET
),
92 pref_config_read_pending_(true),
93 registered_observer_(false) {
94 // ProxyConfigServiceImpl is created on the UI thread, but used on the network
96 thread_checker_
.DetachFromThread();
99 ProxyConfigServiceImpl::~ProxyConfigServiceImpl() {
100 if (registered_observer_
&& base_service_
.get())
101 base_service_
->RemoveObserver(this);
104 void ProxyConfigServiceImpl::AddObserver(
105 net::ProxyConfigService::Observer
* observer
) {
107 observers_
.AddObserver(observer
);
110 void ProxyConfigServiceImpl::RemoveObserver(
111 net::ProxyConfigService::Observer
* observer
) {
112 observers_
.RemoveObserver(observer
);
115 net::ProxyConfigService::ConfigAvailability
116 ProxyConfigServiceImpl::GetLatestProxyConfig(net::ProxyConfig
* config
) {
119 if (pref_config_read_pending_
)
120 return net::ProxyConfigService::CONFIG_PENDING
;
122 // Ask the base service if available.
123 net::ProxyConfig system_config
;
124 ConfigAvailability system_availability
=
125 net::ProxyConfigService::CONFIG_UNSET
;
126 if (base_service_
.get())
127 system_availability
= base_service_
->GetLatestProxyConfig(&system_config
);
129 ProxyPrefs::ConfigState config_state
;
130 return PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig(
131 pref_config_state_
, pref_config_
, system_availability
, system_config
,
132 false, &config_state
, config
);
135 void ProxyConfigServiceImpl::OnLazyPoll() {
136 if (base_service_
.get())
137 base_service_
->OnLazyPoll();
140 void ProxyConfigServiceImpl::UpdateProxyConfig(
141 ProxyPrefs::ConfigState config_state
,
142 const net::ProxyConfig
& config
) {
143 DCHECK(thread_checker_
.CalledOnValidThread());
144 pref_config_read_pending_
= false;
145 pref_config_state_
= config_state
;
146 pref_config_
= config
;
148 if (!observers_
.might_have_observers())
151 // Evaluate the proxy configuration. If GetLatestProxyConfig returns
152 // CONFIG_PENDING, we are using the system proxy service, but it doesn't have
153 // a valid configuration yet. Once it is ready, OnProxyConfigChanged() will be
154 // called and broadcast the proxy configuration.
155 // Note: If a switch between a preference proxy configuration and the system
156 // proxy configuration occurs an unnecessary notification might get send if
157 // the two configurations agree. This case should be rare however, so we don't
158 // handle that case specially.
159 net::ProxyConfig new_config
;
160 ConfigAvailability availability
= GetLatestProxyConfig(&new_config
);
161 if (availability
!= CONFIG_PENDING
) {
162 FOR_EACH_OBSERVER(net::ProxyConfigService::Observer
, observers_
,
163 OnProxyConfigChanged(new_config
, availability
));
167 void ProxyConfigServiceImpl::OnProxyConfigChanged(
168 const net::ProxyConfig
& config
,
169 ConfigAvailability availability
) {
170 DCHECK(thread_checker_
.CalledOnValidThread());
172 // Check whether there is a proxy configuration defined by preferences. In
173 // this case that proxy configuration takes precedence and the change event
174 // from the delegate proxy service can be disregarded.
175 if (!PrefProxyConfigTrackerImpl::PrefPrecedes(pref_config_state_
)) {
176 net::ProxyConfig actual_config
;
177 availability
= GetLatestProxyConfig(&actual_config
);
178 FOR_EACH_OBSERVER(net::ProxyConfigService::Observer
, observers_
,
179 OnProxyConfigChanged(actual_config
, availability
));
183 void ProxyConfigServiceImpl::RegisterObserver() {
184 DCHECK(thread_checker_
.CalledOnValidThread());
185 if (!registered_observer_
&& base_service_
.get()) {
186 base_service_
->AddObserver(this);
187 registered_observer_
= true;
191 //========================= PrefProxyConfigTrackerImpl =========================
193 PrefProxyConfigTrackerImpl::PrefProxyConfigTrackerImpl(
194 PrefService
* pref_service
,
195 scoped_refptr
<base::SingleThreadTaskRunner
> io_task_runner
)
196 : pref_service_(pref_service
),
197 proxy_config_service_impl_(NULL
),
198 update_pending_(true),
199 io_task_runner_(io_task_runner
) {
200 config_state_
= ReadPrefConfig(pref_service_
, &pref_config_
);
201 proxy_prefs_
.Init(pref_service
);
202 proxy_prefs_
.Add(proxy_config::prefs::kProxy
,
203 base::Bind(&PrefProxyConfigTrackerImpl::OnProxyPrefChanged
,
204 base::Unretained(this)));
207 PrefProxyConfigTrackerImpl::~PrefProxyConfigTrackerImpl() {
208 DCHECK(pref_service_
== NULL
);
211 scoped_ptr
<net::ProxyConfigService
>
212 PrefProxyConfigTrackerImpl::CreateTrackingProxyConfigService(
213 scoped_ptr
<net::ProxyConfigService
> base_service
) {
214 proxy_config_service_impl_
=
215 new ProxyConfigServiceImpl(base_service
.release());
216 VLOG(1) << this << ": set chrome proxy config service to "
217 << proxy_config_service_impl_
;
218 if (proxy_config_service_impl_
&& update_pending_
)
219 OnProxyConfigChanged(config_state_
, pref_config_
);
221 return scoped_ptr
<net::ProxyConfigService
>(proxy_config_service_impl_
);
224 void PrefProxyConfigTrackerImpl::DetachFromPrefService() {
225 DCHECK(thread_checker_
.CalledOnValidThread());
226 // Stop notifications.
227 proxy_prefs_
.RemoveAll();
228 pref_service_
= NULL
;
229 proxy_config_service_impl_
= NULL
;
233 bool PrefProxyConfigTrackerImpl::PrefPrecedes(
234 ProxyPrefs::ConfigState config_state
) {
235 return config_state
== ProxyPrefs::CONFIG_POLICY
||
236 config_state
== ProxyPrefs::CONFIG_EXTENSION
||
237 config_state
== ProxyPrefs::CONFIG_OTHER_PRECEDE
;
241 net::ProxyConfigService::ConfigAvailability
242 PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig(
243 ProxyPrefs::ConfigState pref_state
,
244 const net::ProxyConfig
& pref_config
,
245 net::ProxyConfigService::ConfigAvailability system_availability
,
246 const net::ProxyConfig
& system_config
,
247 bool ignore_fallback_config
,
248 ProxyPrefs::ConfigState
* effective_config_state
,
249 net::ProxyConfig
* effective_config
) {
250 net::ProxyConfigService::ConfigAvailability rv
;
251 *effective_config_state
= pref_state
;
253 if (PrefPrecedes(pref_state
)) {
254 *effective_config
= pref_config
;
255 rv
= net::ProxyConfigService::CONFIG_VALID
;
256 } else if (system_availability
== net::ProxyConfigService::CONFIG_UNSET
) {
257 // If there's no system proxy config, fall back to prefs or default.
258 if (pref_state
== ProxyPrefs::CONFIG_FALLBACK
&& !ignore_fallback_config
)
259 *effective_config
= pref_config
;
261 *effective_config
= net::ProxyConfig::CreateDirect();
262 rv
= net::ProxyConfigService::CONFIG_VALID
;
264 *effective_config_state
= ProxyPrefs::CONFIG_SYSTEM
;
265 *effective_config
= system_config
;
266 rv
= system_availability
;
269 // Remove any Data Reduction Proxies like *.googlezip.net from the proxy
270 // config rules, since specifying a DRP in the proxy rules is not a supported
271 // means of activating the DRP, and could cause requests to be sent to the DRP
272 // without the appropriate authentication headers and without using any of the
273 // DRP bypass logic. This prevents the Data Reduction Proxy from being
274 // improperly activated via the proxy pref.
275 // TODO(sclittle): This is a temporary fix for http://crbug.com/476610, and
276 // should be removed once that bug is fixed and verified.
277 if (rv
== net::ProxyConfigService::CONFIG_VALID
)
278 RemoveGooglezipDataReductionProxies(&effective_config
->proxy_rules());
284 void PrefProxyConfigTrackerImpl::RegisterPrefs(PrefRegistrySimple
* registry
) {
285 base::DictionaryValue
* default_settings
=
286 ProxyConfigDictionary::CreateSystem();
287 registry
->RegisterDictionaryPref(proxy_config::prefs::kProxy
,
292 void PrefProxyConfigTrackerImpl::RegisterProfilePrefs(
293 user_prefs::PrefRegistrySyncable
* pref_service
) {
294 base::DictionaryValue
* default_settings
=
295 ProxyConfigDictionary::CreateSystem();
296 pref_service
->RegisterDictionaryPref(proxy_config::prefs::kProxy
,
301 ProxyPrefs::ConfigState
PrefProxyConfigTrackerImpl::ReadPrefConfig(
302 const PrefService
* pref_service
,
303 net::ProxyConfig
* config
) {
304 // Clear the configuration and source.
305 *config
= net::ProxyConfig();
306 ProxyPrefs::ConfigState config_state
= ProxyPrefs::CONFIG_UNSET
;
308 const PrefService::Preference
* pref
=
309 pref_service
->FindPreference(proxy_config::prefs::kProxy
);
312 const base::DictionaryValue
* dict
=
313 pref_service
->GetDictionary(proxy_config::prefs::kProxy
);
315 ProxyConfigDictionary
proxy_dict(dict
);
317 if (PrefConfigToNetConfig(proxy_dict
, config
)) {
318 if (!pref
->IsUserModifiable() || pref
->HasUserSetting()) {
319 if (pref
->IsManaged())
320 config_state
= ProxyPrefs::CONFIG_POLICY
;
321 else if (pref
->IsExtensionControlled())
322 config_state
= ProxyPrefs::CONFIG_EXTENSION
;
324 config_state
= ProxyPrefs::CONFIG_OTHER_PRECEDE
;
326 config_state
= ProxyPrefs::CONFIG_FALLBACK
;
333 ProxyPrefs::ConfigState
PrefProxyConfigTrackerImpl::GetProxyConfig(
334 net::ProxyConfig
* config
) {
335 DCHECK(thread_checker_
.CalledOnValidThread());
336 if (config_state_
!= ProxyPrefs::CONFIG_UNSET
)
337 *config
= pref_config_
;
338 return config_state_
;
341 void PrefProxyConfigTrackerImpl::OnProxyConfigChanged(
342 ProxyPrefs::ConfigState config_state
,
343 const net::ProxyConfig
& config
) {
344 if (!proxy_config_service_impl_
) {
345 VLOG(1) << "No chrome proxy config service to push to UpdateProxyConfig";
346 update_pending_
= true;
349 update_pending_
= !io_task_runner_
->PostTask(
350 FROM_HERE
, base::Bind(&ProxyConfigServiceImpl::UpdateProxyConfig
,
351 base::Unretained(proxy_config_service_impl_
),
352 config_state
, config
));
353 VLOG(1) << this << (update_pending_
? ": Error" : ": Done")
354 << " pushing proxy to UpdateProxyConfig";
357 bool PrefProxyConfigTrackerImpl::PrefConfigToNetConfig(
358 const ProxyConfigDictionary
& proxy_dict
,
359 net::ProxyConfig
* config
) {
360 ProxyPrefs::ProxyMode mode
;
361 if (!proxy_dict
.GetMode(&mode
)) {
362 // Fall back to system settings if the mode preference is invalid.
367 case ProxyPrefs::MODE_SYSTEM
:
368 // Use system settings.
370 case ProxyPrefs::MODE_DIRECT
:
371 // Ignore all the other proxy config preferences if the use of a proxy
372 // has been explicitly disabled.
374 case ProxyPrefs::MODE_AUTO_DETECT
:
375 config
->set_auto_detect(true);
377 case ProxyPrefs::MODE_PAC_SCRIPT
: {
378 std::string proxy_pac
;
379 if (!proxy_dict
.GetPacUrl(&proxy_pac
)) {
380 LOG(ERROR
) << "Proxy settings request PAC script but do not specify "
381 << "its URL. Falling back to direct connection.";
384 GURL
proxy_pac_url(proxy_pac
);
385 if (!proxy_pac_url
.is_valid()) {
386 LOG(ERROR
) << "Invalid proxy PAC url: " << proxy_pac
;
389 config
->set_pac_url(proxy_pac_url
);
390 bool pac_mandatory
= false;
391 proxy_dict
.GetPacMandatory(&pac_mandatory
);
392 config
->set_pac_mandatory(pac_mandatory
);
395 case ProxyPrefs::MODE_FIXED_SERVERS
: {
396 std::string proxy_server
;
397 if (!proxy_dict
.GetProxyServer(&proxy_server
)) {
398 LOG(ERROR
) << "Proxy settings request fixed proxy servers but do not "
399 << "specify their URLs. Falling back to direct connection.";
402 config
->proxy_rules().ParseFromString(proxy_server
);
404 std::string proxy_bypass
;
405 if (proxy_dict
.GetBypassList(&proxy_bypass
)) {
406 config
->proxy_rules().bypass_rules
.ParseFromString(proxy_bypass
);
410 case ProxyPrefs::kModeCount
: {
411 // Fall through to NOTREACHED().
414 NOTREACHED() << "Unknown proxy mode, falling back to system settings.";
418 void PrefProxyConfigTrackerImpl::OnProxyPrefChanged() {
419 DCHECK(thread_checker_
.CalledOnValidThread());
420 net::ProxyConfig new_config
;
421 ProxyPrefs::ConfigState config_state
=
422 ReadPrefConfig(pref_service_
, &new_config
);
423 if (config_state_
!= config_state
||
424 (config_state_
!= ProxyPrefs::CONFIG_UNSET
&&
425 !pref_config_
.Equals(new_config
))) {
426 config_state_
= config_state
;
427 if (config_state_
!= ProxyPrefs::CONFIG_UNSET
)
428 pref_config_
= new_config
;
429 update_pending_
= true;
432 OnProxyConfigChanged(config_state
, new_config
);