1 // Copyright 2014 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/data_reduction_proxy/browser/data_reduction_proxy_settings.h"
8 #include "base/command_line.h"
9 #include "base/metrics/field_trial.h"
10 #include "base/metrics/histogram.h"
11 #include "base/metrics/sparse_histogram.h"
12 #include "base/prefs/pref_member.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/prefs/scoped_user_pref_update.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler.h"
20 #include "components/data_reduction_proxy/browser/data_reduction_proxy_configurator.h"
21 #include "components/data_reduction_proxy/browser/data_reduction_proxy_params.h"
22 #include "components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats.h"
23 #include "components/data_reduction_proxy/common/data_reduction_proxy_pref_names.h"
24 #include "components/data_reduction_proxy/common/data_reduction_proxy_switches.h"
25 #include "net/base/host_port_pair.h"
26 #include "net/base/load_flags.h"
27 #include "net/base/net_errors.h"
28 #include "net/base/net_util.h"
29 #include "net/http/http_network_session.h"
30 #include "net/http/http_response_headers.h"
31 #include "net/url_request/url_fetcher.h"
32 #include "net/url_request/url_fetcher_delegate.h"
33 #include "net/url_request/url_request_context_getter.h"
34 #include "net/url_request/url_request_status.h"
38 using base::StringPrintf
;
41 // Values of the UMA DataReductionProxy.NetworkChangeEvents histograms.
42 // This enum must remain synchronized with the enum of the same
43 // name in metrics/histograms/histograms.xml.
44 enum DataReductionProxyNetworkChangeEvent
{
45 IP_CHANGED
= 0, // The client IP address changed.
46 DISABLED_ON_VPN
= 1, // The proxy is disabled because a VPN is running.
47 CHANGE_EVENT_COUNT
= 2 // This must always be last.
50 // Key of the UMA DataReductionProxy.StartupState histogram.
51 const char kUMAProxyStartupStateHistogram
[] =
52 "DataReductionProxy.StartupState";
54 // Key of the UMA DataReductionProxy.ProbeURL histogram.
55 const char kUMAProxyProbeURL
[] = "DataReductionProxy.ProbeURL";
57 // Key of the UMA DataReductionProxy.ProbeURLNetError histogram.
58 const char kUMAProxyProbeURLNetError
[] = "DataReductionProxy.ProbeURLNetError";
60 // Record a network change event.
61 void RecordNetworkChangeEvent(DataReductionProxyNetworkChangeEvent event
) {
62 UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.NetworkChangeEvents",
67 int64
GetInt64PrefValue(const base::ListValue
& list_value
, size_t index
) {
69 std::string pref_value
;
70 bool rv
= list_value
.GetString(index
, &pref_value
);
73 rv
= base::StringToInt64(pref_value
, &val
);
79 bool IsEnabledOnCommandLine() {
80 const CommandLine
& command_line
= *CommandLine::ForCurrentProcess();
81 return command_line
.HasSwitch(
82 data_reduction_proxy::switches::kEnableDataReductionProxy
);
87 namespace data_reduction_proxy
{
89 DataReductionProxySettings::DataReductionProxySettings(
90 DataReductionProxyParams
* params
)
91 : restricted_by_carrier_(false),
92 enabled_by_user_(false),
93 disabled_on_vpn_(false),
96 local_state_prefs_(NULL
),
97 url_request_context_getter_(NULL
) {
99 params_
.reset(params
);
102 DataReductionProxySettings::~DataReductionProxySettings() {
103 if (params_
->allowed())
104 spdy_proxy_auth_enabled_
.Destroy();
107 void DataReductionProxySettings::InitPrefMembers() {
108 DCHECK(thread_checker_
.CalledOnValidThread());
109 spdy_proxy_auth_enabled_
.Init(
110 prefs::kDataReductionProxyEnabled
,
111 GetOriginalProfilePrefs(),
112 base::Bind(&DataReductionProxySettings::OnProxyEnabledPrefChange
,
113 base::Unretained(this)));
114 data_reduction_proxy_alternative_enabled_
.Init(
115 prefs::kDataReductionProxyAltEnabled
,
116 GetOriginalProfilePrefs(),
118 &DataReductionProxySettings::OnProxyAlternativeEnabledPrefChange
,
119 base::Unretained(this)));
122 void DataReductionProxySettings::InitDataReductionProxySettings(
124 PrefService
* local_state_prefs
,
125 net::URLRequestContextGetter
* url_request_context_getter
) {
126 DCHECK(thread_checker_
.CalledOnValidThread());
128 DCHECK(local_state_prefs
);
129 DCHECK(url_request_context_getter
);
131 local_state_prefs_
= local_state_prefs
;
132 url_request_context_getter_
= url_request_context_getter
;
134 RecordDataReductionInit();
136 // Disable the proxy if it is not allowed to be used.
137 if (!params_
->allowed())
140 AddDefaultProxyBypassRules();
141 net::NetworkChangeNotifier::AddIPAddressObserver(this);
143 // We set or reset the proxy pref at startup.
144 MaybeActivateDataReductionProxy(true);
147 void DataReductionProxySettings::InitDataReductionProxySettings(
149 PrefService
* local_state_prefs
,
150 net::URLRequestContextGetter
* url_request_context_getter
,
151 scoped_ptr
<DataReductionProxyConfigurator
> configurator
) {
152 InitDataReductionProxySettings(prefs
,
154 url_request_context_getter
);
155 SetProxyConfigurator(configurator
.Pass());
158 void DataReductionProxySettings::SetOnDataReductionEnabledCallback(
159 const base::Callback
<void(bool)>& on_data_reduction_proxy_enabled
) {
160 on_data_reduction_proxy_enabled_
= on_data_reduction_proxy_enabled
;
161 on_data_reduction_proxy_enabled_
.Run(IsDataReductionProxyEnabled());
164 void DataReductionProxySettings::SetProxyConfigurator(
165 scoped_ptr
<DataReductionProxyConfigurator
> configurator
) {
166 DCHECK(configurator
);
167 configurator_
= configurator
.Pass();
170 bool DataReductionProxySettings::IsDataReductionProxyEnabled() {
171 return spdy_proxy_auth_enabled_
.GetValue() || IsEnabledOnCommandLine();
175 DataReductionProxySettings::IsDataReductionProxyAlternativeEnabled() const {
176 return data_reduction_proxy_alternative_enabled_
.GetValue();
179 bool DataReductionProxySettings::IsDataReductionProxyManaged() {
180 return spdy_proxy_auth_enabled_
.IsManaged();
183 void DataReductionProxySettings::SetDataReductionProxyEnabled(bool enabled
) {
184 DCHECK(thread_checker_
.CalledOnValidThread());
185 // Prevent configuring the proxy when it is not allowed to be used.
186 if (!params_
->allowed())
189 if (spdy_proxy_auth_enabled_
.GetValue() != enabled
) {
190 spdy_proxy_auth_enabled_
.SetValue(enabled
);
191 OnProxyEnabledPrefChange();
195 void DataReductionProxySettings::SetDataReductionProxyAlternativeEnabled(
197 DCHECK(thread_checker_
.CalledOnValidThread());
198 // Prevent configuring the proxy when it is not allowed to be used.
199 if (!params_
->alternative_allowed())
201 if (data_reduction_proxy_alternative_enabled_
.GetValue() != enabled
) {
202 data_reduction_proxy_alternative_enabled_
.SetValue(enabled
);
203 OnProxyAlternativeEnabledPrefChange();
207 int64
DataReductionProxySettings::GetDataReductionLastUpdateTime() {
208 DCHECK(thread_checker_
.CalledOnValidThread());
209 PrefService
* local_state
= GetLocalStatePrefs();
210 int64 last_update_internal
=
211 local_state
->GetInt64(prefs::kDailyHttpContentLengthLastUpdateDate
);
212 base::Time last_update
= base::Time::FromInternalValue(last_update_internal
);
213 return static_cast<int64
>(last_update
.ToJsTime());
216 DataReductionProxySettings::ContentLengthList
217 DataReductionProxySettings::GetDailyOriginalContentLengths() {
218 DCHECK(thread_checker_
.CalledOnValidThread());
219 return GetDailyContentLengths(prefs::kDailyHttpOriginalContentLength
);
222 void DataReductionProxySettings::SetUnreachable(bool unreachable
) {
223 unreachable_
= unreachable
;
226 bool DataReductionProxySettings::IsDataReductionProxyUnreachable() {
227 DCHECK(thread_checker_
.CalledOnValidThread());
231 DataReductionProxySettings::ContentLengthList
232 DataReductionProxySettings::GetDailyReceivedContentLengths() {
233 DCHECK(thread_checker_
.CalledOnValidThread());
234 return GetDailyContentLengths(prefs::kDailyHttpReceivedContentLength
);
237 void DataReductionProxySettings::OnURLFetchComplete(
238 const net::URLFetcher
* source
) {
239 DCHECK(thread_checker_
.CalledOnValidThread());
241 // The purpose of sending a request for the warmup URL is to warm the
242 // connection to the data_reduction_proxy. The result is ignored.
243 if (source
== warmup_fetcher_
.get())
246 DCHECK(source
== fetcher_
.get());
247 net::URLRequestStatus status
= source
->GetStatus();
248 if (status
.status() == net::URLRequestStatus::FAILED
) {
249 if (status
.error() == net::ERR_INTERNET_DISCONNECTED
) {
250 RecordProbeURLFetchResult(INTERNET_DISCONNECTED
);
253 // TODO(bengr): Remove once we understand the reasons probes are failing.
254 // Probe errors are either due to fetcher-level errors or modified
255 // responses. This only tracks the former.
256 UMA_HISTOGRAM_SPARSE_SLOWLY(
257 kUMAProxyProbeURLNetError
, std::abs(status
.error()));
260 std::string response
;
261 source
->GetResponseAsString(&response
);
263 if ("OK" == response
.substr(0, 2)) {
264 DVLOG(1) << "The data reduction proxy is unrestricted.";
266 if (enabled_by_user_
) {
267 if (restricted_by_carrier_
) {
268 // The user enabled the proxy, but sometime previously in the session,
269 // the network operator had blocked the canary and restricted the user.
270 // The current network doesn't block the canary, so don't restrict the
271 // proxy configurations.
272 SetProxyConfigs(true /* enabled */,
273 IsDataReductionProxyAlternativeEnabled(),
274 false /* restricted */,
275 false /* at_startup */);
276 RecordProbeURLFetchResult(SUCCEEDED_PROXY_ENABLED
);
278 RecordProbeURLFetchResult(SUCCEEDED_PROXY_ALREADY_ENABLED
);
281 restricted_by_carrier_
= false;
284 DVLOG(1) << "The data reduction proxy is restricted to the configured "
285 << "fallback proxy.";
286 if (enabled_by_user_
) {
287 if (!restricted_by_carrier_
) {
288 // Restrict the proxy.
289 SetProxyConfigs(true /* enabled */,
290 IsDataReductionProxyAlternativeEnabled(),
291 true /* restricted */,
292 false /* at_startup */);
293 RecordProbeURLFetchResult(FAILED_PROXY_DISABLED
);
295 RecordProbeURLFetchResult(FAILED_PROXY_ALREADY_DISABLED
);
298 restricted_by_carrier_
= true;
301 PrefService
* DataReductionProxySettings::GetOriginalProfilePrefs() {
302 DCHECK(thread_checker_
.CalledOnValidThread());
306 PrefService
* DataReductionProxySettings::GetLocalStatePrefs() {
307 DCHECK(thread_checker_
.CalledOnValidThread());
308 return local_state_prefs_
;
311 void DataReductionProxySettings::AddDefaultProxyBypassRules() {
313 DCHECK(configurator_
);
314 configurator_
->AddHostPatternToBypass("<local>");
315 // RFC1918 private addresses.
316 configurator_
->AddHostPatternToBypass("10.0.0.0/8");
317 configurator_
->AddHostPatternToBypass("172.16.0.0/12");
318 configurator_
->AddHostPatternToBypass("192.168.0.0/16");
319 // RFC4193 private addresses.
320 configurator_
->AddHostPatternToBypass("fc00::/7");
321 // IPV6 probe addresses.
322 configurator_
->AddHostPatternToBypass("*-ds.metric.gstatic.com");
323 configurator_
->AddHostPatternToBypass("*-v4.metric.gstatic.com");
326 void DataReductionProxySettings::LogProxyState(
327 bool enabled
, bool restricted
, bool at_startup
) {
328 // This must stay a LOG(WARNING); the output is used in processing customer
330 const char kAtStartup
[] = "at startup";
331 const char kByUser
[] = "by user action";
332 const char kOn
[] = "ON";
333 const char kOff
[] = "OFF";
334 const char kRestricted
[] = "(Restricted)";
335 const char kUnrestricted
[] = "(Unrestricted)";
337 std::string annotated_on
=
338 kOn
+ std::string(" ") + (restricted
? kRestricted
: kUnrestricted
);
340 LOG(WARNING
) << "SPDY proxy " << (enabled
? annotated_on
: kOff
)
341 << " " << (at_startup
? kAtStartup
: kByUser
);
344 void DataReductionProxySettings::OnIPAddressChanged() {
345 DCHECK(thread_checker_
.CalledOnValidThread());
346 if (enabled_by_user_
) {
347 DCHECK(params_
->allowed());
348 RecordNetworkChangeEvent(IP_CHANGED
);
351 ProbeWhetherDataReductionProxyIsAvailable();
352 WarmProxyConnection();
356 void DataReductionProxySettings::OnProxyEnabledPrefChange() {
357 DCHECK(thread_checker_
.CalledOnValidThread());
358 if (!on_data_reduction_proxy_enabled_
.is_null())
359 on_data_reduction_proxy_enabled_
.Run(IsDataReductionProxyEnabled());
360 if (!params_
->allowed())
362 MaybeActivateDataReductionProxy(false);
365 void DataReductionProxySettings::OnProxyAlternativeEnabledPrefChange() {
366 DCHECK(thread_checker_
.CalledOnValidThread());
367 if (!params_
->alternative_allowed())
369 MaybeActivateDataReductionProxy(false);
372 void DataReductionProxySettings::ResetDataReductionStatistics() {
373 DCHECK(thread_checker_
.CalledOnValidThread());
374 PrefService
* prefs
= GetLocalStatePrefs();
377 ListPrefUpdate
original_update(prefs
, prefs::kDailyHttpOriginalContentLength
);
378 ListPrefUpdate
received_update(prefs
, prefs::kDailyHttpReceivedContentLength
);
379 original_update
->Clear();
380 received_update
->Clear();
381 for (size_t i
= 0; i
< kNumDaysInHistory
; ++i
) {
382 original_update
->AppendString(base::Int64ToString(0));
383 received_update
->AppendString(base::Int64ToString(0));
387 void DataReductionProxySettings::MaybeActivateDataReductionProxy(
389 DCHECK(thread_checker_
.CalledOnValidThread());
390 PrefService
* prefs
= GetOriginalProfilePrefs();
391 // TODO(marq): Consider moving this so stats are wiped the first time the
392 // proxy settings are actually (not maybe) turned on.
393 if (spdy_proxy_auth_enabled_
.GetValue() &&
394 !prefs
->GetBoolean(prefs::kDataReductionProxyWasEnabledBefore
)) {
395 prefs
->SetBoolean(prefs::kDataReductionProxyWasEnabledBefore
, true);
396 ResetDataReductionStatistics();
398 // Configure use of the data reduction proxy if it is enabled.
399 enabled_by_user_
= IsDataReductionProxyEnabled();
400 SetProxyConfigs(enabled_by_user_
&& !disabled_on_vpn_
,
401 IsDataReductionProxyAlternativeEnabled(),
402 restricted_by_carrier_
,
405 // Check if the proxy has been restricted explicitly by the carrier.
406 if (enabled_by_user_
&& !disabled_on_vpn_
) {
407 ProbeWhetherDataReductionProxyIsAvailable();
408 WarmProxyConnection();
412 void DataReductionProxySettings::SetProxyConfigs(bool enabled
,
413 bool alternative_enabled
,
416 DCHECK(thread_checker_
.CalledOnValidThread());
417 DCHECK(configurator_
);
419 LogProxyState(enabled
, restricted
, at_startup
);
420 // The alternative is only configured if the standard configuration is
422 if (enabled
& !params_
->holdback()) {
423 if (alternative_enabled
) {
424 configurator_
->Enable(restricted
,
425 !params_
->fallback_allowed(),
426 params_
->alt_origin().spec(),
427 params_
->alt_fallback_origin().spec(),
428 params_
->ssl_origin().spec());
430 configurator_
->Enable(restricted
,
431 !params_
->fallback_allowed(),
432 params_
->origin().spec(),
433 params_
->fallback_origin().spec(),
437 configurator_
->Disable();
442 void DataReductionProxySettings::RecordDataReductionInit() {
443 DCHECK(thread_checker_
.CalledOnValidThread());
444 ProxyStartupState state
= PROXY_NOT_AVAILABLE
;
445 if (params_
->allowed()) {
446 if (IsDataReductionProxyEnabled())
447 state
= PROXY_ENABLED
;
449 state
= PROXY_DISABLED
;
452 RecordStartupState(state
);
455 void DataReductionProxySettings::RecordProbeURLFetchResult(
456 ProbeURLFetchResult result
) {
457 UMA_HISTOGRAM_ENUMERATION(kUMAProxyProbeURL
,
459 PROBE_URL_FETCH_RESULT_COUNT
);
462 void DataReductionProxySettings::RecordStartupState(ProxyStartupState state
) {
463 UMA_HISTOGRAM_ENUMERATION(kUMAProxyStartupStateHistogram
,
465 PROXY_STARTUP_STATE_COUNT
);
468 void DataReductionProxySettings::GetNetworkList(
469 net::NetworkInterfaceList
* interfaces
,
471 net::GetNetworkList(interfaces
, policy
);
474 void DataReductionProxySettings::ResetParamsForTest(
475 DataReductionProxyParams
* params
) {
476 params_
.reset(params
);
479 DataReductionProxySettings::ContentLengthList
480 DataReductionProxySettings::GetDailyContentLengths(const char* pref_name
) {
481 DCHECK(thread_checker_
.CalledOnValidThread());
482 DataReductionProxySettings::ContentLengthList content_lengths
;
483 const base::ListValue
* list_value
= GetLocalStatePrefs()->GetList(pref_name
);
484 if (list_value
->GetSize() == kNumDaysInHistory
) {
485 for (size_t i
= 0; i
< kNumDaysInHistory
; ++i
) {
486 content_lengths
.push_back(GetInt64PrefValue(*list_value
, i
));
489 return content_lengths
;
492 void DataReductionProxySettings::GetContentLengths(
494 int64
* original_content_length
,
495 int64
* received_content_length
,
496 int64
* last_update_time
) {
497 DCHECK(thread_checker_
.CalledOnValidThread());
498 DCHECK_LE(days
, kNumDaysInHistory
);
499 PrefService
* local_state
= GetLocalStatePrefs();
501 *original_content_length
= 0L;
502 *received_content_length
= 0L;
503 *last_update_time
= 0L;
507 const base::ListValue
* original_list
=
508 local_state
->GetList(prefs::kDailyHttpOriginalContentLength
);
509 const base::ListValue
* received_list
=
510 local_state
->GetList(prefs::kDailyHttpReceivedContentLength
);
512 if (original_list
->GetSize() != kNumDaysInHistory
||
513 received_list
->GetSize() != kNumDaysInHistory
) {
514 *original_content_length
= 0L;
515 *received_content_length
= 0L;
516 *last_update_time
= 0L;
522 // Include days from the end of the list going backwards.
523 for (size_t i
= kNumDaysInHistory
- days
;
524 i
< kNumDaysInHistory
; ++i
) {
525 orig
+= GetInt64PrefValue(*original_list
, i
);
526 recv
+= GetInt64PrefValue(*received_list
, i
);
528 *original_content_length
= orig
;
529 *received_content_length
= recv
;
531 local_state
->GetInt64(prefs::kDailyHttpContentLengthLastUpdateDate
);
534 net::URLFetcher
* DataReductionProxySettings::GetBaseURLFetcher(
538 net::URLFetcher
* fetcher
= net::URLFetcher::Create(gurl
,
539 net::URLFetcher::GET
,
541 fetcher
->SetLoadFlags(load_flags
);
542 DCHECK(url_request_context_getter_
);
543 fetcher
->SetRequestContext(url_request_context_getter_
);
544 // Configure max retries to be at most kMaxRetries times for 5xx errors.
545 static const int kMaxRetries
= 5;
546 fetcher
->SetMaxRetriesOn5xx(kMaxRetries
);
547 fetcher
->SetAutomaticallyRetryOnNetworkChanges(kMaxRetries
);
553 DataReductionProxySettings::GetURLFetcherForAvailabilityCheck() {
554 return GetBaseURLFetcher(params_
->probe_url(),
555 net::LOAD_DISABLE_CACHE
| net::LOAD_BYPASS_PROXY
);
559 void DataReductionProxySettings::ProbeWhetherDataReductionProxyIsAvailable() {
560 net::URLFetcher
* fetcher
= GetURLFetcherForAvailabilityCheck();
563 fetcher_
.reset(fetcher
);
567 net::URLFetcher
* DataReductionProxySettings::GetURLFetcherForWarmup() {
568 return GetBaseURLFetcher(params_
->warmup_url(), net::LOAD_DISABLE_CACHE
);
571 void DataReductionProxySettings::WarmProxyConnection() {
572 net::URLFetcher
* fetcher
= GetURLFetcherForWarmup();
575 warmup_fetcher_
.reset(fetcher
);
576 warmup_fetcher_
->Start();
579 bool DataReductionProxySettings::DisableIfVPN() {
580 net::NetworkInterfaceList network_interfaces
;
581 GetNetworkList(&network_interfaces
, 0);
582 // VPNs use a "tun" interface, so the presence of a "tun" interface indicates
584 // TODO(kundaji): Verify this works on Windows.
585 const std::string vpn_interface_name_prefix
= "tun";
586 for (size_t i
= 0; i
< network_interfaces
.size(); ++i
) {
587 std::string interface_name
= network_interfaces
[i
].name
;
588 if (LowerCaseEqualsASCII(
589 interface_name
.begin(),
590 interface_name
.begin() + vpn_interface_name_prefix
.size(),
591 vpn_interface_name_prefix
.c_str())) {
592 SetProxyConfigs(false,
593 IsDataReductionProxyAlternativeEnabled(),
596 disabled_on_vpn_
= true;
597 RecordNetworkChangeEvent(DISABLED_ON_VPN
);
601 if (disabled_on_vpn_
) {
602 SetProxyConfigs(enabled_by_user_
,
603 IsDataReductionProxyAlternativeEnabled(),
604 restricted_by_carrier_
,
607 disabled_on_vpn_
= false;
611 } // namespace data_reduction_proxy