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 "chrome/browser/net/pref_proxy_config_tracker_impl.h"
8 #include "base/prefs/pref_registry_simple.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/strings/string_util.h"
11 #include "base/values.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/prefs/proxy_config_dictionary.h"
14 #include "chrome/common/pref_names.h"
15 #include "components/pref_registry/pref_registry_syncable.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/notification_details.h"
18 #include "content/public/browser/notification_source.h"
19 #include "net/proxy/proxy_list.h"
20 #include "net/proxy/proxy_server.h"
22 using content::BrowserThread
;
26 // Determine if |proxy| is of the form "*.googlezip.net".
27 bool IsGooglezipDataReductionProxy(const net::ProxyServer
& proxy
) {
28 return proxy
.is_valid() && !proxy
.is_direct() &&
29 EndsWith(proxy
.host_port_pair().host(), ".googlezip.net", true);
32 // Removes any Data Reduction Proxies like *.googlezip.net from |proxy_list|.
33 void RemoveGooglezipDataReductionProxiesFromList(net::ProxyList
* proxy_list
) {
34 if (proxy_list
->IsEmpty())
37 bool found_googlezip_proxy
= false;
38 for (const net::ProxyServer
& proxy
: proxy_list
->GetAll()) {
39 if (IsGooglezipDataReductionProxy(proxy
)) {
40 found_googlezip_proxy
= true;
44 if (!found_googlezip_proxy
)
47 net::ProxyList replacement_list
;
48 for (const net::ProxyServer
& proxy
: proxy_list
->GetAll()) {
49 if (!IsGooglezipDataReductionProxy(proxy
))
50 replacement_list
.AddProxyServer(proxy
);
53 if (replacement_list
.IsEmpty())
54 replacement_list
.AddProxyServer(net::ProxyServer::Direct());
55 *proxy_list
= replacement_list
;
58 // Remove any Data Reduction Proxies like *.googlezip.net from |proxy_rules|.
59 // This is to prevent a Data Reduction Proxy from being activated in an
60 // unsupported way, such as from a proxy pref, which could cause Chrome to use
61 // the Data Reduction Proxy without adding any of the necessary authentication
62 // headers or applying the Data Reduction Proxy bypass logic. See
63 // http://crbug.com/476610.
64 // TODO(sclittle): Add UMA to record how often this method is called, and how
65 // often it actually removes a *.googlezip.net proxy. This method should be
66 // removed once it stops actually finding and removing *.googlezip.net proxies
67 // from the proxy rules.
68 void RemoveGooglezipDataReductionProxies(
69 net::ProxyConfig::ProxyRules
* proxy_rules
) {
70 RemoveGooglezipDataReductionProxiesFromList(&proxy_rules
->fallback_proxies
);
71 RemoveGooglezipDataReductionProxiesFromList(&proxy_rules
->proxies_for_ftp
);
72 RemoveGooglezipDataReductionProxiesFromList(&proxy_rules
->proxies_for_http
);
73 RemoveGooglezipDataReductionProxiesFromList(&proxy_rules
->proxies_for_https
);
74 RemoveGooglezipDataReductionProxiesFromList(&proxy_rules
->single_proxies
);
79 //============================= ChromeProxyConfigService =======================
81 ChromeProxyConfigService::ChromeProxyConfigService(
82 net::ProxyConfigService
* base_service
)
83 : base_service_(base_service
),
84 pref_config_state_(ProxyPrefs::CONFIG_UNSET
),
85 pref_config_read_pending_(true),
86 registered_observer_(false) {
89 ChromeProxyConfigService::~ChromeProxyConfigService() {
90 if (registered_observer_
&& base_service_
.get())
91 base_service_
->RemoveObserver(this);
94 void ChromeProxyConfigService::AddObserver(
95 net::ProxyConfigService::Observer
* observer
) {
97 observers_
.AddObserver(observer
);
100 void ChromeProxyConfigService::RemoveObserver(
101 net::ProxyConfigService::Observer
* observer
) {
102 observers_
.RemoveObserver(observer
);
105 net::ProxyConfigService::ConfigAvailability
106 ChromeProxyConfigService::GetLatestProxyConfig(net::ProxyConfig
* config
) {
109 if (pref_config_read_pending_
)
110 return net::ProxyConfigService::CONFIG_PENDING
;
112 // Ask the base service if available.
113 net::ProxyConfig system_config
;
114 ConfigAvailability system_availability
=
115 net::ProxyConfigService::CONFIG_UNSET
;
116 if (base_service_
.get())
117 system_availability
= base_service_
->GetLatestProxyConfig(&system_config
);
119 ProxyPrefs::ConfigState config_state
;
120 return PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig(
121 pref_config_state_
, pref_config_
,
122 system_availability
, system_config
, false,
123 &config_state
, config
);
126 void ChromeProxyConfigService::OnLazyPoll() {
127 if (base_service_
.get())
128 base_service_
->OnLazyPoll();
131 void ChromeProxyConfigService::UpdateProxyConfig(
132 ProxyPrefs::ConfigState config_state
,
133 const net::ProxyConfig
& config
) {
134 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
136 pref_config_read_pending_
= false;
137 pref_config_state_
= config_state
;
138 pref_config_
= config
;
140 if (!observers_
.might_have_observers())
143 // Evaluate the proxy configuration. If GetLatestProxyConfig returns
144 // CONFIG_PENDING, we are using the system proxy service, but it doesn't have
145 // a valid configuration yet. Once it is ready, OnProxyConfigChanged() will be
146 // called and broadcast the proxy configuration.
147 // Note: If a switch between a preference proxy configuration and the system
148 // proxy configuration occurs an unnecessary notification might get send if
149 // the two configurations agree. This case should be rare however, so we don't
150 // handle that case specially.
151 net::ProxyConfig new_config
;
152 ConfigAvailability availability
= GetLatestProxyConfig(&new_config
);
153 if (availability
!= CONFIG_PENDING
) {
154 FOR_EACH_OBSERVER(net::ProxyConfigService::Observer
, observers_
,
155 OnProxyConfigChanged(new_config
, availability
));
159 void ChromeProxyConfigService::OnProxyConfigChanged(
160 const net::ProxyConfig
& config
,
161 ConfigAvailability availability
) {
162 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
164 // Check whether there is a proxy configuration defined by preferences. In
165 // this case that proxy configuration takes precedence and the change event
166 // from the delegate proxy service can be disregarded.
167 if (!PrefProxyConfigTrackerImpl::PrefPrecedes(pref_config_state_
)) {
168 net::ProxyConfig actual_config
;
169 availability
= GetLatestProxyConfig(&actual_config
);
170 FOR_EACH_OBSERVER(net::ProxyConfigService::Observer
, observers_
,
171 OnProxyConfigChanged(actual_config
, availability
));
175 void ChromeProxyConfigService::RegisterObserver() {
176 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
177 if (!registered_observer_
&& base_service_
.get()) {
178 base_service_
->AddObserver(this);
179 registered_observer_
= true;
183 //========================= PrefProxyConfigTrackerImpl =========================
185 PrefProxyConfigTrackerImpl::PrefProxyConfigTrackerImpl(
186 PrefService
* pref_service
)
187 : pref_service_(pref_service
),
188 chrome_proxy_config_service_(NULL
),
189 update_pending_(true) {
190 config_state_
= ReadPrefConfig(pref_service_
, &pref_config_
);
191 proxy_prefs_
.Init(pref_service
);
192 proxy_prefs_
.Add(prefs::kProxy
,
193 base::Bind(&PrefProxyConfigTrackerImpl::OnProxyPrefChanged
,
194 base::Unretained(this)));
197 PrefProxyConfigTrackerImpl::~PrefProxyConfigTrackerImpl() {
198 DCHECK(pref_service_
== NULL
);
201 scoped_ptr
<net::ProxyConfigService
>
202 PrefProxyConfigTrackerImpl::CreateTrackingProxyConfigService(
203 scoped_ptr
<net::ProxyConfigService
> base_service
) {
204 chrome_proxy_config_service_
=
205 new ChromeProxyConfigService(base_service
.release());
206 VLOG(1) << this << ": set chrome proxy config service to "
207 << chrome_proxy_config_service_
;
208 if (chrome_proxy_config_service_
&& update_pending_
)
209 OnProxyConfigChanged(config_state_
, pref_config_
);
211 return scoped_ptr
<net::ProxyConfigService
>(chrome_proxy_config_service_
);
214 void PrefProxyConfigTrackerImpl::DetachFromPrefService() {
215 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
216 // Stop notifications.
217 proxy_prefs_
.RemoveAll();
218 pref_service_
= NULL
;
219 chrome_proxy_config_service_
= NULL
;
223 bool PrefProxyConfigTrackerImpl::PrefPrecedes(
224 ProxyPrefs::ConfigState config_state
) {
225 return config_state
== ProxyPrefs::CONFIG_POLICY
||
226 config_state
== ProxyPrefs::CONFIG_EXTENSION
||
227 config_state
== ProxyPrefs::CONFIG_OTHER_PRECEDE
;
231 net::ProxyConfigService::ConfigAvailability
232 PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig(
233 ProxyPrefs::ConfigState pref_state
,
234 const net::ProxyConfig
& pref_config
,
235 net::ProxyConfigService::ConfigAvailability system_availability
,
236 const net::ProxyConfig
& system_config
,
237 bool ignore_fallback_config
,
238 ProxyPrefs::ConfigState
* effective_config_state
,
239 net::ProxyConfig
* effective_config
) {
240 net::ProxyConfigService::ConfigAvailability rv
;
241 *effective_config_state
= pref_state
;
243 if (PrefPrecedes(pref_state
)) {
244 *effective_config
= pref_config
;
245 rv
= net::ProxyConfigService::CONFIG_VALID
;
246 } else if (system_availability
== net::ProxyConfigService::CONFIG_UNSET
) {
247 // If there's no system proxy config, fall back to prefs or default.
248 if (pref_state
== ProxyPrefs::CONFIG_FALLBACK
&& !ignore_fallback_config
)
249 *effective_config
= pref_config
;
251 *effective_config
= net::ProxyConfig::CreateDirect();
252 rv
= net::ProxyConfigService::CONFIG_VALID
;
254 *effective_config_state
= ProxyPrefs::CONFIG_SYSTEM
;
255 *effective_config
= system_config
;
256 rv
= system_availability
;
259 // Remove any Data Reduction Proxies like *.googlezip.net from the proxy
260 // config rules, since specifying a DRP in the proxy rules is not a supported
261 // means of activating the DRP, and could cause requests to be sent to the DRP
262 // without the appropriate authentication headers and without using any of the
263 // DRP bypass logic. This prevents the Data Reduction Proxy from being
264 // improperly activated via the proxy pref.
265 // TODO(sclittle): This is a temporary fix for http://crbug.com/476610, and
266 // should be removed once that bug is fixed and verified.
267 if (rv
== net::ProxyConfigService::CONFIG_VALID
)
268 RemoveGooglezipDataReductionProxies(&effective_config
->proxy_rules());
274 void PrefProxyConfigTrackerImpl::RegisterPrefs(PrefRegistrySimple
* registry
) {
275 base::DictionaryValue
* default_settings
=
276 ProxyConfigDictionary::CreateSystem();
277 registry
->RegisterDictionaryPref(prefs::kProxy
, default_settings
);
281 void PrefProxyConfigTrackerImpl::RegisterProfilePrefs(
282 user_prefs::PrefRegistrySyncable
* pref_service
) {
283 base::DictionaryValue
* default_settings
=
284 ProxyConfigDictionary::CreateSystem();
285 pref_service
->RegisterDictionaryPref(prefs::kProxy
, default_settings
);
289 ProxyPrefs::ConfigState
PrefProxyConfigTrackerImpl::ReadPrefConfig(
290 const PrefService
* pref_service
,
291 net::ProxyConfig
* config
) {
292 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
294 // Clear the configuration and source.
295 *config
= net::ProxyConfig();
296 ProxyPrefs::ConfigState config_state
= ProxyPrefs::CONFIG_UNSET
;
298 const PrefService::Preference
* pref
=
299 pref_service
->FindPreference(prefs::kProxy
);
302 const base::DictionaryValue
* dict
=
303 pref_service
->GetDictionary(prefs::kProxy
);
305 ProxyConfigDictionary
proxy_dict(dict
);
307 if (PrefConfigToNetConfig(proxy_dict
, config
)) {
308 if (!pref
->IsUserModifiable() || pref
->HasUserSetting()) {
309 if (pref
->IsManaged())
310 config_state
= ProxyPrefs::CONFIG_POLICY
;
311 else if (pref
->IsExtensionControlled())
312 config_state
= ProxyPrefs::CONFIG_EXTENSION
;
314 config_state
= ProxyPrefs::CONFIG_OTHER_PRECEDE
;
316 config_state
= ProxyPrefs::CONFIG_FALLBACK
;
323 ProxyPrefs::ConfigState
PrefProxyConfigTrackerImpl::GetProxyConfig(
324 net::ProxyConfig
* config
) {
325 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
326 if (config_state_
!= ProxyPrefs::CONFIG_UNSET
)
327 *config
= pref_config_
;
328 return config_state_
;
331 void PrefProxyConfigTrackerImpl::OnProxyConfigChanged(
332 ProxyPrefs::ConfigState config_state
,
333 const net::ProxyConfig
& config
) {
334 if (!chrome_proxy_config_service_
) {
335 VLOG(1) << "No chrome proxy config service to push to UpdateProxyConfig";
336 update_pending_
= true;
339 update_pending_
= !BrowserThread::PostTask(
340 BrowserThread::IO
, FROM_HERE
,
341 base::Bind(&ChromeProxyConfigService::UpdateProxyConfig
,
342 base::Unretained(chrome_proxy_config_service_
),
343 config_state
, config
));
344 VLOG(1) << this << (update_pending_
? ": Error" : ": Done")
345 << " pushing proxy to UpdateProxyConfig";
348 bool PrefProxyConfigTrackerImpl::PrefConfigToNetConfig(
349 const ProxyConfigDictionary
& proxy_dict
,
350 net::ProxyConfig
* config
) {
351 ProxyPrefs::ProxyMode mode
;
352 if (!proxy_dict
.GetMode(&mode
)) {
353 // Fall back to system settings if the mode preference is invalid.
358 case ProxyPrefs::MODE_SYSTEM
:
359 // Use system settings.
361 case ProxyPrefs::MODE_DIRECT
:
362 // Ignore all the other proxy config preferences if the use of a proxy
363 // has been explicitly disabled.
365 case ProxyPrefs::MODE_AUTO_DETECT
:
366 config
->set_auto_detect(true);
368 case ProxyPrefs::MODE_PAC_SCRIPT
: {
369 std::string proxy_pac
;
370 if (!proxy_dict
.GetPacUrl(&proxy_pac
)) {
371 LOG(ERROR
) << "Proxy settings request PAC script but do not specify "
372 << "its URL. Falling back to direct connection.";
375 GURL
proxy_pac_url(proxy_pac
);
376 if (!proxy_pac_url
.is_valid()) {
377 LOG(ERROR
) << "Invalid proxy PAC url: " << proxy_pac
;
380 config
->set_pac_url(proxy_pac_url
);
381 bool pac_mandatory
= false;
382 proxy_dict
.GetPacMandatory(&pac_mandatory
);
383 config
->set_pac_mandatory(pac_mandatory
);
386 case ProxyPrefs::MODE_FIXED_SERVERS
: {
387 std::string proxy_server
;
388 if (!proxy_dict
.GetProxyServer(&proxy_server
)) {
389 LOG(ERROR
) << "Proxy settings request fixed proxy servers but do not "
390 << "specify their URLs. Falling back to direct connection.";
393 config
->proxy_rules().ParseFromString(proxy_server
);
395 std::string proxy_bypass
;
396 if (proxy_dict
.GetBypassList(&proxy_bypass
)) {
397 config
->proxy_rules().bypass_rules
.ParseFromString(proxy_bypass
);
401 case ProxyPrefs::kModeCount
: {
402 // Fall through to NOTREACHED().
405 NOTREACHED() << "Unknown proxy mode, falling back to system settings.";
409 void PrefProxyConfigTrackerImpl::OnProxyPrefChanged() {
410 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
411 net::ProxyConfig new_config
;
412 ProxyPrefs::ConfigState config_state
= ReadPrefConfig(pref_service_
,
414 if (config_state_
!= config_state
||
415 (config_state_
!= ProxyPrefs::CONFIG_UNSET
&&
416 !pref_config_
.Equals(new_config
))) {
417 config_state_
= config_state
;
418 if (config_state_
!= ProxyPrefs::CONFIG_UNSET
)
419 pref_config_
= new_config
;
420 update_pending_
= true;
423 OnProxyConfigChanged(config_state
, new_config
);