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 url_request_context_getter_(NULL
),
99 params_
.reset(params
);
102 DataReductionProxySettings::~DataReductionProxySettings() {
103 if (params_
->allowed())
104 spdy_proxy_auth_enabled_
.Destroy();
105 net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
108 void DataReductionProxySettings::InitPrefMembers() {
109 DCHECK(thread_checker_
.CalledOnValidThread());
110 spdy_proxy_auth_enabled_
.Init(
111 prefs::kDataReductionProxyEnabled
,
112 GetOriginalProfilePrefs(),
113 base::Bind(&DataReductionProxySettings::OnProxyEnabledPrefChange
,
114 base::Unretained(this)));
115 data_reduction_proxy_alternative_enabled_
.Init(
116 prefs::kDataReductionProxyAltEnabled
,
117 GetOriginalProfilePrefs(),
119 &DataReductionProxySettings::OnProxyAlternativeEnabledPrefChange
,
120 base::Unretained(this)));
123 void DataReductionProxySettings::InitDataReductionProxySettings(
125 net::URLRequestContextGetter
* url_request_context_getter
) {
126 DCHECK(thread_checker_
.CalledOnValidThread());
128 DCHECK(url_request_context_getter
);
130 url_request_context_getter_
= url_request_context_getter
;
132 RecordDataReductionInit();
134 // Disable the proxy if it is not allowed to be used.
135 if (!params_
->allowed())
138 AddDefaultProxyBypassRules();
139 net::NetworkChangeNotifier::AddIPAddressObserver(this);
141 // We set or reset the proxy pref at startup.
142 MaybeActivateDataReductionProxy(true);
145 void DataReductionProxySettings::InitDataReductionProxySettings(
147 net::URLRequestContextGetter
* url_request_context_getter
,
148 DataReductionProxyConfigurator
* configurator
) {
149 InitDataReductionProxySettings(prefs
,
150 url_request_context_getter
);
151 SetProxyConfigurator(configurator
);
154 void DataReductionProxySettings::SetDataReductionProxyStatisticsPrefs(
155 DataReductionProxyStatisticsPrefs
* statistics_prefs
) {
156 statistics_prefs_
= statistics_prefs
;
159 void DataReductionProxySettings::SetOnDataReductionEnabledCallback(
160 const base::Callback
<void(bool)>& on_data_reduction_proxy_enabled
) {
161 on_data_reduction_proxy_enabled_
= on_data_reduction_proxy_enabled
;
162 on_data_reduction_proxy_enabled_
.Run(IsDataReductionProxyEnabled());
165 void DataReductionProxySettings::SetProxyConfigurator(
166 DataReductionProxyConfigurator
* configurator
) {
167 DCHECK(configurator
);
168 configurator_
= configurator
;
171 bool DataReductionProxySettings::IsDataReductionProxyEnabled() {
172 return spdy_proxy_auth_enabled_
.GetValue() || IsEnabledOnCommandLine();
176 DataReductionProxySettings::IsDataReductionProxyAlternativeEnabled() const {
177 return data_reduction_proxy_alternative_enabled_
.GetValue();
180 bool DataReductionProxySettings::IsDataReductionProxyManaged() {
181 return spdy_proxy_auth_enabled_
.IsManaged();
184 void DataReductionProxySettings::SetDataReductionProxyEnabled(bool enabled
) {
185 DCHECK(thread_checker_
.CalledOnValidThread());
186 // Prevent configuring the proxy when it is not allowed to be used.
187 if (!params_
->allowed())
190 if (spdy_proxy_auth_enabled_
.GetValue() != enabled
) {
191 spdy_proxy_auth_enabled_
.SetValue(enabled
);
192 OnProxyEnabledPrefChange();
196 void DataReductionProxySettings::SetDataReductionProxyAlternativeEnabled(
198 DCHECK(thread_checker_
.CalledOnValidThread());
199 // Prevent configuring the proxy when it is not allowed to be used.
200 if (!params_
->alternative_allowed())
202 if (data_reduction_proxy_alternative_enabled_
.GetValue() != enabled
) {
203 data_reduction_proxy_alternative_enabled_
.SetValue(enabled
);
204 OnProxyAlternativeEnabledPrefChange();
208 int64
DataReductionProxySettings::GetDataReductionLastUpdateTime() {
209 DCHECK(thread_checker_
.CalledOnValidThread());
210 DCHECK(statistics_prefs_
);
211 int64 last_update_internal
=
212 statistics_prefs_
->GetInt64(prefs::kDailyHttpContentLengthLastUpdateDate
);
213 base::Time last_update
= base::Time::FromInternalValue(last_update_internal
);
214 return static_cast<int64
>(last_update
.ToJsTime());
217 DataReductionProxySettings::ContentLengthList
218 DataReductionProxySettings::GetDailyOriginalContentLengths() {
219 DCHECK(thread_checker_
.CalledOnValidThread());
220 return GetDailyContentLengths(prefs::kDailyHttpOriginalContentLength
);
223 void DataReductionProxySettings::SetUnreachable(bool unreachable
) {
224 unreachable_
= unreachable
;
227 bool DataReductionProxySettings::IsDataReductionProxyUnreachable() {
228 DCHECK(thread_checker_
.CalledOnValidThread());
232 DataReductionProxySettings::ContentLengthList
233 DataReductionProxySettings::GetDailyReceivedContentLengths() {
234 DCHECK(thread_checker_
.CalledOnValidThread());
235 return GetDailyContentLengths(prefs::kDailyHttpReceivedContentLength
);
238 void DataReductionProxySettings::OnURLFetchComplete(
239 const net::URLFetcher
* source
) {
240 DCHECK(thread_checker_
.CalledOnValidThread());
242 DCHECK(source
== fetcher_
.get());
243 net::URLRequestStatus status
= source
->GetStatus();
244 if (status
.status() == net::URLRequestStatus::FAILED
) {
245 if (status
.error() == net::ERR_INTERNET_DISCONNECTED
) {
246 RecordProbeURLFetchResult(INTERNET_DISCONNECTED
);
249 // TODO(bengr): Remove once we understand the reasons probes are failing.
250 // Probe errors are either due to fetcher-level errors or modified
251 // responses. This only tracks the former.
252 UMA_HISTOGRAM_SPARSE_SLOWLY(
253 kUMAProxyProbeURLNetError
, std::abs(status
.error()));
256 std::string response
;
257 source
->GetResponseAsString(&response
);
259 if ("OK" == response
.substr(0, 2)) {
260 DVLOG(1) << "The data reduction proxy is unrestricted.";
262 if (enabled_by_user_
) {
263 if (restricted_by_carrier_
) {
264 // The user enabled the proxy, but sometime previously in the session,
265 // the network operator had blocked the canary and restricted the user.
266 // The current network doesn't block the canary, so don't restrict the
267 // proxy configurations.
268 SetProxyConfigs(true /* enabled */,
269 IsDataReductionProxyAlternativeEnabled(),
270 false /* restricted */,
271 false /* at_startup */);
272 RecordProbeURLFetchResult(SUCCEEDED_PROXY_ENABLED
);
274 RecordProbeURLFetchResult(SUCCEEDED_PROXY_ALREADY_ENABLED
);
277 restricted_by_carrier_
= false;
280 DVLOG(1) << "The data reduction proxy is restricted to the configured "
281 << "fallback proxy.";
282 if (enabled_by_user_
) {
283 if (!restricted_by_carrier_
) {
284 // Restrict the proxy.
285 SetProxyConfigs(true /* enabled */,
286 IsDataReductionProxyAlternativeEnabled(),
287 true /* restricted */,
288 false /* at_startup */);
289 RecordProbeURLFetchResult(FAILED_PROXY_DISABLED
);
291 RecordProbeURLFetchResult(FAILED_PROXY_ALREADY_DISABLED
);
294 restricted_by_carrier_
= true;
297 PrefService
* DataReductionProxySettings::GetOriginalProfilePrefs() {
298 DCHECK(thread_checker_
.CalledOnValidThread());
302 void DataReductionProxySettings::AddDefaultProxyBypassRules() {
304 DCHECK(configurator_
);
305 configurator_
->AddHostPatternToBypass("<local>");
306 // RFC1918 private addresses.
307 configurator_
->AddHostPatternToBypass("10.0.0.0/8");
308 configurator_
->AddHostPatternToBypass("172.16.0.0/12");
309 configurator_
->AddHostPatternToBypass("192.168.0.0/16");
310 // RFC4193 private addresses.
311 configurator_
->AddHostPatternToBypass("fc00::/7");
312 // IPV6 probe addresses.
313 configurator_
->AddHostPatternToBypass("*-ds.metric.gstatic.com");
314 configurator_
->AddHostPatternToBypass("*-v4.metric.gstatic.com");
317 void DataReductionProxySettings::LogProxyState(
318 bool enabled
, bool restricted
, bool at_startup
) {
319 // This must stay a LOG(WARNING); the output is used in processing customer
321 const char kAtStartup
[] = "at startup";
322 const char kByUser
[] = "by user action";
323 const char kOn
[] = "ON";
324 const char kOff
[] = "OFF";
325 const char kRestricted
[] = "(Restricted)";
326 const char kUnrestricted
[] = "(Unrestricted)";
328 std::string annotated_on
=
329 kOn
+ std::string(" ") + (restricted
? kRestricted
: kUnrestricted
);
331 LOG(WARNING
) << "SPDY proxy " << (enabled
? annotated_on
: kOff
)
332 << " " << (at_startup
? kAtStartup
: kByUser
);
335 void DataReductionProxySettings::OnIPAddressChanged() {
336 DCHECK(thread_checker_
.CalledOnValidThread());
337 if (enabled_by_user_
) {
338 DCHECK(params_
->allowed());
339 RecordNetworkChangeEvent(IP_CHANGED
);
342 if (IsDataReductionProxyAlternativeEnabled() &&
343 !params_
->alternative_fallback_allowed()) {
346 ProbeWhetherDataReductionProxyIsAvailable();
350 void DataReductionProxySettings::OnProxyEnabledPrefChange() {
351 DCHECK(thread_checker_
.CalledOnValidThread());
352 if (!on_data_reduction_proxy_enabled_
.is_null())
353 on_data_reduction_proxy_enabled_
.Run(IsDataReductionProxyEnabled());
354 if (!params_
->allowed())
356 MaybeActivateDataReductionProxy(false);
359 void DataReductionProxySettings::OnProxyAlternativeEnabledPrefChange() {
360 DCHECK(thread_checker_
.CalledOnValidThread());
361 if (!params_
->alternative_allowed())
363 MaybeActivateDataReductionProxy(false);
366 void DataReductionProxySettings::ResetDataReductionStatistics() {
367 DCHECK(thread_checker_
.CalledOnValidThread());
368 DCHECK(statistics_prefs_
);
369 base::ListValue
* original_update
=
370 statistics_prefs_
->GetList(prefs::kDailyHttpOriginalContentLength
);
371 base::ListValue
* received_update
=
372 statistics_prefs_
->GetList(prefs::kDailyHttpReceivedContentLength
);
373 original_update
->Clear();
374 received_update
->Clear();
375 for (size_t i
= 0; i
< kNumDaysInHistory
; ++i
) {
376 original_update
->AppendString(base::Int64ToString(0));
377 received_update
->AppendString(base::Int64ToString(0));
381 void DataReductionProxySettings::MaybeActivateDataReductionProxy(
383 DCHECK(thread_checker_
.CalledOnValidThread());
384 PrefService
* prefs
= GetOriginalProfilePrefs();
385 // TODO(marq): Consider moving this so stats are wiped the first time the
386 // proxy settings are actually (not maybe) turned on.
387 if (spdy_proxy_auth_enabled_
.GetValue() &&
388 !prefs
->GetBoolean(prefs::kDataReductionProxyWasEnabledBefore
)) {
389 prefs
->SetBoolean(prefs::kDataReductionProxyWasEnabledBefore
, true);
390 ResetDataReductionStatistics();
392 // Configure use of the data reduction proxy if it is enabled.
393 enabled_by_user_
= IsDataReductionProxyEnabled();
394 SetProxyConfigs(enabled_by_user_
&& !disabled_on_vpn_
,
395 IsDataReductionProxyAlternativeEnabled(),
396 restricted_by_carrier_
,
399 // Check if the proxy has been restricted explicitly by the carrier.
400 if (enabled_by_user_
&& !disabled_on_vpn_
&&
401 !(IsDataReductionProxyAlternativeEnabled() &&
402 !params_
->alternative_fallback_allowed())) {
403 ProbeWhetherDataReductionProxyIsAvailable();
407 void DataReductionProxySettings::SetProxyConfigs(bool enabled
,
408 bool alternative_enabled
,
411 DCHECK(thread_checker_
.CalledOnValidThread());
412 DCHECK(configurator_
);
414 LogProxyState(enabled
, restricted
, at_startup
);
415 // The alternative is only configured if the standard configuration is
417 if (enabled
& !params_
->holdback()) {
418 if (alternative_enabled
) {
419 configurator_
->Enable(restricted
,
420 !params_
->alternative_fallback_allowed(),
421 params_
->alt_origin().spec(),
423 params_
->ssl_origin().spec());
425 configurator_
->Enable(restricted
,
426 !params_
->fallback_allowed(),
427 params_
->origin().spec(),
428 params_
->fallback_origin().spec(),
432 configurator_
->Disable();
437 void DataReductionProxySettings::RecordDataReductionInit() {
438 DCHECK(thread_checker_
.CalledOnValidThread());
439 ProxyStartupState state
= PROXY_NOT_AVAILABLE
;
440 if (params_
->allowed()) {
441 if (IsDataReductionProxyEnabled())
442 state
= PROXY_ENABLED
;
444 state
= PROXY_DISABLED
;
447 RecordStartupState(state
);
450 void DataReductionProxySettings::RecordProbeURLFetchResult(
451 ProbeURLFetchResult result
) {
452 UMA_HISTOGRAM_ENUMERATION(kUMAProxyProbeURL
,
454 PROBE_URL_FETCH_RESULT_COUNT
);
457 void DataReductionProxySettings::RecordStartupState(ProxyStartupState state
) {
458 UMA_HISTOGRAM_ENUMERATION(kUMAProxyStartupStateHistogram
,
460 PROXY_STARTUP_STATE_COUNT
);
463 void DataReductionProxySettings::GetNetworkList(
464 net::NetworkInterfaceList
* interfaces
,
466 net::GetNetworkList(interfaces
, policy
);
469 void DataReductionProxySettings::ResetParamsForTest(
470 DataReductionProxyParams
* params
) {
471 params_
.reset(params
);
474 DataReductionProxySettings::ContentLengthList
475 DataReductionProxySettings::GetDailyContentLengths(const char* pref_name
) {
476 DCHECK(thread_checker_
.CalledOnValidThread());
477 DataReductionProxySettings::ContentLengthList content_lengths
;
478 DCHECK(statistics_prefs_
);
479 const base::ListValue
* list_value
= statistics_prefs_
->GetList(pref_name
);
480 if (list_value
->GetSize() == kNumDaysInHistory
) {
481 for (size_t i
= 0; i
< kNumDaysInHistory
; ++i
) {
482 content_lengths
.push_back(GetInt64PrefValue(*list_value
, i
));
485 return content_lengths
;
488 void DataReductionProxySettings::GetContentLengths(
490 int64
* original_content_length
,
491 int64
* received_content_length
,
492 int64
* last_update_time
) {
493 DCHECK(thread_checker_
.CalledOnValidThread());
494 DCHECK_LE(days
, kNumDaysInHistory
);
495 DCHECK(statistics_prefs_
);
497 const base::ListValue
* original_list
=
498 statistics_prefs_
->GetList(prefs::kDailyHttpOriginalContentLength
);
499 const base::ListValue
* received_list
=
500 statistics_prefs_
->GetList(prefs::kDailyHttpReceivedContentLength
);
502 if (original_list
->GetSize() != kNumDaysInHistory
||
503 received_list
->GetSize() != kNumDaysInHistory
) {
504 *original_content_length
= 0L;
505 *received_content_length
= 0L;
506 *last_update_time
= 0L;
512 // Include days from the end of the list going backwards.
513 for (size_t i
= kNumDaysInHistory
- days
;
514 i
< kNumDaysInHistory
; ++i
) {
515 orig
+= GetInt64PrefValue(*original_list
, i
);
516 recv
+= GetInt64PrefValue(*received_list
, i
);
518 *original_content_length
= orig
;
519 *received_content_length
= recv
;
521 statistics_prefs_
->GetInt64(prefs::kDailyHttpContentLengthLastUpdateDate
);
524 net::URLFetcher
* DataReductionProxySettings::GetBaseURLFetcher(
528 net::URLFetcher
* fetcher
= net::URLFetcher::Create(gurl
,
529 net::URLFetcher::GET
,
531 fetcher
->SetLoadFlags(load_flags
);
532 DCHECK(url_request_context_getter_
);
533 fetcher
->SetRequestContext(url_request_context_getter_
);
534 // Configure max retries to be at most kMaxRetries times for 5xx errors.
535 static const int kMaxRetries
= 5;
536 fetcher
->SetMaxRetriesOn5xx(kMaxRetries
);
537 fetcher
->SetAutomaticallyRetryOnNetworkChanges(kMaxRetries
);
543 DataReductionProxySettings::GetURLFetcherForAvailabilityCheck() {
544 return GetBaseURLFetcher(params_
->probe_url(),
545 net::LOAD_DISABLE_CACHE
| net::LOAD_BYPASS_PROXY
);
549 void DataReductionProxySettings::ProbeWhetherDataReductionProxyIsAvailable() {
550 net::URLFetcher
* fetcher
= GetURLFetcherForAvailabilityCheck();
553 fetcher_
.reset(fetcher
);
557 bool DataReductionProxySettings::DisableIfVPN() {
558 net::NetworkInterfaceList network_interfaces
;
559 GetNetworkList(&network_interfaces
, 0);
560 // VPNs use a "tun" interface, so the presence of a "tun" interface indicates
562 // TODO(kundaji): Verify this works on Windows.
563 const std::string vpn_interface_name_prefix
= "tun";
564 for (size_t i
= 0; i
< network_interfaces
.size(); ++i
) {
565 std::string interface_name
= network_interfaces
[i
].name
;
566 if (LowerCaseEqualsASCII(
567 interface_name
.begin(),
568 interface_name
.begin() + vpn_interface_name_prefix
.size(),
569 vpn_interface_name_prefix
.c_str())) {
570 SetProxyConfigs(false,
571 IsDataReductionProxyAlternativeEnabled(),
574 disabled_on_vpn_
= true;
575 RecordNetworkChangeEvent(DISABLED_ON_VPN
);
579 if (disabled_on_vpn_
) {
580 SetProxyConfigs(enabled_by_user_
,
581 IsDataReductionProxyAlternativeEnabled(),
582 restricted_by_carrier_
,
585 disabled_on_vpn_
= false;
589 } // namespace data_reduction_proxy